Handling UIKeyboardWillShowNotification so that the keyboard is already pressed when the display controller is pressed?

Usually, if you set UITextField to become the first responder in viewDidLoad or viewWillAppear when the view controller is viewWillAppear the navigation stack, you get an animation in which the view controller shifts to the right with the keyboard already installed (i.e. there is no double animation):

enter image description here
Perfect animation (250 ms)

However, when I use the automatic layout and control the layout of my view depending on the size of the keyboard , it causes strange animations. In this case, my button has a restriction on the bottom of the supervisor, and I update its constant property in the keyboard notification event.

enter image description here
Buggy animation (250 ms)

(Notice how the button, placeholder, and label all the animations go up while you click. Also, notice how the cursor starts below the placeholder text and floats up until it reaches the placeholder.)

The main problem is that when you start a notification, it is already in the animation block (the one that animates the navigation controller), then you delete the other animation block inside the existing one (i.e. self.view.layoutIfNeeded() so that the automatic layout changes animated ).

Of course, I could call startFirstResponder in viewDidAppear , but then I get two animations. I want to use the keyboard behavior already, so the interface looks more responsive (ideally keeping it about 250 ms ).

enter image description here
Dual animation (600 ms)

How can I handle UIKeyboardWillShowNotification in such a way that it will work both when the user clicks on the UITextField and when the view controller is clicked, my ideal animation above?


The perfect solution:

  • Apple will provide a different meaning in the notification dictionary, such as shouldAnimate, which will tell you whether to use the UIAnimation block or not when processing the notification. Example: in the case of this click, it will send false, but if the user simply clicks on the text field, he will send true.

Hacker workarounds:

  • Instead of setting startFirstReponder to viewDidLoad immediately, call it with an arbitrary delay. This will cause an unwanted double animation: first, the view controller moves to the right, and then the keyboard appears. (See the "Double Animation" animation above for an example of this.)
  • It is displayed if you are in the animation block, looking at the key in the notification user information dictionary, for example UIKeyboardAnimationDurationUserInfoKey , which is 0.35 at the time of pressing, and 0.25 when the user clicks on UITextField. This can easily break if the animation is set up, Apple changes the animation, etc.
  • Set the flag to viewWillAppear ( preventAnimationBlock = true ) and viewDidAppear ( preventAnimationBlock = false ). This seems like the safest approach and gives the exact animation I want. However, maintaining state is undesirable.

Solutions that do not work:

  • Layout in viewDidLoad . At the end of the view, they loaded, call setNeedsLayout and layoutIfNeeded , while this solution worked in the past, it does not work with the example button above. This will result in incorrect animation of the label and text, but the button will still not animation correctly.
  • Trying to check if you are already in an animation block . If there was a way to find out if I'm already inside the animation block, this might solve my problem; In this case, I simply would not call layoutIfNeeded . I tried to figure this out using one of these methods , but none of them worked. Probably because it's some kind of hidden / magic animation block that Apple uses internally.
  • Try to see if the screen is displayed on the screen . The idea with this is that the view will report itself behind the scenes because viewDidAppear has not yet been called, and therefore I will not wrap the code in an animation block. However, when you try to find out if the screen is displayed on the screen by looking at its window property , it reports that it is on the screen, and thus this solution will not work.

Reference:

Order of events:

  • viewDidLoad
    • textField.becomeFirstResponder = true
    • register for UIKeyboardWillShowNotification
  • viewWillAppear
  • UIKeyboardWillShowNotification quits
    • myConstraint.constant = keyboardHeight from notification
    • layoutIfNeeded called inside an animation block
  • viewDidAppear

Notification information when a user clicks on textField:

[UIKeyboardFrameBeginUserInfoKey: NSRect: {{0, 736}, {414, 271}},
UIKeyboardCenterEndUserInfoKey: NSPoint: {207, 600.5},
UIKeyboardBoundsUserInfoKey: NSRect: {{0, 0}, {414, 271}},
UIKeyboardFrameEndUserInfoKey: NSRect: {{0, 465}, {414, 271}},
UIKeyboardAnimationDurationUserInfoKey: 0.25,
UIKeyboardCenterBeginUserInfoKey: NSPoint: {207, 871.5},
UIKeyboardAnimationCurveUserInfoKey: 7,
UIKeyboardIsLocalUserInfoKey: 1]

Notification information when calling textField.becomeFirstResponder in viewDidLoad (i.e. when clicked):

[ UIKeyboardFrameBeginUserInfoKey : NSRect: {{0, 465 }, {414, 271}},
UIKeyboardCenterEndUserInfoKey: NSPoint: {207, 600.5},
UIKeyboardBoundsUserInfoKey: NSRect: {{0, 0}, {414, 271}},
UIKeyboardFrameEndUserInfoKey: NSRect: {{0, 465}, {414, 271}},
UIKeyboardAnimationDurationUserInfoKey : 0.35 ,
UIKeyboardCenterBeginUserInfoKey : NSPoint: {207, 600.5 },
UIKeyboardAnimationCurveUserInfoKey: 7,
UIKeyboardIsLocalUserInfoKey: 1]

(The differences are in bold.)

+5
source share

Source: https://habr.com/ru/post/1235051/


All Articles