Ios 沿键盘设置UIView动画显示动画

Ios 沿键盘设置UIView动画显示动画,ios,animation,uikeyboard,Ios,Animation,Uikeyboard,我正在使用UIKeyboardWillShowNotification和UIKeyboardWillHideNotification沿键盘显示动画视图,使用UIKeyboardAnimationDurationUserInfoKey,uikeyboardanimationcurveueinfokey和UIKeyboardFrameEndUserInfoKey 只要元素的起始位置在屏幕底部,一切正常。我的元素(屏幕截图中的输入框)从UITabBarController上方开始,因此,如果我的动画开

我正在使用
UIKeyboardWillShowNotification
UIKeyboardWillHideNotification
沿键盘显示动画视图,使用
UIKeyboardAnimationDurationUserInfoKey
uikeyboardanimationcurveueinfokey
UIKeyboardFrameEndUserInfoKey

只要元素的起始位置在屏幕底部,一切正常。我的元素(屏幕截图中的输入框)从UITabBarController上方开始,因此,如果我的动画开始,键盘和UITextField之间会有间隙,该间隙会沿着动画收缩,直到到达其终点

我搜索的是这样的:“使用相同的动画曲线制作动画,但如果键盘到达我的最大位置,就开始移动”

如果我想为动画的启动增加一个延迟的话,这将不符合放松的要求,这可能会在未来的iOS版本中被打破

如果你能和我分享你的想法,那就太好了。:-)


通常有两种方法可用于在键盘动画设置到位时将视图保持在键盘上方。如您所知,第一种方法是监听
ui键盘willshowNotification
,并使用用户数据中附带的持续时间/曲线/帧值来帮助您在键盘上方定位视图并设置其动画

第二种方法是为调用键盘的视图(
UITextField
,此处)提供
inputAccessoryView
(我意识到这不会提供您想要的效果,即一旦键盘进入工具栏/文本字段,就会向上“推”工具栏/文本字段。但稍后会有更多内容。)iOS会将您的inputAccessoryView设置为视图的父视图,该视图也会设置为键盘的父视图,并将它们一起设置动画。根据我的经验,这提供了最好看的动画。我认为我从来没有使用
UIKeyboardWillShowNotification
方法制作过perfect动画,尤其是现在在iOS7中,在键盘动画的末尾有一点弹跳。UIKit Dynamics可能也有一种方法可以将这种反弹应用到您的视图中,但要使其与键盘完全同步是很困难的

下面是我在过去为类似于您的场景所做的工作:在自定义视图栏按钮项中有一个位于底部的
UIToolbar
,用于输入。在您的情况下,它位于
uitabar
上方。
ITextField
有一个自定义的
inputAccessoryView
集,它是另一个
UIToolbar
和另一个UITextField

当用户点击文本字段,它成为第一响应者时,键盘会随着第二个工具栏/文本字段一起移动到位(这个过渡看起来很不错!)。当我们注意到发生这种情况时,我们会将第一个响应程序从第一个文本字段转换到第二个文本字段,这样一旦键盘就位,它就会有闪烁的插入符号

诀窍在于当您确定结束编辑的时间时该怎么做。首先,您必须在第二个文本字段中退出FirstResponder,但是如果您不小心,那么系统会将FirstResponder状态传递回原始文本字段!所以你必须防止这种情况发生,否则你将陷入一个无限循环,传递给第一响应者,键盘将永远不会关闭。其次,您需要将输入到第二个文本字段的任何文本镜像回第一个文本字段

以下是此方法的代码:

@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
同步工具栏/文本字段的动画,因为它实时检测键盘位置的变化。在我的测试中,它看起来相当不错。我看到的主要缺点是工具栏的定位有点滞后。我正在使用自动布局,转换到传统的帧定位可能会更快。另一个缺点是对键盘视图层次结构的依赖性没有显著变化。这可能是最大的风险

这还有一个诀窍。工具栏使用约束定位在我的故事板中。与视图底部的距离有两个约束。一个绑定到IBOutlet“\u toolbarbottomdistance约束”,这就是代码用来移动工具栏的内容。此约束是具有“相等”关系的“垂直空间”约束。我把优先级设置为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
这是视图层次结构和构造