I've got a keyboard toolbar that isn't being hidden properly. If the user hits the Done button, I call the following code:
- (void)accessoryDoneAction:(id)sender {
[self.selectedAmount endEditing:YES];
}
Then if the user moves to the next screen, comes back, locks the phone and unlocks it everything is fine. However, if the user does not hit the Done button, and moves on to the next view, comes back, locks the phone and unlocks the phone, they will see the keyboard toolbar at the bottom of the screen.
The only difference between the two scenarios, that I can see, is that if the user does not hit the done button, endEditing isn't called until viewWillDisappear: or viewDidDisappear: (I've tried both.) Neither one seems to have the same effect, as the toolbar is still present after moving on, coming back, locking and unlocking.
I've even tried placing it in viewDidAppear: after reading through this thread Keyboard does not disappear after viewDidDisappear on iOS 7, but that didn't work either.
Any suggestions for why calling endEditing: in the lifecycle methods isn't accomplishing the same thing as calling it when the Done button is hit?
Related
I have a simple iOS application written in Swift that is hanging every 2nd time I tap a row to present the next view.
The first time, 3rd, 5th, etc times I select a row it waits for 5-15 seconds and then a modal MZFormSheetPresentationController (or whatever modal view) is shown.
If I dismiss and tap the row again (to show the same modal) it appears immediately. If I'm in the delay sequence and I tap somewhere else it triggers the appearance immediately. That made me feel like a main thread issue, but I can't find anywhere I get off track.
I found prepareForSegue was missing a super call, but adding it, or commenting entirely has no bearing. Happens regardless of if the Segue kind is Modal or Custom; if it has a custom class or not, if animates is enabled
Sometimes it feels like taps are being dropped entirely (The dismiss button feels especially random).
iOS 7 introduces edge swiping to navigate backwards in a navigation controller (so long as you don't override the leftBarButtonItem, among other things).
Imagine I'm implementing an app similar to Apple Notes. In Apple's app, once the user taps into a note, there is a Back button on the top left, and a Done button on the top right. This view controller is automatically saved after the user navigates away. Even if the user swipes backwards while still editing the note, it will auto-save.
One way to implement this is to write to disk on each key stroke. However, I'm looking for a more efficient implementation.
Which method should I override to perform my auto-save?
To be clear, the auto-save code should be invoked in these three cases:
The user taps the Back button
The user swipes backwards
The user taps the Done button
So far, I've been implementing the save logic in viewWillDisappear, this minimizes the amounts of writes, and also takes care to save if the user kills the app.
Additionally, instead of implementing save logic in the Done button, the Done button can simply call:
[self.navigationController popViewControllerAnimated:YES];
... (e.g. via a delegate, or unwind segue) and it will automatically save as well. All three code paths will go through viewWillDisappear.
Although it could technically be implemented in viewDidDisappear, if the previous view controller needs to display the updated data, this method will be too late.
For more complex view controllers (e.g. those that have destructive behavior once the view controller is dismissed), there are several other things to consider:
Should the save be called if a modal is presented above the current view controller? (e.g. In Apple Notes, imagine that the share button launched a modal; should it save when you tap on this button?). If it is important not to save at this time, you can read the value of presentedViewController. If it has a value, that means viewWillDisappear is being called because a modal is being presented above it.
Should the save be called if a view controller is pushed on to the current navigation stack? (e.g. Again, in Apple Notes, if the share button used a push instead of a modal, should it save?) You can check the value of isMovingFromParentViewController in this case.
If the user taps the home button to quit your app, should it save?
If the user begins swiping backwards then cancels the swipe to stay on the current screen, should it save?
Imagine the view controller is in a modal, when the modal is dismissed should the destructive action happen? Check isBeingDismissed in this case.
Same as above, but imagine the view controller is the second page in a navigation controller than is being presented.
I'm new to iOS development and have recently learned that to make the on screen keyboard disappear we must always call resignFirstResponder on the text view. This causes the text view to relinquish it's first responder status, and hence the keyboard disappears, since there is no need for the text view to respond.
However I also noticed that there's the becomeFirstResponder method to make a view a first responder. However, this method is never called on the text view. So when/how does a textview become first responder when that method is never called?(at least, by me. I'm unsure if it is called elsewhere in the system)
My theory is that is has to already be a first responder before it can resign the first responder status.
firstResponder status is automatically handled for you when a user taps on the text field. So long as user interaction is enabled for the UITextField/UITextView, the keyboard should appear when tapped.
You can monitor for it using the delegate method textViewDidBeginEditiing or, more broadly, by listening for keyboard appearance notifications (UIKeyboardWillShowNotification and UIKeyboardDidShowNotification).
Further, there are ways to dismiss the keyboard without the need of calling the corresponding method resignFirstResponder (such as calling endEditing: on a container view, or setting a scroll view's UIScrollViewKeyboardDismissMode).
Note: In the simulator, it is possible that the keyboard still doesn't appear even if all is correctly working. In that case you just want to make sure keyboard hardware is toggled (CMD+K) for the simulator.
You call becomeFirstResponder yourself if you want to give a UITextField focus and show the keyboard.
This is useful for view controllers that are basically forms. For example, if a user presses a "Sign Up" button, you might push a view controller with a couple of text fields and call becomeFirstResponder on the first one to automatically give it focus and open the keyboard.
I had issue that every time when I enter a view controller - which is the "MessageListViewController" in the following screen-shot, the keyboard will be automatically popped out.
The thing is I used a UITextView in this MessageListViewController for text input, and when I pop this view controller from navigationController stack, if I leave the keyboard open, then the next time when it was pushed again, this keyboard will be popped out automatically.
If I make the text view resignFirstResponder before I popped out this view controller, then the next time enter it will be fine. However resignFirstResponder manually will make the keyboard dismissing with an ugly animation (manually dismiss keyboard it will make the keyboard always go downing the screen vertically, however the view controller popping transaction is horizontal, so it will look very wired)
I tried to build a simple sample to test the keyboard dismiss behavior - there are two view controllers, one is the rootViewController of the navigationController, and it will push another into the viewController stack, so the pushed view controller contains a UITextView, if I highlight the text view of the pushed one, then go back to the root view controller, the animation works just fine, also the keyboard won't be automatically popped out. So I just don't know why in my project, this keyboard always shows up unexpectedly if I just left the page without manually dismiss the keyboard.
BTW, the following call stack happened between viewWillAppear and viewDidAppear. It looks like triggered by iOS to restore the last state of the UITextView.
- (UIView*)findFirstResponder {
if (self.isFirstResponder) {
return self;
}
for (UIView* subview in self.subviews) {
UIView* ret = [subview findFirstResponder];
if (ret) {
return ret;
}
}
return nil;
}
NSLog(#"%#", [[[UIApplication sharedApplication] keyWindow] findFirstResponder]);
I also tried to track with the key window's first responder when this call stack triggered by keyboard will show notification, and the out put is just "nil".
Thanks guys for helping, I just realized where the problem is, actually I did not declare that the messagelistViewController which I used here is actually a singleton, so it never release even I pop the this out of the viewController stack.
Under that scenario, when the next time this view controller was appear again, the UIKit will be smart enough to restore the previous view state if it's not dealloced.
And in my test sample, I did not keep the test viewcontroller when it was popped out of stack.
so that's why this works fine for my test sample.
Then my final solution is kinda simple, just remove the textview from it's superview when the view did disappear, and then re-add this back when the view will appear.
It turns out my question is a little bit stupid, however it do let me know two things:
1) The view controller will restore to it's previous state every time when it appears if you not delete it
2) The keyboard dismiss animation will always be going down if manually resign the input from the first responder, if we don't want this effect, we shall never dismiss it manually.
I created a demo in iOS 7 as per you said in your question, it was working fine for me even I didn't have to call resignFirstResponder method for resigning keyboard. So you could look in your code as you might have been calling becomeFirstResponder somewhere making the keyboard to be active again or simply post your code here so I could have a look at it.
I know it's a very long shot and the problem description is vague at best, but I'm hoping someone might have encountered that as well.
I have a UIViewController subclass that is being presented modally. The view of the controller contains some UIButtons and some UITextFields. The view controller might be presented multiple times during a user session; it is created every time it's presented and gets destroyed when it's dismissed. Most of the time everything works as expected, however, sometimes the text fields don't respond to user touch, i.e. tapping them does not bring up the keyboard. The buttons on the view work fine, it's just the text fields that become disabled.
What could possibly be causing this?
Turns out the reason UITextFields inside a modally presented view controller were not responding was that one of the long-living view controllers in the app called [self becomeFirstResponder] on itself, but never called [self resignFirstResponder]. It had to be the first responder in order for it to receive remote controls events when the app was playing music.