UIKeyboard avoidance and Auto Layout

后端 未结 6 728
梦谈多话
梦谈多话 2021-01-31 09:16

Given the focus on Auto Layout in iOS 6, and the recommendation by Apple engineers (see WWDC 2012 videos) that we no longer manipulate a views\' frame directly, how wou

6条回答
  •  执念已碎
    2021-01-31 10:08

    That blog post is great, but I'd like to suggest some improvements to it. First, you can register to observe frame changes, so you don't need to register to observe both show and hide notifications. Second, you should convert the CGRects for the keyboard from screen to view coordinates. Last, you can copy the exact animation curve used by iOS for the keyboard itself, so the keyboard and the tracking views move in synchrony.

    Putting it all together, you get the following:

    @interface MyViewController ()
    // This IBOutlet holds a reference to the bottom vertical spacer
    // constraint that positions the "tracking view",i.e., the view that
    // we want to track the vertical motion of the keyboard
    @property (weak, nonatomic) IBOutlet NSLayoutConstraint *bottomVerticalSpacerConstraint;
    @end
    
    
    @implementation MyViewController
    -(void)viewDidLoad
    {
      [super viewDidLoad];
      // register for notifications about the keyboard changing frame
      [[NSNotificationCenter defaultCenter] addObserver:self
                                               selector:@selector(keyboardWillChangeFrame:)
                                                   name:UIKeyboardWillChangeFrameNotification
                                                 object:self.view.window];
    }
    
    -(void)keyboardWillChangeFrame:(NSNotification*)notification
    {
      NSDictionary * userInfo = notification.userInfo;
      UIViewAnimationCurve animationCurve  = [userInfo[UIKeyboardAnimationCurveUserInfoKey] intValue];
      NSTimeInterval duration = [userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue];
    
      // convert the keyboard's CGRect from screen coords to view coords
      CGRect kbEndFrame = [self.view convertRect:[[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue]
                                        fromView:self.view.window];
      CGRect kbBeginFrame = [self.view convertRect:[[userInfo objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue]
                                          fromView:self.view.window];
      CGFloat deltaKeyBoardOrigin = kbEndFrame.origin.y - kbBeginFrame.origin.y;
    
      // update the constant factor of the constraint governing your tracking view
      self.bottomVerticalSpacerConstraint.constant -= deltaKeyBoardOrigin;
      // tell the constraint solver it needs to re-solve other constraints.
      [self.view setNeedsUpdateConstraints];
    
      [UIView beginAnimations:nil context:NULL];
      [UIView setAnimationDuration:duration];
      [UIView setAnimationCurve:animationCurve];
      [UIView setAnimationBeginsFromCurrentState:YES];
      // within this animation block, force the layout engine to apply 
      // the new layout changes immediately, so that we
      // animate to that new layout. We need to use old-style
      // UIView animations to pass the curve type.
      [self.view layoutIfNeeded];
      [UIView commitAnimations];
    }
    
    -(void)dealloc {
      [[NSNotificationCenter defaultCenter] removeObserver:self
                                                      name:UIKeyboardWillChangeFrameNotification
                                                    object:nil];
    }
    @end
    

    This will work, as long as you don't change orientation while the keyboard is up.

    It was an answer on How to mimic Keyboard animation on iOS 7 to add "Done" button to numeric keyboard? showed how to mimic the keyboard animation curve correctly.

    One last thing to beware of with respect to all these notification-based solutions: they can produce unexpected effects if some other screen in your app also uses the keyboard, because your view controller will still receive the notifications as long as it has not been deallocated, even if it's views are unloaded. One remedy for this is to put a conditional in the notification handler to ensure it only operates when the view controller is on screen.

提交回复
热议问题