鉴于 iOS 6 中对 Auto Layout 的关注,以及 Apple 工程师的建议(参见 WWDC 2012 视频)我们不再直接操作视图的框架,如何避免仅使用 Auto Layout 和 NSLayoutConstraint 的键盘?
更新
这看起来是一个合理的解决方案:键盘敏感布局示例(GitHub 源),但我看到的一个潜在问题是当用户旋转设备并且键盘已经在屏幕上时会发生什么?
那篇博客文章很棒,但我想建议对其进行一些改进。首先,您可以注册观察帧变化,因此您不需要注册观察显示和隐藏通知。其次,您应该将键盘的 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
只要您在键盘启动时不更改方向,这将起作用。
这是关于如何在 iOS 7 上模拟键盘动画以将“完成”按钮添加到数字键盘的答案?展示了如何正确模仿键盘动画曲线。
关于所有这些基于通知的解决方案要注意的最后一件事是:如果您的应用程序中的某些其他屏幕也使用键盘,它们可能会产生意想不到的效果,因为您的视图控制器仍然会收到通知,只要它没有被释放,即使它的视图已卸载。对此的一种补救方法是在通知处理程序中放置一个条件,以确保它仅在视图控制器在屏幕上时运行。
在Spring框架中使用KeyboardLayoutConstraint是迄今为止我发现的最简单的解决方案。
我的想法是创建一个UIView
,我们称之为键盘视图,并将其放置到您的视图控制器的视图中。然后观察键盘框架更改通知UIKeyboardDidChangeFrameNotification
并将键盘框架与键盘视图匹配(我建议对更改进行动画处理)。观察此通知可以处理您提到的旋转以及 iPad 上的移动键盘。
然后简单地创建与此键盘视图相关的约束。不要忘记将约束添加到他们的公共超级视图中。
要使键盘框架正确翻译并旋转到您的视图坐标,请查看UIKeyboardFrameEndUserInfoKey
.
我创建了一个像这样的视图,当键盘打开/关闭屏幕时,它会观察键盘并改变它自己的约束。
@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
我编写了一个库,可以为您完成所有工作(支持 Auto Layout 和 Springs & Struts)
IHKeyboardAvoiding https://github.com/IdleHandsApps/IHKeyboardAvoiding
只需调用 [IHKeyboardAvoiding setAvoidingView:self.myView];
对于带键盘盒的自动布局,我使用静态表格视图。这使您的代码更加简单,并且不需要跟踪键盘高度。我学到的关于表格视图的一件事是使每个表格行尽可能窄。如果将太多 UI 垂直放在一行中,可能会出现键盘重叠。