UIKeyboard避免和自动布局

Mic*_*gle 27 cocoa-touch objective-c uiviewcontroller ios ios6

考虑到重点关注iOS 6中的自动布局,以及Apple工程师的建议(参见WWDC 2012视频)我们不再直接操作视图框架,如何仅使用自动布局和NSLayoutConstraint来避开键盘?

更新

这看起来是一个合理的解决方案:键盘敏感布局(GitHub源)的一个例子,但我看到的一个潜在问题是当用户旋转设备并且键盘已经在屏幕上时会发生什么?

alg*_*gal 7

那篇博文很棒,但我想对它进行一些改进.首先,您可以注册以观察帧更改,因此您无需注册即可观察显示和隐藏通知.其次,您应该将键盘的CGRects从屏幕转换为视图坐标.最后,您可以复制iOS用于键盘本身的确切动画曲线,以便键盘和跟踪视图同步移动.

总而言之,您将获得以下内容:

@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
Run Code Online (Sandbox Code Playgroud)

只要在键盘启动时不改变方向,这将有效.

这是关于如何模仿iOS 7上的键盘动画将"完成"按钮添加到数字键盘的答案展示了如何正确模仿键盘动画曲线.

关于所有这些基于通知的解决方案,最后要注意的是:如果您的应用中的某些其他屏幕也使用键盘,它们会产生意外的效果,因为只要尚未解除分配,您的视图控制器仍会收到通知,即使它的视图被卸载.解决此问题的一种方法是在通知处理程序中放置一个条件,以确保它仅在视图控制器在屏幕上时才运行.


Jam*_*ang 7

Spring框架中使用KeyboardLayoutConstraint是迄今为止我发现的最简单的解决方案. KeyboardLayoutConstraint


Tri*_*ops 6

我的想法是创建一个UIView,让我们称之为键盘视图,并将其放置在视图控制器的视图中.然后观察键盘框架更改通知UIKeyboardDidChangeFrameNotification并将键盘框架与键盘视图匹配(我建议为更改设置动画).观察此通知会处理您提到的旋转并在iPad上移动键盘.

然后只需创建相对于此键盘视图的约束.不要忘记将约束添加到它们的公共超级视图中.

要使键盘框架正确平移并旋转到您的视图坐标,请查看文档UIKeyboardFrameEndUserInfoKey.


Ste*_*ing 5

我创建了一个这样的视图,可以在键盘打开/关闭屏幕时观看键盘并更改自己的约束.

@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
Run Code Online (Sandbox Code Playgroud)