I have a UITextField with this NSNotification:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(textFieldDidChange:) name:#"UITextFieldTextDidChangeNotification" object:_searchTextField];
- (void)textFieldDidChange :(NSNotification *)notif
{
//
}
The NSLog is when I type in r
NSConcreteNotification 0x193c20 {name = UITextFieldTextDidChangeNotification; object = <UITextField: 0x15a980; frame = (20 20; 280 31); text = 'r'; clipsToBounds = YES; opaque = NO; autoresize = RM+BM; layer = <CALayer: 0x15aad0>>}
How do I get the text r out of the notif object?
The notification's object property stores the text field whose text changed, so notif.object.text would contain the text "r".
Related
I'm creating a sudoku style swift app using 81 buttons (organized in stack views). I'm referencing the buttons using their tag value 0 to 80 which aligns with the arrays I'm using to store the values. But, the first button is causing problems, I think because there are UILayoutGuide objects that also have tag 0.
This solution was working but I've just upgraded to Xcode Version 8.0 (8A218a) and converted to swift 3.
I added the following code to the viewDidLoad function of the view controller:
// Get the subviews of the view
var subviews = view.subviews
// Return if there are no subviews
if subviews.count == 0 {
return
}
for subview : AnyObject in subviews{
// Do what you want to do with the subview
print("\(subview.tag) - \(subview)")
}
This produces the following log:
Optional(200) - UIImageView: 0x7ff3e240f3b0; frame = (-4 0; 383 667); autoresize = RM+BM; userInteractionEnabled = NO; tag = 200; layer =
Optional(200) - UIStackView: 0x7ff3e240f590; frame = (7 103; 360 360); opaque = NO; autoresize = RM+BM; tag = 200; layer = CATransformLayer: 0x6100002316a0
Optional(200) - UIImageView: 0x7ff3e24111b0; frame = (0 47; 375 436); autoresize = RM+BM; userInteractionEnabled = NO; tag = 200; layer = CALayer: 0x6100002344e0
Optional(200) - UIToolbar: 0x7ff3e2637ff0; frame = (0 623; 375 44); opaque = NO; autoresize = RM+BM; tag = 200; layer = CALayer: 0x6000000343e0
Optional(200) - UIImageView: 0x7ff3e26015f0; frame = (63 228; 248 211); autoresize = RM+BM; userInteractionEnabled = NO; tag = 200; layer = CALayer: 0x600000034ea0
Optional(200) - UILabel: 0x7ff3e263e2e0; frame = (117 313; 141 41); text = 'Nice Work'; opaque = NO; autoresize = RM+BM; userInteractionEnabled = NO; tag = 200; layer = _UILabelLayer: 0x600000093060
Optional(200) - UIStackView: 0x7ff3e263e750; frame = (127 491; 120 120); opaque = NO; autoresize = RM+BM; tag = 200; layer = CATransformLayer: 0x600000035520
Optional(0) - _UILayoutGuide: 0x7ff3e263ee80; frame = (0 0; 0 0); hidden = YES; layer = CALayer: 0x600000035bc0
Optional(0) - _UILayoutGuide: 0x7ff3e263f030; frame = (0 0; 0 0); hidden = YES; layer = CALayer: 0x600000035c60
So the last 2 output lines are my suspected UILayoutGuide objects. I didn't code them and they are not objects in the hierarchy. How do I see them/get rid of them?
If you just want to prevent the loop from adding the _UILayoutGuide (which is of class UILayoutSupport), do this:
// Get the subviews of the view
var subviews = view.subviews
// Return if there are no subviews
if subviews.count == 0 {
return
}
for subview : AnyObject in subviews{
// Do what you want to do with the subview
print("\(subview.tag) - \(subview)")
// Check for whether is it a UILayout class or not
if(subview is UILayoutSupport){
//This section is executed when UILayoutGuide will come into play
print("Don't add it to the stack")
}else{
// Write your code in this section
}
}
you can also print your stack, check for UILayoutGuide in it and remove it.
Simple answer is that the UILayoutGuides don't have tags. When you print the subview.tag its showing tag = 0 when there is no tag. So the issue with button 0 is not related to the UILayoutGuide.
I didn't figure out what the issue was with the tag 0 button but recoded to avoid using tag 0. Not exactly fixed but the solution works.
I have a UIButton that is only reacting to my tap gestures when I tap on a very small sub-area within it. I'm assuming that maybe another view is swallowing my gestures when I tap in other places within the button.
the question is.. is there a way to know which UIView exactly is receiving the taps? I know I can brute force it an attach all gesture handlers to all of my views.. but seems like too much work. any ideas?
update:
Ahmed's answer below worked perfectly.. I also added to it UIView's recursiveDescription to find the offending view quickly.
ie if i put a breakpoint on the NSLog line:
- (void)sendEvent:(UIEvent *)event
{
[super sendEvent:event];
UIView *touchReceipientView =((UITouch *)[event.allTouches anyObject]).view;
NSLog(#"View = %# ",touchReceipientView);
}
I get this output (as an example):
(lldb) po [touchReceipientView recursiveDescription]
<UIView: 0x1fd16470; frame = (0 430; 320 40); layer = <CALayer: 0x1fd164d0>>
| <UIButton: 0x1fd13030; frame = (297 0; 18 19); opaque = NO; tag = 11; layer = <CALayer: 0x1fd130f0>>
| | <UIImageView: 0x1fd13190; frame = (-41 -40.5; 100 100); alpha = 0; opaque = NO; userInteractionEnabled = NO; tag = 1886548836; layer = <CALayer: 0x1fd131f0>>
| | <UIImageView: 0x1fd14d00; frame = (1 2; 16 15); clipsToBounds = YES; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x1fdc8e50>>
which made me identify the offending UIView immediately!
You can subclass the UIApplication and see who is receiving the events.
and in main.m,
#interface MyApplication : UIApplication
#end
#implementation MyApplication
- (void)sendEvent:(UIEvent *)event
{
[super sendEvent:event];
NSLog(#"View = %#", ((UITouch *)[event.allTouches anyObject]).view);
}
#end
int main(int argc, char *argv[])
{
#autoreleasepool {
return UIApplicationMain(argc, argv, NSStringFromClass([MyApplication class]), NSStringFromClass([MyAppDelegate class]));
}
}
I have an iPhone game app in objective c which uses a private class to load every view programmatically. (without using nib files) I am trying to place an admob ads in the view but it seems I can't have it to work. I tried to place my admob ads view in the init method but still it doesn't load the view.
Here are some codes of how I initialize the data to load the view:
-(id)init
{
if( (self=[super init]))
{
played_ = NO;
KWSprite* background = [KWSprite spriteWithFile:#"title_background.png"];
background.position = ccp(winSize_.width/2, winSize_.height/2);
KWSprite* logo = [KWSprite spriteWithFile:#"logo.png"];
logo.position = ccp(winSize_.width/2, 260);
CCMenuItemImage* start = [CCMenuItemImage itemFromNormalImage:#"start.png" selectedImage:#"start_selected.png" target:self selector:#selector(pressStartButton:)]
CCMenuItemImage* credit = [CCMenuItemImage itemFromNormalImage:#"credit.png" selectedImage:#"credit_selected.png" target:self selector:#selector(pressCreditButton:)];
CCMenuItemImage* iADButton = [CCMenuItemImage itemFromNormalImage:#"noAdsD.png" selectedImage:#"noAdsD#2x.png" target:self selector:#selector(pressiAdButton:)];
CCMenuItemImage* howto = [CCMenuItemImage itemFromNormalImage:#"howto.png" selectedImage:#"howto_selected.png" target:self selector:#selector(pressHowtoButton:)];
CCMenu* menu = [CCMenu menuWithItems:howto, start, credit, iADButton, nil];
[menu alignItemsHorizontally];
menu.position = ccp(winSize_.width/2, 40);
[self addChild:background];
[self addChild:logo];
[self addChild:menu];
}
return self;
}
Looks like you're using Cocos2D here. If you're using a version afte v0.99.5b3, I'd recommend putting your AdMob code within the RootViewController class. It would be better for OpenGL performance since you're not combining your UIKit views with your OpenGL views.
On a high level, you have to add a GADBannerView to your RootViewController's view, then reduce the size of the Cocos2D OpenGL view to make sure the ad shows. What this might look like is below (this code assumes the ad is positioned at the top):
- (void)showBanner {
// Frame of the main RootViewController which we call the root view.
CGRect rootViewFrame = self.view.frame;
// Frame of the main RootViewController view that holds the Cocos2D view.
CGRect glViewFrame = [[CCDirector sharedDirector] openGLView].frame;
// Frame of the GADBannerView
CGRect bannerViewFrame = bannerView_.frame;
CGRect frame = bannerViewFrame;
// The updated x and y coordinates for the origin of the banner.
CGFloat yLocation = 0.0;
CGFloat xLocation = 0.0;
// Move the root view underneath the ad banner.
glViewFrame.origin.y = bannerViewFrame.size.height;
// Center the banner using the value of the origin.
if (UIInterfaceOrientationIsLandscape(toInt)) {
// The superView has not had its width and height updated yet so use those
// values for the x and y of the new origin respectively.
xLocation = (rootViewFrame.size.height -
bannerViewFrame.size.width) / 2.0;
} else {
xLocation = (rootViewFrame.size.width -
bannerViewFrame.size.width) / 2.0;
}
frame.origin = CGPointMake(xLocation, yLocation);
bannerView_.frame = frame;
if (UIInterfaceOrientationIsLandscape(toInt)) {
// The super view's frame hasn't been updated so use its width
// as the height.
glViewFrame.size.height = rootViewFrame.size.width -
bannerViewFrame.size.height;
glViewFrame.size.width = rootViewFrame.size.height;
} else {
glViewFrame.size.height = rootViewFrame.size.height -
bannerViewFrame.size.height;
}
[[CCDirector sharedDirector] openGLView].frame = glViewFrame;
}
I have a view which has two labels. When I swipe left I fill next content to label text. Similarly swiping right loads previous content. I want to give an effect to labels like they are scrolling from left or right.
I used a scrollview before but it had a memory problem. So I'm using one view, and swipe gesture loads next or previous content. I want to add scrollview's sliding effect to labels. How can I do that?
I'm not quite sure precisely what effect you're looking for, but you could do something like this, which creates a new, temporary label, puts it off screen, animates the moving it over the label you have on screen, and then when done, resets the old one and deletes the temporary label. This is what a non-autolayout implementation might look like:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
UISwipeGestureRecognizer *left = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(leftSwipe:)];
[left setDirection:UISwipeGestureRecognizerDirectionLeft];
[self.view addGestureRecognizer:left];
// if non-ARC, release it
// [release left];
self.label1.text = #"Mo";
}
- (void)leftSwipe:(UISwipeGestureRecognizer *)gesture
{
NSString *newText;
UILabel *existingLabel = self.label1;
// in my example, I'm just going to toggle the value between Mo and Curly
if ([existingLabel.text isEqualToString:#"Curly"])
newText = #"Mo";
else
newText = #"Curly";
// create new label
UILabel *tempLabel = [[UILabel alloc] initWithFrame:existingLabel.frame];
[existingLabel.superview addSubview:tempLabel];
tempLabel.text = newText;
// move the new label off-frame to the right
tempLabel.transform = CGAffineTransformMakeTranslation(tempLabel.superview.bounds.size.width, 0);
// animate the sliding of them into place
[UIView animateWithDuration:0.5
animations:^{
tempLabel.transform = CGAffineTransformIdentity;
existingLabel.transform = CGAffineTransformMakeTranslation(-existingLabel.superview.bounds.size.width, 0);
}
completion:^(BOOL finished) {
existingLabel.text = newText;
existingLabel.transform = CGAffineTransformIdentity;
[tempLabel removeFromSuperview];
}];
// if non-ARC, release it
// [release tempLabel];
}
This animation animates the label with respect to its superview. You may want to ensure that the superview is set to "clip subviews". This way, the animation will be constrained to the bounds of that superview, which yields a slightly more polished look.
Note, if using auto layout, the idea is the same (though the execution is more complicated). Basically configure your constraints so new view is off to the right, then, in animation block update/replace the constraints so the original label is off to the left and the new one is in the spot of the original label, and, finally, in the completion block reset the constraints of the original label and remove the temporary label.
By the way, this is all infinitely easier if you're comfortable with one of the built in transitions:
- (void)leftSwipe:(UISwipeGestureRecognizer *)gesture
{
NSString *newText;
UILabel *existingLabel = self.label1;
// in my example, I'm just going to toggle the value between Mo and Curly
if ([existingLabel.text isEqualToString:#"Curly"])
newText = #"Mo";
else
newText = #"Curly";
[UIView transitionWithView:existingLabel // or try `existingLabel.superview`
duration:0.5
options:UIViewAnimationOptionTransitionFlipFromRight
animations:^{
existingLabel.text = newText;
}
completion:nil];
}
If you prefer animation that behaves more like a scroll view (i.e., with continuous feedback on the gesture), it might look something like the following:
- (void)viewDidLoad
{
[super viewDidLoad];
UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(panHandler:)];
[self.view addGestureRecognizer:pan];
self.label1.text = #"Mo";
}
- (void)panHandler:(UIPanGestureRecognizer *)sender
{
if (sender.state == UIGestureRecognizerStateBegan)
{
_panLabel = [[UILabel alloc] init];
// in my example, I'm just going to toggle the value between Mo and Curly
// you'll presumably set the label contents based upon the direction of the
// pan (if positive, swiping to the right, grab the "previous" label, if negative
// pan, grab the "next" label)
if ([self.label1.text isEqualToString:#"Curly"])
_newText = #"Mo";
else
_newText = #"Curly";
// set the text
_panLabel.text = _newText;
// set the frame to just be off screen
_panLabel.frame = CGRectMake(self.label1.frame.origin.x + self.containerView.frame.size.width,
self.label1.frame.origin.y,
self.label1.frame.size.width,
self.label1.frame.size.height);
[self.containerView addSubview:_panLabel];
_originalCenter = self.label1.center; // save where the original label originally was
}
else if (sender.state == UIGestureRecognizerStateChanged)
{
CGPoint translate = [sender translationInView:self.containerView];
if (translate.x > 0)
{
_panLabel.center = CGPointMake(_originalCenter.x - self.containerView.frame.size.width + translate.x, _originalCenter.y);
self.label1.center = CGPointMake(_originalCenter.x + translate.x, _originalCenter.y);
}
else
{
_panLabel.center = CGPointMake(_originalCenter.x + self.containerView.frame.size.width + translate.x, _originalCenter.y);
self.label1.center = CGPointMake(_originalCenter.x + translate.x, _originalCenter.y);
}
}
else if (sender.state == UIGestureRecognizerStateEnded || sender.state == UIGestureRecognizerStateFailed || sender.state == UIGestureRecognizerStateCancelled)
{
CGPoint translate = [sender translationInView:self.containerView];
CGPoint finalNewFieldLocation;
CGPoint finalOriginalFieldLocation;
BOOL panSucceeded;
if (sender.state == UIGestureRecognizerStateFailed ||
sender.state == UIGestureRecognizerStateCancelled)
{
panSucceeded = NO;
}
else
{
// by factoring in the velocity, we can capture a flick more accurately
//
// (by the way, I don't like iOS's velocity, because if you stop moving, it records the velocity
// prior to stopping the move rather than noting that you actually stopped, so I usually calculate my own,
// but I'll leave this as is for purposes of this example)
CGPoint velocity = [sender velocityInView:self.containerView];
if (translate.x < 0)
panSucceeded = ((translate.x + velocity.x * 0.5) < -(self.containerView.frame.size.width / 2));
else
panSucceeded = ((translate.x + velocity.x * 0.5) > (self.containerView.frame.size.width / 2));
}
if (panSucceeded)
{
// if we succeeded, finish moving the stuff
finalNewFieldLocation = _originalCenter;
if (translate.x < 0)
finalOriginalFieldLocation = CGPointMake(_originalCenter.x - self.containerView.frame.size.width, _originalCenter.y);
else
finalOriginalFieldLocation = CGPointMake(_originalCenter.x + self.containerView.frame.size.width, _originalCenter.y);
}
else
{
// if we didn't, then just return everything to where it was
finalOriginalFieldLocation = _originalCenter;
if (translate.x < 0)
finalNewFieldLocation = CGPointMake(_originalCenter.x + self.containerView.frame.size.width, _originalCenter.y);
else
finalNewFieldLocation = CGPointMake(_originalCenter.x - self.containerView.frame.size.width, _originalCenter.y);
}
// animate the moving of stuff to their final locations, and on completion, clean everything up
[UIView animateWithDuration:0.3
delay:0.0
options:UIViewAnimationOptionCurveEaseOut
animations:^{
_panLabel.center = finalNewFieldLocation;
self.label1.center = finalOriginalFieldLocation;
}
completion:^(BOOL finished) {
if (panSucceeded)
self.label1.text = _newText;
self.label1.center = _originalCenter;
[_panLabel removeFromSuperview];
_panLabel = nil; // in non-ARC, release instead
}
];
}
}
Note, I have put both the original label, as well as the new label being panned on, in a container UIView (called containerView, surprisingly enough), so that I can clip the animation to that container.
as topic... I want a simple to change the color of AlertView, but after googling, I find it is trouble, anyway, can give a tip on this problem ?
I also trace the UIAlertView, the structure will be like as follows :
CAlertView: 0x6463050; baseClass = UIAlertView; frame = (3.8 175.1; 312.4 129.8); transform = [1.1, 0, 0, 1.1, 0, 0]; opaque = NO; tag = -1; animations = { transform=<CABasicAnimation: 0x616cfb0>; opacity=<CABasicAnimation: 0x616d050>; }; layer = <CALayer: 0x6407f90>>
above is base view
subViews as follows:
"<UIImageView: 0x6470dc0; frame = (0 0; 284 118); opaque = NO; autoresize = W+H; userInteractionEnabled = NO; layer = <CALayer: 0x6470df0>>",
"<UILabel: 0x64649c0; frame = (12 15; 260 0); text = ''; clipsToBounds = YES; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x6444490>>",
"<UILabel: 0x6454180; frame = (12 22; 260 21); text = 'Submit successfully!'; clipsToBounds = YES; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x6453660>>",
"<UIThreePartButton: 0x64690f0; frame = (11 59; 262 43); opaque = NO; tag = 1; layer = <CALayer: 0x6468800>>"
I think that the UIImageView is the background view, I try to change the color of the view, but it does not effect the color of the UIAlertView...
anyone can share a good and simple way to change the color of the alert view ?
thanks for your time .
Regards
This is app going to be submitted to the app store? If so, I can almost guarentee that Apple will reject your app on Human Interface Guidelines.
You would probably be better creating your own custom version of a UIAlertView and showing that instead (you would also have a lot more control over the appearance and behaviour of it).