I am trying to make a toolbar that is always above the keyboard, pretty much like in Apple's native Messages app. If you take a closer look at that app on the iPad, try undocking the keyboard and the dragging it around by holding than button in its bottom right. You will notice that Apple's toolbar tracks the keyboard's position quite nicely.
Trying to replicate that behavior, I subscribed to both the keyboard-will-change-frame and keyboard-did-change-frame notifications. However, to my disappointment I found that the first one is sent when the user starts the dragging, and the latter is sent when the keyboard snaps to its final position, but there are no notifications I could find that are sent in between. Am I missing something? Here's what I am talking about:
This is the code I use to subscribe to notifications:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillChangeFrame:) name:UIKeyboardWillChangeFrameNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardDidChangeFrame:) name:UIKeyboardDidChangeFrameNotification object:nil];
These are my listener functions:
- (void)keyboardWillChangeFrame:(NSNotification *)notification{
NSLog(#"will change");
// [self alignToolbarForKeyboard:notification action:#"will_change_frame"];
}
- (void)keyboardDidChangeFrame:(NSNotification *)notification{
NSLog(#"did change");
// [self alignToolbarForKeyboard:notification action:#"did_change_frame"];
}
I commented out the call both listener functions make to make sure it's not blocking anything. Is there a notification type I do not know of? I did look at the UIWindow reference but couldn't find anything beyond what I already have. Just to make sure, I also checked whether the show/hide notifications are sent during the movement. They aren't.
Any ideas? Thanks!
Related
I've created an app that has two viewcontrollers. The app opens to a title screen (general UIViewController titled 'Title') with a segue connection to the second view that is a custom class (OSViewController titled 'MapView'). As it is, the app suspends when entered into the background state so it opens right where you left off which is typically in MapView.
I want to know what I need to do to have the app start at the title screen when it becomes active. Preferably, I'd like it to open to the title screen if it is inactive for more than 1 minute. From what I've been reading, it seems like I would make a call in applicationDidBecomeActive: method in my AppDelegate to code this in. Please provide me the code to put in the applicationDidBecomeActive: method (if that's the right place to put it) that will reopen my app to the title screen when transitioning from the inactive state to the active state. My app is almost finished but I'd like to fix this issue and I don't have a lot of experience dealing with app states. Thanks in advance for your time.
If you need more information just ask.
You can also register a class as an observer of the "didBecomeActive" notification. You should place this in the viewDidLoad or the init method of your class.
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(willBecomeActive:) name:UIApplicationDidBecomeActiveNotification object:nil];
In this case, willBecomeActive: is a method that you have defined in your class that get's called when the app becomes active again. That might look something like this:
- (void)willBecomeActive:(NSNotification *)notification {
if (self.navigationController.topViewController == self) {
[self.navigationController popToRootViewControllerAnimated:YES];
}
}
You'll also need to add this in your viewDidUnload method
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidBecomeActiveNotification object:nil];
EDIT:
Thanks #AMayes for the advice. I don't believe key/value observing is necessary in this instance.
I have a split view based app, and would like to listen for some sort of notification for when the root menu is displayed. The reason I want to do this is because the keyboard overlaps the menu, so I would like to hide the keyboard when the menu is displayed.
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillHide:)
name:<Some Notification Here>
object:self.view.window];
I already have the method to hide the keyboard, I'm just looking for the appropriate notification.
Thanks!
you can post the notification yourself from the root menu. Just subclass it and post the notification on viewDidAppear or viewWillAppear.
The word of warning: if you are targeting iOS 5 and up you should kep in mind the viewWillAppear and viewDidAppear are deprecated in iOS6. use
willMoveToParentViewController or didMoveToParentViewController or -(void)viewWillLayoutSubviews
-(void)willMoveToParentViewController:(UIViewController *)parent{
if (!parent)
//post notification here
}
Also, you can provide nil as a notification name and listen to any possible notification, then NSLog it out, perhaps you can find a useful notification there, just make sure it is documented to futureproof your product.
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(notificationReceived:) name:nil object:nil];
I have a Single View Application. When I hit the home button and ‘minimise’ the application I want to be able to execute code when the user reopens it.
For some reason viewDidAppear and viewWillAppear do not execute when I minimise and reopen the application.
Any suggestions?
Thanks in advance
sAdam
You can either execute code in the app delegate in
- (void)applicationDidBecomeActive:(UIApplication *)application
or register to observe the UIApplicationDidBecomeActiveNotification notification and execute your code in response.
There is also the notification UIApplicationWillEnterForegroundNotification and the method - (void)applicationWillEnterForeground:(UIApplication *)application in the app delegate.
To hook up notifications add this at an appropriate point
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(didBecomeActive:)
name:UIApplicationDidBecomeActiveNotification
object:nil];
Define a the corresponding method
- (void)didBecomeActive:(NSNotification *)notification;
{
// Do some stuff
}
Then don't forget to remove yourself an observer at an appropriate point
[[NSNotificationCenter defaultCenter] removeObserver:self];
Discussion
You most likely only want your viewController to respond to events whilst it is the currently active view controller so a good place to register for the notifications would be viewDidLoad and then a good place to remove yourself as an observer would be viewDidUnload
If you are wanting to run the same logic that occurs in your viewDidAppear: method then abstract it into another method and have viewDidAppear: and the method that responds to the notification call this new method.
This is because since Apple implemented "Multitasking", apps are completely reloaded when you start them again, just as if you had never closed them. Because of this, there is no reason for viewDidAppear to be called.
You could either implement
- (void)applicationWillEnterForeground:(UIApplication *)application
and do there what ever you want. Or you register for the notification UIApplicationWillEnterForegroundNotification in your view controller. Do this in viewDidLoad:
[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(myAppWillEnterForeground)
name:UIApplicationWillEnterForegroundNotification object:nil];
And of course implement the specified selector and do there what you want.
I am not sure how the answer by #Paul.s performs the OP request since registering UIApplicationDidBecomeActiveNotification will be executed twice:
When launching the app
When application goes into the background
A better practice will be to decouple those events into 2 different notifications:
UIApplicationDidBecomeActiveNotification:
Posted when the app becomes active.
An app is active when it is receiving events. An active app can be said to have focus. It gains focus after being launched, loses focus when an overlay window pops up or when the device is locked, and gains focus when the device is unlocked.
Which basically means that all logic related to "when application launched for the first time"
UIApplicationWillEnterForegroundNotification:
Posted shortly before an app leaves the background state on its way to becoming the active app.
Conclusion
This way we can create a design that will perform both algorithms but as a decoupled way:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(yourMethodName1) name:UIApplicationWillEnterForegroundNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(yourMethodName2) name:UIApplicationDidBecomeActiveNotification object:nil];
This because you don't redraw your view. Use applicationWillEnterForeground in the AppDelegate instead. This should work fine for you.
How can one simulate applicationWillResignActive to be called?
Locking screen, going to main menu, simulating phone call - none seemed to have helped.
In case I expect things that won't happen, let me tell you more: I subscribe to this message and when that happens hope that notification is sent to a method as listed below:
UIApplication *app = [UIApplication sharedApplication];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(applicationWillResignActive:)
name: UIApplicationWillResignActiveNotification object:app];
Your code is valid, it works for me. And locking the screen or hitting the home button will cause this notification to be posted.
*(One caveat to this is if your device does not support multitasking or if you have the "Application does not run in background" property set to yes in your *info.plist. In which case it will go straight to the "UIApplicationWillTerminateNotification" notification)
So barring that there there are two possiblities:
1) Your addObserver code is not being called, ie. It's in the wrong method.
To test try this:
UIApplication *app = [UIApplication sharedApplication];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(applicationWillResignActive:)
name:UIApplicationWillResignActiveNotification
object:app];
NSLog(#"Observer added");
2) The observer method is not being called properly. Which requires the same method as the at selector.
To test try this:
-(void)applicationWillResignActive:(NSNotification *)notification{
NSLog(#"applicationWillResignActive");
}
Just as an additional point, if you want to see in action which of these notifications are called and when. In your AppDelegate class put the line NSLog(#"%#", NSStringFromSelector(_cmd)); in each of the -application... methods and it will log them when they are called. It's a nice hands on way to get to know them.
In applications like Foursquare when I click the Home button the application goes to background. Then when I click on its icon, it loads back the content on the screen.
When I send my app to background and then I recall it back, it doesn't load back the content to the screen. I have entered my code in the viewDidAppear method but it is not executed.
How is it possible to load the application content when it becomes active?
You need to respond to - (void)applicationDidBecomeActive:(UIApplication *)application or - (void)applicationWillEnterForeground:(UIApplication *)application or the equivalent UIApplication notifications. The UIViewController lifecycle calls like viewDidAppear aren't triggered by app lifecycle transitions.
smparkes suggestion is right. You could register for UIApplicationDidBecomeActiveNotification or UIApplicationWillEnterForegroundNotification. These notifications are called after those method (the ones smparkes wrote) are called. In the handler for this notification do what you want. For example in viewDidLoad for your controller register the following notification:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(doUpdate:)
name:UIApplicationDidBecomeActiveNotification object:nil];
Do not forget to remove in dealloc:
[[NSNotificationCenter defaultCenter] removeObserver:self];
Finally, doUpdate method could be the following
-(void)doUpdate:(NSNotification*)note
{
// do your stuff here...
}
I suggest you to read UIApplicationDelegate class reference. In particular read about Monitoring Application State Changes.
Hope it helps.
Suppose you want to listen to UIApplicationDidBecomeActiveNotification,here is the ObjC code that might help you.
[[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationDidBecomeActiveNotification
object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
// custom code goes here.
}];