Question

This is a little weird and I'm not sure what the heck is going on. I'm using a MKMapView that has a custom annotation on it. In that annotation I have a text field that is editable. When the user taps on the field, the keyboard comes up and I respond to that by moving/shrinking my map view and re-centering the annotation. All is good, except on the iPad in landscape mode with a split keyboard. Only in this situation, when I change the frame of my map view it then calls 'resignFirstResponder' on my text field and begins animating the keyboard off screen. As it's being moved offscreen, my code responds to that by moving/expanding my mapview to fill in the space. Then, my text field receives the message becomeFirstResponder again. I checked the call stack and this is coming from: [UIView(Hierarchy) deferredBecomeFirstResponder]. The keyboard comes back up, which I respond to again and the cycle continues: up -> down -> up -> down -> up -> down.....on and on. The resignFirstResponder is definitely coming from setting the frame of my map view. If I set a break on the resignFirstResponder of my text field and check the call stack, the previous call is my setting the map view frame. Also, if I break on my code that responds to the keyboard when it's being dismissed I notice these calls on the stack immediately after my the map view frame is set:

#6  0x001264cf in -[CTView resignFirstResponder]
#7  0x00e3bed7 in -[UIView setUserInteractionEnabled:] ()
#8  0x00710020 in ___lldb_unnamed_function213$$MapKit ()
#9  0x00714c14 in ___lldb_unnamed_function326$$MapKit ()
#10 0x00716f82 in ___lldb_unnamed_function361$$MapKit ()
#11 0x007168c4 in ___lldb_unnamed_function359$$MapKit ()
#12 0x00716068 in ___lldb_unnamed_function344$$MapKit ()
#13 0x00715c5c in ___lldb_unnamed_function343$$MapKit ()
#14 0x007198d0 in ___lldb_unnamed_function385$$MapKit ()
#15 0x0071a1ad in ___lldb_unnamed_function392$$MapKit ()
#16 0x0071a417 in ___lldb_unnamed_function396$$MapKit ()
#17 0x00711074 in ___lldb_unnamed_function255$$MapKit ()

I can get around this by not resizing my map view. If I don't, the cycle never starts up. Instead, if I put the map view in a container view, set clipToBounds = YES on the container view and no auto resizing on the map view, I can resize the container view and leave the map view the same size....the problem is that it's a work around. If the user rotates the iPad, I need to change the size of the map view, but that will kick off this ugly cycle.

Does anybody know what's going on?

Was it helpful?

Solution 2

So in the end the only solution I could figure out was to remove the text field as a subview of MKMapView and place it in MKMapView's superview. From what I can tell, it's not a good idea to allow any subview of MKMapView become a first responder. MKMapView has a tendency to 'steal' back first responder status for frame changes. This can lead to weird responder cycles if you're responding to UIKeyboardNotifications.

Removing the text field from the Map View also ended up being a little better UI experience anyway. By animating the change, the user experienced the shift from "I am manipulating this map" to "I am editing this text field" much more definitely. Disabling the map during the editing also reinforces this. It took a little more work, but in the end proved to be valuable.

OTHER TIPS

During resizing, MKMapView might calculate that the position of the annotation is off-screen and dump or flag the MKAnnotationView containing the UITextField that is currently first responder. This causes the UITextField to get ResignFirstResponder called against it. After the resizing, UIKit decides that the UITextField should still be first responder. Perhaps the UITextField still has isFirstResponder == YES because this particular series of animations is an edge case that UIKit does not handle well. UIKit then shows the keyboard, triggering your code, and on and on and on.

Have you tried playing around with the methods that UITextField inherits from UIResponder? Maybe setting canResignFirstResponder == NO during the resizing animation?

Or, if this is triggered by the MKMapView calculating that the MKAnnotationView should not be shown, have you tried setting up your animation so that the annotation is never calculated as being outside the visible extent?

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top