首页 文章

键盘处理就像iOS 7中的Messages app一样

提问于
浏览
10

我正在实现一个类似于Messages应用程序中发生的视图,因此有一个视图,UITextView附加到屏幕的底部,还有UITableView显示主要内容 . 当它被轻敲时,它会随着键盘向上滑动,当键盘被解除时,它会滑回到屏幕的底部 .

我有这个部分并且它完美地工作 - 我只是订阅键盘通知 - 将隐藏和显示 .

问题是我已经在UITableView上将键盘解除模式设置为交互式,并且我无法在平移时捕获对键盘的更改 .

第二个问题是这个uitextview栏覆盖了uitableview的某些部分 . 如何解决这个问题?我仍然希望uitableview像在消息应用程序中一样“在”这个栏下面 .

我在所有地方都使用AutoLayout .

任何帮助将不胜感激!

============

EDIT1:这是一些代码:

查看层次结构如下:

视图 - UITableView(这个将包含“消息”) - UIView(这个将滑动)

UITableView对父视图的顶部,左侧,右侧和底部有约束,因此它填满整个屏幕 . UIView对父视图的左侧,右侧和底部有约束,因此它粘在底部 - 我通过调整约束的常量来移动它 .

在ViewWillAppear方法中:

NSNotificationCenter.DefaultCenter.AddObserver (UIKeyboard.DidShowNotification, OnKeyboardDidShowNotification);
NSNotificationCenter.DefaultCenter.AddObserver (UIKeyboard.WillChangeFrameNotification, OnKeyboardDidShowNotification);
NSNotificationCenter.DefaultCenter.AddObserver (UIKeyboard.WillHideNotification, OnKeyboardWillHideNotification);

以下是方法:

void OnKeyboardDidShowNotification (NSNotification notification)
{
    AdjustViewToKeyboard (Ui.KeyboardHeightFromNotification (notification), notification);
}

void OnKeyboardWillHideNotification (NSNotification notification)
{   
    AdjustViewToKeyboard (0.0f, notification);
}

void AdjustViewToKeyboard (float offset, NSNotification notification = null)
{
    commentEditViewBottomConstraint.Constant = -offset;

    if (notification != null) {
        UIView.BeginAnimations (null, IntPtr.Zero);
        UIView.SetAnimationDuration (Ui.KeyboardAnimationDurationFromNotification (notification));
        UIView.SetAnimationCurve ((UIViewAnimationCurve)Ui.KeyboardAnimationCurveFromNotification (notification));
        UIView.SetAnimationBeginsFromCurrentState (true);
    }

    View.LayoutIfNeeded ();
    commentEditView.LayoutIfNeeded ();

    var insets = commentsListView.ContentInset;
    insets.Bottom = offset;
    commentsListView.ContentInset = insets;

    if (notification != null) {
        UIView.CommitAnimations ();
    }
}

