UIKeyboard avoidance and Auto Layout

后端 未结 6 714
梦谈多话
梦谈多话 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 09:58

    I created a view like this that would watch the keyboard and change its own constraints when the keyboard comes on/off the screen.

    @interface YMKeyboardLayoutHelperView ()
    @property (nonatomic) CGFloat desiredHeight;
    @property (nonatomic) CGFloat duration;
    @end
    
    @implementation YMKeyboardLayoutHelperView
    
    - (id)init
    {
        self = [super init];
        if (self) {
            self.translatesAutoresizingMaskIntoConstraints = NO;
    
            [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name:@"UIKeyboardWillShowNotification" object:nil];
            [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) name:@"UIKeyboardWillHideNotification" object:nil];
        }
        return self;
    }
    
    - (void)keyboardWillShow:(NSNotification *)notification
    {
        // Save the height of keyboard and animation duration
        NSDictionary *userInfo = [notification userInfo];
        CGRect keyboardRect = [userInfo[@"UIKeyboardBoundsUserInfoKey"] CGRectValue];
        self.desiredHeight = CGRectGetHeight(keyboardRect);
        self.duration = [userInfo[@"UIKeyboardAnimationDurationUserInfoKey"] floatValue];
    
        [self setNeedsUpdateConstraints];
    }
    
    - (void)keyboardWillHide:(NSNotification *)notification
    {
        // Reset the desired height (keep the duration)
        self.desiredHeight = 0.0f;
    
        [self setNeedsUpdateConstraints];
    }
    
    - (void)updateConstraints
    {
        [super updateConstraints];
    
        // Remove old constraints
        if ([self.constraints count]) {
            [self removeConstraints:self.constraints];
        }
    
        // Add new constraint with desired height
        NSString *constraintFormat = [NSString stringWithFormat:@"V:[self(%f)]", self.desiredHeight];
        [self addVisualConstraints:constraintFormat views:@{@"self": self}];
    
        // Animate transition
        [UIView animateWithDuration:self.duration animations:^{
            [self.superview layoutIfNeeded];
        }];
    
    }
    
    - (void)dealloc
    {
        [[NSNotificationCenter defaultCenter] removeObserver:self];
    }
    
    @end
    
    0 讨论(0)
  • 2021-01-31 09:58

    For auto layout with keyboard case, I use static table view. This keeps your codes much simpler and not need to keep track of keyboard height. One thing I learned about table view is to keep each table row as narrow as possible. If you put too many UIs vertically in one row, you may get keyboard overlap.

    0 讨论(0)
  • 2021-01-31 10:03

    My idea is to create a UIView, let's call it keyboard view, and place it to your view controller's view. Then observe keyboard frame change notifications UIKeyboardDidChangeFrameNotification and match the frame of the keyboard to the keyboard view (I recommend to animate the change). Observing this notification handles the rotation you mentioned and also moving keyboard on iPad.

    Then simply create your constraints relative to this keyboard view. Don't forget to add the constraint to their common superview.

    To get the keyboard frame correctly translated and rotated to your view coordinates check out the docs for UIKeyboardFrameEndUserInfoKey.

    0 讨论(0)
  • 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.

    0 讨论(0)
  • 2021-01-31 10:12

    Ive written a library that will do it all for you (supports Auto Layout and Springs & Struts)

    IHKeyboardAvoiding https://github.com/IdleHandsApps/IHKeyboardAvoiding

    Just call [IHKeyboardAvoiding setAvoidingView:self.myView];

    0 讨论(0)
  • 2021-01-31 10:17

    Using the KeyboardLayoutConstraint in the Spring framework is the simplest solution I've found so far. KeyboardLayoutConstraint

    0 讨论(0)
提交回复
热议问题