I've been having some issues with calling -becomeFirstResponder on a UITextField contained with a view controller that is presented modally. I call this method in the modal view controller's -viewDidLoad method so that the keyboard is immediately displayed. What I expected is for both the keyboard and the modal view controller to animate from up the bottom of the screen at the same time. However, what I'm observing is the following:
There is a ~0.2 second UI lag between clicking the button that calls the -presentModalViewController:animated: method on the parent view controller and when the child view controller begins to animate modally.
The keyboard is immediately presented with absolutely no animation as soon as the modal view controller's animation begins.
Once the modal view controller's animation is complete, everything else seems to operate smoothly.
Dismissing the modal view controller results in it being smoothly animated off screen (along with the keyboard, coincidentally).
Clicking the button that presents the modal view controller any time after the first time results in the same pattern except that there is no ~0.2 second UI lag.
It's as if the keyboard's animation and the modal view controller's animation are both competing for some lower-level Core Animation resource at the same time, but I don't see why this should be happening. What further seems to corroborate this hunch is if I don't ask the UITextField to become the first responder (i.e., if I don't ask the keyboard to present itself), then there is absolutely no UI lag, and the modal view controller animates instantly.
Interestingly, if I do something like [self.textField performSelector:#selector(becomeFirstResponder) withObject:nil afterDelay:0.0001]; then the animation of the keyboard happens nearly at the same time as the modal view controller's animation -- it's extremely difficult to tell that they aren't both being animated at the exact same time when running on the iPhone Simulator. However, when running on an actual device, it's easily noticeable that the keyboard doesn't appear until after the modal view controller is presented. Importantly, though, there's no more UI lag.
Has anyone experienced anything similar to this?
I believe you're having problems because you're effectively stacking animations. The keyboard view is contained by the modal view. The keyboard view is trying to animate its slide in transition within the context of a view which is itself animating a slide in transition. The keyboard animation is trying to hit a moving target.
The pause is most likely the run time of the keyboard transition animation. I am fairly certain the the keyboard animation seizes priority from other animations so that it can drive the rearrangement of the UI e.g. scrolling a table so that the keyboard does not overlay the edited table row. In any case, the keyboard animation occurs within the context of the superview. This is especially true in the case of modal view.
So, the keyboard view animates itself sliding in but because the superview is not actually visible yet, you see nothing. When the superview does slide in, the keyboard is already present because its animation was completed before the superview started its animation.
In short, I don't think you can actual accomplish what you want to do. Instead, I think you will have to animate the modal view transition first, then run the keyboard animation or you will have to accept having the keyboard immediately visible.
I think that Cirrostratus' suggest above is a good one. Use an image of the keyboard that will slide in with the modal view and then immediately swap it out with the real keyboard.
The delayed keyboard animation bothered me as well. viewDidLayoutSubviews was the magical method I was looking for. Placing the becomeFirstResponder call there makes the keyboard slide up in time with the modal.
https://stackoverflow.com/a/19517739/3618864
Try moving your code that sends becomeFirstResponder out of viewDidLoad and into viewWillAppear. I think it is starting too early, you want the keyboard animation to happen when the view appearing animation happens.
Are you saying that you are seeing lag on the Simulator but not on the device? If that's the case you might be seeing a lag due to your computer taking it's time loading everything into memory. When loading up the Simulator the first time it's not just running the code natively, it's probably loading all manner of runtime and debugging libraries. Once loaded into memory the system is probably rather fast. If you are experiancing lag on the Simulator maybe some more RAM in your dev machine might help. If your machine is a few years old you might think about going for something new.
This is what I did to make the keyboard appear to animate exactly the same time as a modalviewcontroller:
In the (init) method of the view being presented modally, I created the UITextField and made it the first responder. Then when I present the modal view controller with animation they both appear at the same time.
Related
I have a main uinavigationcontroller that loads a uitableview as a subview as my "main view", I push a new uiview controller on, then in that viewcontroller I add a left nav button to popviewcontroller, bringing me back to the main view
all of that works just fine, the issue is when I pop back to the main view all input is doa, no swipes, scrolls, gestures, whatever, can't click on any of the table cells or scroll the table
not getting thrown to the debugger so I can only assume I haven't crashed the app, and I can do programmatic things in viewdidappear on the main view that affects the view, set timers, etc, but my screen input just doesn't work
any ideas as to where my input goes?
It's hard without any code, but you're probably adding gesture recognizers to views in the view you just popped. You can try adding the gestures to UINavigationController's view, or window.view, or some other view that stays alive between navigation stack pushing/popping.
I am currently executing my own version of this opening animation
http://www.raywenderlich.com/2454/how-to-use-uiview-animation-tutorial
The animation runs perfectly however once the default.png disappears and my animation starts the UITabBar and the UIPicker I have loaded in the first view are sitting above my animation when it plays.. I am wondering how to make sure this animation plays above everything.
Before running the animation, call:
[self.view bringSubviewToFront:basketTop];
[self.view bringSubviewToFront:basketBottom];
[self.view bringSubviewToFront:napkinTop];
[self.view bringSubviewToFront:napkinBottom];
This basically pulls those 4 objects to the front of the view, so they are on top of everything else. You might need to play around with the order in which you bring the 4 to the front.
Except for the z-order you should check that the view you are trying to animate has the proper size, otherwise it can't possibly cover the whole screen. And of course check your view's superview - if the superview covers only the area between UITabBar and UIPicker it won't cover them, ever.
In your application:didFinishLaunchingWithOptions: you could present a modal view controller and perform your animation(s) inside of it then remove the controller.
Or you could perform those animations on the window.
Since the controller's view is inside a a tab bar controller, its view doesn't cover the tab bar and therefore cannot show over it.
I have a UIViewController (A) that modally presents a second view controller (B). Then, that second view controller modally presents a UIImagePickerController (IP). Basically, I have a stack of 2 modal view controllers.
(A) --modally presents--> (B) --modally presents--> (IP)
View controller (A) is the delegate of the image picker, and it dismisses the entire modal stack using:
[self dismissModalViewControllerAnimated:YES];
The problem is with the animation. When dismissing a modal stack like this, the currently visible view should slide off the bottom of the screen, revealing the newly visible view. So in this case, I expect (IP) to slide off the bottom of the screen, revealing the view for (A).
However, what actually happens is this: The image picker view simply disappears, immediately revealing the view for (A), and only the navigation bar animates off the bottom of the screen. The status bar is also left as black translucent instead of transitioning back to a standard gray; this seems to indicate that the image picker normally does some kind of "cleaning up" that isn't being performed when it's dismissed as part of a modal stack.
If I replace the image picker with another generic view controller, the animation works fine. If (IP) is dismissed by (B), the animation also works fine. The problem seems to occur only when dismissing multiple modal view controllers containing UIImagePickerController.
Has anyone seen this before? Any ideas what I might be doing wrong or how to work around this?
Unfortunately, the method dismissModalViewControllerAnimated does not work exactly as you would expect (at least not visually). To achieve what you want, you need to dismiss both modal viewcontrollers in a row, the first non-animated and the second one animated, as described e.g. here.
I have a view controller with animation that run when the view controller appears.
Every time the view controller appears the animation should reset to certain position and replay the animation again.
The viewWillAppear resets the position of the animated subviews.
The viewDidAppear runs CALayer animation.
It works good first time, but when I present a modal view controller and dismiss it, the animated view controller's subviews appear for a small time in the final position of the animation, then it resets to the original position where it starts the animation.
I made sure viewWillAppear is called where it should reset the animated subviews' positions.
Any suggestions are welcome.
Thanks for advance.
I think you intuition is correct: when you dismiss the modal view controller, the underlying view is displayed again and it controller receives the viewWillAppear/viewDidAppear messages.
One possibility you have to fix this is resetting the animation in viewDidLoad, which is called once the view has been loaded in memory (just once if the view is not unloaded). Indeed this method is used to complete the view initialization and seems the right place where to put the reset of the animation.
If this solution is not right for your app (I have no idea about what you are doing overall) and you need to have the reset in the viewWillAppear, you could think of setting a flag in your controller when the modal view is displayed, so you know, the next time your controller receives the viewWillAppear that it comes after the dismiss of the modal view and you don't do the reset. I am not suggesting you to do this, which is pretty hacky and not very resilient, just saying, if you cannot accept the other solution. Much better redesigning your app so that reset is not required in viewWillAppear.
I have a paged scrollview. Each page has a viewcontroller (buttonViewController) that manages a grid of buttons. If you press one of the buttons, the buttonViewController pops up another viewcontroller (detailViewController) modally.
The problem is, when I dismiss the modal view controller, the visible buttonViewController is shifted down by what looks like about 20 pixels. The weird thing is that only the visible page is shifted. If I scroll over to another page that is already loaded, it is still in the correct position.
My question is basically the same as dismissing modalViewController moves main view buttons around iphone
However, the accepted answer to that question assumed that the status bar is hidden. I am not hiding the status bar in my application.
Any other ideas?
One more note: The shift only happens the first time I launch a modal view controller. If I keep opening and closing the modal view controller, everything stays the same.
One further note: if I add the following code:
CGRect frame = self.view.frame;
frame.origin.y=0;
self.view.frame = frame;
after I dismiss the modal view controller, then I can work around the problem. The frame seems to move by 20pixels in y. I still don't know what's causing the move though.
Well I had this problem and it was due to the way my first viewController was set up (on the UIWindow), it is very weird since this had never happened to me before, the behavior I was observing was that when you add a subview at point (0,0) it would overlap with the status bar, which is not usual, usually the views know about the status bar and they behave appropriately. As a side effect, when one pushes a modal view that takes off the status bar, after dismissing it I saw the same problem you are seeing. What I did to fix was to set the UIWindows view to my controllers view in the nib, this seemed to fix everything. Once again I don't know why simply adding to the window programmatically with CGRectMake(0,0,320,460) had this odd behavior, since I have been doing this for all my previous projects and saw the correct behavior. But when I set it up in the nib, it worked as expected. Maybe this is your problem as well.
Turns out this was related to:
ModalViewController doesn't display during animation when paged scrollView is scrolled
I had mismatched the heights of some of my viewcontrollers, and that was throwing everything off.
Some of the views had heights of 460pixels, some had 480pixels. For some reason, this made it shift by 20pixels evertime a modal view controller disappeared. Once I made everything 460pixels, everything worked great.