0

I am presenting a UIViewController from another UIViewController. The presented view controller implements viewDidAppear as such:

- (void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];
    [self.addressTextView becomeFirstResponder];
}

However, if I implement viewDidAppear like this:

- (void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];
    [self.addressTextView performSelector:@selector(becomeFirstResponder) withObject:nil afterDelay:0.0f];
}

There's no noticeable delay in the presentation animation.

My main question is, does anyone know if there's a another way to make the text field become first responder, without delaying my present animation? I do not consider the second solution clean. I'm relying on some implementation detail of how perform selector works to get the functionality I want. I'd rather not.

I'm also curious why there's such a delay with the first method and how the first method differs from the second method "behind the scenes".

edit: It's perhaps worth noting that the delay in the present view controller animation only occurs the FIRST time it's presented.

BrennanR
  • 203
  • 3
  • 13

1 Answers1

3

This is a known problem. Have a look here.

To Fix it simply add this to your AppDelegate:

UITextField *lagFreeField = [[UITextField alloc] init];
lagFreeField.hidden = YES;
[self.window addSubview:lagFreeField];
[lagFreeField becomeFirstResponder];
[lagFreeField resignFirstResponder];
[lagFreeField removeFromSuperview];

It will load an invisible Keyboard on Application Launch, which caches the keyboard and makes it load faster on subsequent appearances i.e your text View.

While this will fix it, it will make your Application Load slower(and even possibly crash your application because it takes to long)

Another options would be to dispatch the becomeFirstResponder to the main Thread which will make it run after the push/modal animation finished (essentially the same as your second solution but way cleaner):

dispatch_async(dispatch_get_main_queue(), ^(void){
  [self.adressTextView becomeFirstResponder];
});

I'd choose the second one.

Anyways to answer your second question:

-performSelector:withObject:afterDelay: with a delay of 0.0 seconds does not execute the given selector immediately but instead performs it after the current Runloop Cycle finishes and after the given delay.

Therefore using -performSelector:withObject:afterDelay: the UI has a change of getting updated in the current Runloop Cycle i.e in this case the view is pushed, before your selector is performed(and make your textView firstResponder and so on) which makes everything smooth.

Source: Apple Dev Docs and this Thread Answer

Hope I Could Help.

Community
  • 1
  • 1
Malte
  • 704
  • 2
  • 5
  • 22