通常,有两种方法可以在键盘动画到位时保持视图在其上方。 正如您所知,第一种方法是监听
UIKeyboardWillShowNotification
并使用 userData 中的配套 duration/curve/frame 值来帮助您定位和动画化视图以使其位于键盘上方。
第二种方法是为触发键盘的视图(这里是
UITextField
)提供一个
inputAccessoryView
。 iOS 将将您的 inputAccessoryView 与同样父级键盘的视图一起进行动画处理。 在我的经验中,这提供了最佳的动画效果。 我不认为我曾经使用过
UIKeyboardWillShowNotification
方法来获得完美的动画效果,特别是现在在 iOS7 中,键盘动画结束时会有一个小反弹。 可能有一种方法可以使用 UIKit 动力学将此反弹应用于您的视图,但使其与键盘完全同步可能很难。
以下是我以前在类似情况下所做的事情:底部位置有一个
UIToolbar
,其中包含一个自定义视图栏按钮项的
UITextField
输入。 在您的情况下,这位于
UITabBar
上方。
ITextField
设置了一个自定义的
inputAccessoryView
,这是
另一个UIToolbar
,其中包含
另一个UITextField
。
当用户点击文本字段并它成为第一响应者时,键盘与第二个工具栏/文本字段一起动画化到位(这个过渡看起来非常好!)。 当我们注意到这种情况发生时,我们将第一响应者从第一个文本字段转换为第二个文本字段,以便在键盘到位后它具有闪烁的光标。
诀窍在于确定何时结束编辑。 首先,您必须在第二个文本字段上调用 resignFirstResponder,但是如果不小心,系统会将第一响应者状态传递回原始文本字段! 因此,您必须防止这种情况发生,否则您将处于无限循环中,传递第一个响应者,并且键盘永远不会消失。 其次,您需要将任何文本输入镜像到第二个文本字段中,并将其返回到第一个文本字段中。
以下是此方法的代码:
@implementation TSViewController
{
IBOutlet UIToolbar* _toolbar;
IBOutlet UITextField* _textField;
IBOutlet UIToolbar* _inputAccessoryToolbar;
IBOutlet UITextField* _inputAccessoryTextField;
}
- (void) viewDidLoad
{
[super viewDidLoad];
_textField.delegate = self;
_inputAccessoryTextField.delegate = self;
_textField.inputAccessoryView = _inputAccessoryToolbar;
}
- (void) textFieldDidBeginEditing: (UITextField *) textField
{
if ( textField == _textField )
{
dispatch_async(dispatch_get_main_queue(), ^{
_inputAccessoryTextField.text = textField.text;
[_inputAccessoryTextField becomeFirstResponder];
});
}
}
- (BOOL) textFieldShouldBeginEditing: (UITextField *) textField
{
if ( textField == _textField )
{
return ![_inputAccessoryTextField isFirstResponder];
}
return YES;
}
- (void) textFieldDidEndEditing: (UITextField *) textField
{
if ( textField == _inputAccessoryTextField )
{
_textField.text = textField.text;
}
}
- (IBAction) done: (id) sender
{
[_inputAccessoryTextField resignFirstResponder];
}
@end
我能想到的最后一个可能性是:上述方法有两个独立的工具栏/文本框,这是它的缺点。理想情况下,你只需要一个这样的工具栏/文本框,并且希望它看起来像键盘“推”它们(或者拉它们)一样。实际上,动画速度足够快,我认为大多数人不会注意到上述方法中有两组工具栏/文本框,但也许你不喜欢那样...
这种最终的方法监听键盘的显示/隐藏,并使用CADisplayLink在实时检测键盘位置变化时同步动画工具栏/文本框。在我的测试中,它看起来非常好。我看到的主要缺点是工具栏的定位略微滞后。我正在使用自动布局,改用传统的框架定位可能会更快。另一个缺点是对键盘视图层次结构没有剧烈变化的依赖。这可能是最大的风险。
这还有一个技巧。我的storyboard中使用约束来定位工具栏。有两个与视图底部的距离的约束。一个绑定到IBOutlet“_toolbarBottomDistanceConstraint”,代码使用它来移动工具栏。这个约束是一个“垂直空间”约束,具有“等于”关系。我将优先级设置为500。还有一个并行的“垂直空间”约束,具有“大于或等于”关系。这个常数是到视图底部的最小距离(例如,在你的选项卡栏上面),优先级为1000。有了这两个约束,我可以将工具栏距离底部的值设置为任何我喜欢的值,但它永远不会低于我的最小值。这是让它看起来像键盘在推/拉工具栏,但在某一点“掉落”动画的关键。
最后,也许你可以将这种方法与你已经拥有的方法混合使用:使用CADisplayLink回调检测键盘何时“撞上”你的工具栏,然后不是手动定位工具栏剩余的动画,而是使用真正的UIView动画将你的工具栏动画到位。你可以将持续时间设置为键盘显示动画持续时间减去已经经过的时间。
@implementation TSViewController
{
IBOutlet UITextField* _textField;
IBOutlet UIToolbar* _toolbar;
IBOutlet NSLayoutConstraint* _toolbarBottomDistanceConstraint;
CADisplayLink* _displayLink;
}
- (void) dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver: self];
}
- (void) viewDidLoad
{
[super viewDidLoad];
[self.view addGestureRecognizer: [[UITapGestureRecognizer alloc] initWithTarget: self action: @selector( dismiss:) ]];
_textField.inputAccessoryView = [[UIView alloc] init];
[[NSNotificationCenter defaultCenter] addObserver: self
selector: @selector(keyboardWillShowHide:)
name: UIKeyboardWillShowNotification
object: nil];
[[NSNotificationCenter defaultCenter] addObserver: self
selector: @selector(keyboardWillShowHide:)
name: UIKeyboardWillHideNotification
object: nil];
[[NSNotificationCenter defaultCenter] addObserver: self
selector: @selector(keyboardDidShowHide:)
name: UIKeyboardDidShowNotification
object: nil];
[[NSNotificationCenter defaultCenter] addObserver: self
selector: @selector(keyboardDidShowHide:)
name: UIKeyboardDidHideNotification
object: nil];
}
- (void) keyboardWillShowHide: (NSNotification*) n
{
_displayLink = [CADisplayLink displayLinkWithTarget: self selector: @selector( tick: )];
[_displayLink addToRunLoop: [NSRunLoop currentRunLoop] forMode: NSRunLoopCommonModes];
}
- (void) keyboardDidShowHide: (NSNotification*) n
{
[_displayLink removeFromRunLoop: [NSRunLoop currentRunLoop] forMode: NSRunLoopCommonModes];
}
- (void) tick: (CADisplayLink*) dl
{
CGRect r = [_textField.inputAccessoryView.superview.layer.presentationLayer frame];
r = [self.view convertRect: r fromView: _textField.inputAccessoryView.superview.superview];
CGFloat fromBottom = self.view.bounds.size.height - r.origin.y;
_toolbarBottomDistanceConstraint.constant = fromBottom;
}
- (IBAction) dismiss: (id) sender
{
[self.view endEditing: YES];
}
@end
这是视图层次结构和约束条件: