通常,您可以使用两种方法在键盘动画就位时将视图保持在键盘上方。如您所知,第一个是监听UIKeyboardWillShowNotification
并使用 userData 中随附的持续时间/曲线/帧值来帮助您在键盘上方定位和动画化您的视图。
第二种方法是为调用键盘 inputAccessoryView
的视图(此处)提供一个。(我意识到这不会提供您所要求的效果,即一旦键盘碰到它,就会向上“推”工具栏/文本字段。但稍后会详细介绍。) iOS 会将您的 inputAccessoryView 设置为视图还父母键盘和动画他们一起。以我的经验,这提供了最好看的动画。我认为我从来没有使用这种方法制作过完美的动画,尤其是现在在 iOS7 中,键盘动画结束时会有一点反弹。UIKit Dynamics 可能也有一种方法可以将此反弹应用于您的视图,但使其与键盘完美同步会很困难。UITextField
UIKeyboardWillShowNotification
这是我过去为类似于您的场景所做的事情:在 customView 栏按钮项中UIToolbar
有一个底部定位用于输入。UITextField
在您的情况下,它位于 a 上方UITabBar
。有ITextField
一个自定义inputAccessoryView
集,这是另一个 UIToolbar
与另一个 UITextField
。
当用户点击文本字段并成为第一响应者时,键盘将与第二个工具栏/文本字段一起动画到位(这个过渡看起来非常好!)。当我们注意到这种情况发生时,我们将 firstResponder 从第一个文本字段转换到第二个,这样一旦键盘就位,它就会有闪烁的插入符号。
诀窍是当您确定结束编辑的时间时该怎么做。首先,您必须在第二个文本字段上 resignFirstResponder,但如果您不小心,系统会将第一响应者状态传递回原始文本字段!所以你必须防止这种情况发生,否则你将处于传递第一响应者的无限循环中,并且键盘永远不会关闭。其次,您需要将第二个文本字段的任何文本输入镜像回第一个文本字段。
这是这种方法的代码:
@implementation TSViewController
{
IBOutlet UIToolbar* _toolbar; // parented in your view somewhere
IBOutlet UITextField* _textField; // the customView of a UIBarButtonItem in the toolbar
IBOutlet UIToolbar* _inputAccessoryToolbar; // not parented. just owned by the view controller.
IBOutlet UITextField* _inputAccessoryTextField; // the customView of a UIBarButtonItem in the inputAccessoryToolbar
}
- (void) viewDidLoad
{
[super viewDidLoad];
_textField.delegate = self;
_inputAccessoryTextField.delegate = self;
_textField.inputAccessoryView = _inputAccessoryToolbar;
}
- (void) textFieldDidBeginEditing: (UITextField *) textField
{
if ( textField == _textField )
{
// can't change responder directly during textFieldDidBeginEditing. postpone:
dispatch_async(dispatch_get_main_queue(), ^{
_inputAccessoryTextField.text = textField.text;
[_inputAccessoryTextField becomeFirstResponder];
});
}
}
- (BOOL) textFieldShouldBeginEditing: (UITextField *) textField
{
if ( textField == _textField )
{
// only become first responder if the inputAccessoryTextField isn't the first responder.
return ![_inputAccessoryTextField isFirstResponder];
}
return YES;
}
- (void) textFieldDidEndEditing: (UITextField *) textField
{
if ( textField == _inputAccessoryTextField )
{
_textField.text = textField.text;
}
}
// invoke this when you want to dismiss the keyboard!
- (IBAction) done: (id) sender
{
[_inputAccessoryTextField resignFirstResponder];
}
@end
我能想到最后一种可能性。上面的方法有两个单独的工具栏/文本字段的缺点。理想情况下,您想要的只是其中的一组,并且您希望它看起来像是键盘将它们“推”上去(或将它们拉下来)。实际上,动画足够快,我认为大多数人不会注意到上述方法有两套,但也许你不喜欢那样..
最后一种方法监听键盘的显示/隐藏,并CADisplayLink
在实时检测到键盘位置的变化时使用 a 来同步工具栏/文本字段的动画。在我的测试中,它看起来很不错。我看到的主要缺点是工具栏的定位有点滞后。我正在使用自动布局,切换到传统的框架定位可能会更快。另一个缺点是对键盘视图层次结构的依赖性不会发生显着变化。这可能是最大的风险。
这还有另一个技巧。工具栏使用约束定位在我的故事板中。距视图底部的距离有两个约束。一个与 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
这是视图层次结构和约束: