UIKeyboard避免和自动布局

时间:2012-10-04 13:03:02

标签: objective-c ios cocoa-touch uiviewcontroller ios6

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

更新

这看起来是一个合理的解决方案:An example of keyboard sensitive layoutGitHub source)但我看到的一个潜在问题是当用户旋转设备并且键盘已经在屏幕上时会发生什么?

6 个答案:

答案 0 :(得分: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

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

How to mimic Keyboard animation on iOS 7 to add "Done" button to numeric keyboard?上的答案显示了如何正确模仿键盘动画曲线。

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

答案 1 :(得分:7)

使用KeyboardLayoutConstraint框架中的Spring是我迄今为止找到的最简单的解决方案。 KeyboardLayoutConstraint

答案 2 :(得分:6)

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

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

要正确翻译键盘框并将其旋转到视图坐标,请查看UIKeyboardFrameEndUserInfoKey的文档。

答案 3 :(得分: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

答案 4 :(得分:1)

我编写了一个可以为您完成所有操作的库(支持Auto Layout和Springs& Struts)

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

只需调用[IHKeyboardAvoiding setAvoidingView:self.myView];

答案 5 :(得分:-1)

对于带键盘外壳的自动布局,我使用静态表格视图。这使您的代码更加简单,无需跟踪键盘高度。我从表视图中学到的一件事是让每个表行尽可能地缩小。如果在一行中垂直放置太多UI,则可能会出现键盘重叠。