在成为第一响应者或取消第一响应者事件中如何将对象保持在键盘顶部?

13

我当前有一个UITextField在键盘上方。当您点击它时,它应该粘在键盘的顶部并平滑地向上移动。我不知道键盘的确切持续时间和动画类型,所以它会很颠簸。以下是我的代码:

[theTextView resignFirstResponder];
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDelegate:self];
[UIView setAnimationDuration:0.25];
[UIView setAnimationCurve:UIViewAnimationCurveLinear];
// Frame changes go here (move down 216px)
[UIView commitAnimations];

[theTextView becomeFirstResponder];
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDelegate:self];
[UIView setAnimationDuration:0.25];
[UIView setAnimationCurve:UIViewAnimationCurveLinear];
// Frame changes go here (move up 216px)
[UIView commitAnimations];

如果有人以前做过这样的事情,我想知道你用了什么设置来使动画更加流畅,并使它看起来像是条形图“粘”在键盘顶部。

4个回答

46

当键盘显示时,UIKit会发布UIKeyboardWillShowNotification,并在键盘隐藏时发布UIKeyboardWillHideNotification。这些通知包含了你需要正确动画化UITextField所需的所有内容。

假设你的UITextField存储在一个名为myTextField的属性中。

首先,你需要在某个地方注册通知。你注册的位置取决于哪个对象负责移动myTextField。在我的项目中,该字段的父视图负责此操作,并且由于我从一个nib文件中加载我的UI,我在父视图的awakeFromNib方法中完成注册:

- (void)awakeFromNib
{
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHideOrShow:) name:UIKeyboardWillHideNotification object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHideOrShow:) name:UIKeyboardWillShowNotification object:nil];
}

如果你使用UIViewController来移动字段,你可能希望在viewWillAppear:animated:中执行。

你应该在deallocviewWillDisappear:animated:中取消注册:

- (void)dealloc
{
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

当然,棘手的部分在于keyboardWillHideOrShow:方法。首先,我从通知中提取动画参数:

- (void)keyboardWillHideOrShow:(NSNotification *)note
{
    NSDictionary *userInfo = note.userInfo;
    NSTimeInterval duration = [[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
    UIViewAnimationCurve curve = [[userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] intValue];

    CGRect keyboardFrame = [[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];

keyboardFrame 是在全局坐标系中。我需要将该框架转换为与 myTextField.frame 相同的坐标系,并且 myTextField.framemyTextField.superview 的坐标系中:

    CGRect keyboardFrameForTextField = [self.myTextField.superview convertRect:keyboardFrame fromView:nil];

接下来,我计算出我想要myTextField移动到的位置。新框架的底部边缘应该与键盘框架的顶部边缘相等:

    CGRect newTextFieldFrame = self.myTextField.frame;
    newTextFieldFrame.origin.y = keyboardFrameForTextField.origin.y - newTextFieldFrame.size.height;

最后,我使用与键盘相同的动画参数来将myTextField动画到它的新框架中:

    [UIView animateWithDuration:duration delay:0 options:UIViewAnimationOptionBeginFromCurrentState | curve animations:^{
        self.myTextField.frame = newTextFieldFrame;
    } completion:nil];
}

这是完整的代码:

- (void)keyboardWillHideOrShow:(NSNotification *)note
{
    NSDictionary *userInfo = note.userInfo;
    NSTimeInterval duration = [[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
    UIViewAnimationCurve curve = [[userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] intValue];

    CGRect keyboardFrame = [[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
    CGRect keyboardFrameForTextField = [self.myTextField.superview convertRect:keyboardFrame fromView:nil];

    CGRect newTextFieldFrame = self.myTextField.frame;
    newTextFieldFrame.origin.y = keyboardFrameForTextField.origin.y - newTextFieldFrame.size.height;

    [UIView animateWithDuration:duration delay:0 options:UIViewAnimationOptionBeginFromCurrentState | curve animations:^{
        self.myTextField.frame = newTextFieldFrame;
    } completion:nil];
}

1
谢谢。谢谢。谢谢。谢谢。稍加修改后,我让它正常工作了,而且看起来非常流畅。 - iosfreak
@rob mayoff,问题出在我使用了UITabBar。当键盘隐藏时,UITextField会滑动到UITabBar下面。为什么会这样? - Fred Collins
1
UIViewAnimationCurve的值不能直接传递给选项;它们是不同的类型。您需要先进行转换,否则您的时间会稍微偏移:https://dev59.com/aGw05IYBdhLWcg3wSABH - Jesse Rusak
1
一个小小的严谨更正:应该使用integerValue而不是intValue。 - Conor
在IB中对UITextView进行约束,不会破坏这个小技巧吗? - Artem Zaytsev
显示剩余4条评论

5

将您的文本字段(或包含文本字段的视图)设置为您正在编辑的字段的inputAccessoryView。然后它将自动附加到键盘顶部并适当地进行动画处理。


0
为了使UITextField随键盘(带动画)停靠,您需要进行偏移计算,并使用setContentOffset:animation:方法在滚动视图上应用偏移更改(假设UITextField放置在UIScrollView中)。

它不在ScrollView中。ScrollView甚至不存在,它是一个UITableView。 - iosfreak
2
@phpnerd211:UITableView是UIScrollView的子类。 - user836263

0

在您的键盘通知中使用此代码:

@objc func keyboardWillShowNotification(notification: Notification) {
    let keyboardHeight = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue.size.height ?? 216
    let duration = notification.userInfo?[UIKeyboardAnimationDurationUserInfoKey] as? Double ?? 0.25
    let curve = (notification.userInfo?[UIKeyboardAnimationCurveUserInfoKey] as? UInt ?? 7) << 16
    tableViewBottomConstraint.constant = keyboardHeight - submitButtonContainer.frame.size.height

    UIView.animate(withDuration: duration, delay: 0, options: [UIViewAnimationOptions(rawValue: curve)], animations: {
        // Animation
    }, completion: nil)
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接