9

I have a UIPageViewController. One page has a single button, and the other page has a UITextField with a button. When the page scrolls to the view with the field, I'd like it to becomeFirstResponder and open the keyboard. Here's what happens:

  • I call [self.locationQuery becomeFirstResponder] ViewController's viewDidAppear method. But it never opens the keyboard.

  • I do the same in viewWillAppear, and it appears briefly, but then is quickly dismissed.

  • If I'm on the page with the text field, and pull the page partway and let it go (without changing pages), self.locationQuery receives focus just fine.

It seems like something else is grabbing firstResponder status from the field, but I haven't the faintest idea what, and why it would only be happening when the page changed (rather than revealed after a failed page turn). Any ideas?

Update

I created a way to crawl the views to see if any other views were, indeed, taking firstResponder (from an answer to this question: Get the current first responder without using a private API). Results:

  • When I explicitly give first responder to the text field, the method reports it has first responder status.

  • When I don't, it returns null.

Now I'm even more confused.

Community
  • 1
  • 1
dclowd9901
  • 6,756
  • 9
  • 44
  • 63

2 Answers2

5

I don't really understand the nature of what was causing my issue, but I was able to fix it by wrapping the call in an async dispatch in viewDidAppear:

dispatch_async(dispatch_get_main_queue(), ^{
    MapManualViewController *strongSelf = weakSelf;
    [strongSelf.locationQuery becomeFirstResponder];
});
dclowd9901
  • 6,756
  • 9
  • 44
  • 63
  • 1
    had same problem, this fixed it, thanks. I dont totally understand it either, but i guess since we are touching UI by having keyboard appear it makes sense to get main queue – MadeByDouglas Jan 21 '16 at 20:42
  • 2
    I've tried sticking a breakpoint on a method triggered by the UIKeyboardWillHideNotification and looking at the stack trace. It looks like like this is caused by an implementation detail of UIPageViewController where it swaps your view controller's view between two internal scroll views at the end of the animation. The UITextView is then resigning first responder because it's briefly removed from the view hierarchy. Looks like your solution is just delaying becoming first responder until the next run loop. – Adam Swinden Oct 05 '16 at 10:32
0

This one stole a few hours from my life. Here is the Swift 3.x implementation.

DispatchQueue.main.async(execute: {() -> Void in
    let strongSelf: MapManualViewController = self
    strongSelf. textField.becomeFirstResponder()
})

I also put it in viewDidAppear

Arel
  • 3,888
  • 6
  • 37
  • 91
  • You shouldn't need the strongSelf here and this can be shortened to `DispatchQueue.main.async() { self.textField.becomeFirstResoonder() }` – chadbag Oct 27 '17 at 04:56