5 回答

  • 2

    好的,交互式键盘解雇将发送名为 UIKeyboardDidChangeFrameNotification 的通知 .

    这可用于在交互式解除键盘时移动文本视图 .

    您已经在使用此功能,但是您要将其发送到 OnKeyboardDidShow 方法 .

    你需要第三种叫做 keyboardFramedDidChange 的方法 . 这适用于皮革和秀 .

    对于第二个问题,你应该有这样的垂直约束......

    |[theTableView][theTextView (==44)]|
    

    这会将tableview的底部绑定到文本视图的顶部 .

    这不会改变任何动画的工作方式,只是确保表视图将显示其所有内容,无论键盘是否可见 .

    不要更新表视图的内容insets . 使用约束确保框架不重叠 .

    附:理清你的命名惯例 . 方法名称以小写字母开头 .

    P.P.S.使用基于块的动画 .

  • 4

    我试着使用一个空的,零高度的 inputAccessoryView . 诀窍是粘贴你的文本字段's bottom to it when the keyboard appears, so that they' d一起移动 . 键盘消失后,您可以破坏该约束并再次粘贴到屏幕底部 .

  • 11

    我为此目的制作了一个开源库 . 它适用于iOS 7和8,并且也可以作为cocoapod使用 .

    https://github.com/oseparovic/MessageComposerView

    以下是它的样子:

    MessageComposerView

    您可以使用如下所示的非常基本的 init 函数来创建屏幕宽度和默认高度,例如:

    self.messageComposerView = [[MessageComposerView alloc] init];
    self.messageComposerView.delegate = self;
    [self.view addSubview:self.messageComposerView];
    

    还有一些其他初始化程序可用于自定义框架,键盘偏移和textview最大高度以及一些代表以挂钩框架更改和按钮单击 . 请参阅自述文件了解更多!

  • 4

    我建议您覆盖视图控制器的-inputAccessoryView属性,并将可编辑的UITextView作为其子视图 . 另外,不要忘记覆盖-canBecomeFirstResponder方法以返回YES .

    - (BOOL)canBecomeFirstResponder
    {
    if (!RUNNING_ON_IOS7 && !RUNNING_ON_IPAD)
    {
        //Workaround for iOS6-specific bug
        return !(self.viewDisappearing) && (!self.viewAppearing);
    }
    
    return !(self.viewDisappearing);
    }
    

    通过这种方法,系统管理一切 .

    还有一些你必须了解的解决方法:对于UISplitViewController(UISplitViewController detail-only inputAccessoryView),对于释放错误(UIViewController with inputAccessoryView is not deallocated)等等 .

  • 5

    该解决方案基于SO的许多不同答案 . 它有很多好处:

    • 隐藏键盘时,撰写栏保持在底部

    • UITableView 上进行交互式手势时,按键盘跟随键盘

    • UITableViewCell 是自下而上的,就像在消息应用中一样

    • 键盘不会阻止查看全部 UITableViewCell

    • 适用于iOS6,iOS7和iOS8

    这段代码正常工作:

    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    {
        UITableViewCell *cell = // . . .
    
        // . . .
    
        cell.contentView.transform = CGAffineTransformMakeScale(1,-1);
        cell.accessoryView.transform = CGAffineTransformMakeScale(1,-1);
        return cell;
    }
    
    - (UIView *)inputAccessoryView {
        return self.composeBar;
    }
    
    - (BOOL)canBecomeFirstResponder {
        return YES;
    }
    
    - (void)viewDidLoad
    {
        [super viewDidLoad];
    
        self.tableView.transform = CGAffineTransformMakeScale(1,-1);
    
        // This code prevent bottom inset animation while appearing view
        UIEdgeInsets newEdgeInsets = self.tableView.contentInset;
        newEdgeInsets.top = CGRectGetMaxY(self.navigationController.navigationBar.frame);
        newEdgeInsets.bottom = self.view.bounds.size.height - self.composeBar.frame.origin.y;
        self.tableView.contentInset = newEdgeInsets;
        self.tableView.scrollIndicatorInsets = newEdgeInsets;
        self.tableView.contentOffset = CGPointMake(0, -newEdgeInsets.bottom);
    
        // This code need to be done if you added compose bar via IB
        self.composeBar.delegate = self;
        [self.composeBar removeFromSuperview];
    
        [[NSNotificationCenter defaultCenter] addObserverForName:UIKeyboardWillChangeFrameNotification object:nil queue:nil usingBlock:^(NSNotification *note)
        {
            NSNumber *duration = note.userInfo[UIKeyboardAnimationDurationUserInfoKey];
            NSNumber *options = note.userInfo[UIKeyboardAnimationCurveUserInfoKey];
            CGRect beginFrame = [note.userInfo[UIKeyboardFrameBeginUserInfoKey] CGRectValue];
            CGRect endFrame = [note.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
    
            UIEdgeInsets newEdgeInsets = self.tableView.contentInset;
            newEdgeInsets.bottom = self.view.bounds.size.height - endFrame.origin.y;
            CGPoint newContentOffset = self.tableView.contentOffset;
            newContentOffset.y += endFrame.origin.y - beginFrame.origin.y;
    
            [UIView animateWithDuration:duration.doubleValue
                                  delay:0.0
                                options:options.integerValue << 16
                             animations:^{
                                 self.tableView.contentInset = newEdgeInsets;
                                 self.tableView.scrollIndicatorInsets = newEdgeInsets;
                                 self.tableView.contentOffset = newContentOffset;
                             } completion:^(BOOL finished) {
                                 ;
                             }];
        }];
    }
    

    例如 pod 'PHFComposeBarView' 撰写栏:

    @property (nonatomic, strong) IBOutlet PHFComposeBarView *composeBar;
    

    并将此类用于表视图:

    @interface InverseTableView : UITableView
    @end
    @implementation InverseTableView
    void swapCGFLoat(CGFloat *a, CGFloat *b) {
        CGFloat tmp = *a;
        *a = *b;
        *b = tmp;
    }
    - (UIEdgeInsets)contentInset {
        UIEdgeInsets insets = [super contentInset];
        swapCGFLoat(&insets.top, &insets.bottom);
        return insets;
    }
    - (void)setContentInset:(UIEdgeInsets)contentInset {
        swapCGFLoat(&contentInset.top, &contentInset.bottom);
        [super setContentInset:contentInset];
    }
    @end
    

    如果您希望通过点击消息来消除键盘:

    - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
    {
        [self.composeBar.textView resignFirstResponder];
    }
    

    不要打电话给这个,这将隐藏 composeBar

    [self resignFirstResponder];
    

    UPDATE 2:

    键盘跟踪的新解决方案效果更好:

    - (void)viewDidAppear:(BOOL)animated
    {
        [super viewDidAppear:animated];
    
        // Compose view height growing tracking
        [self.composeBar addObserver:self forKeyPath:@"frame" options:0 context:nil];
        // iOS 7 keyboard tracking
        [self.composeBar.superview addObserver:self forKeyPath:@"center" options:0 context:nil];
        // iOS 8 keyboard tracking
        [self.composeBar.superview addObserver:self forKeyPath:@"frame" options:0 context:nil];
    }
    
    - (void)viewDidDisappear:(BOOL)animated
    {
        [super viewDidDisappear:animated];
    
        [self.composeBar removeObserver:self forKeyPath:@"frame"];
        [self.composeBar.superview removeObserver:self forKeyPath:@"center"];
        [self.composeBar.superview removeObserver:self forKeyPath:@"frame"];
    }
    
    - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
    {
        if (object == self.composeBar.superview || object == self.composeBar)
        {
            // Get all values
            CGPoint newContentOffset = self.tableView.contentOffset;
            UIEdgeInsets newEdgeInsets = self.tableView.contentInset;
            UIEdgeInsets newScrollIndicartorInsets = self.tableView.scrollIndicatorInsets;
    
            // Update values
            CGFloat bottomInset = self.view.bounds.size.height - [self.composeBar convertPoint:CGPointZero toView:self.view].y;
            CGFloat diff = newEdgeInsets.bottom - (bottomInset + 7);
            newContentOffset.y += diff;
            newEdgeInsets.bottom = bottomInset + 7;
            newScrollIndicartorInsets.bottom = bottomInset;
    
            // Set all values
            if (diff < 0 || diff > 40)
                self.tableView.contentOffset = CGPointMake(0, newContentOffset.y);
            self.tableView.contentInset = newEdgeInsets;
            self.tableView.scrollIndicatorInsets = newEdgeInsets;
        }
    }
    

相关问题