本文接着上篇文章(传送门)继续来说如何让 tableView 自动滚动到一个合适的位置。
源代码已上传到 github(传送门),欢迎 star。
先来看看效果图:
每一行 cell 类似于空间里的一条说说。
从第一幅图中可以看到,当点击某一条说说的评论按钮时,键盘弹出来后 tableView 会自动滚动,直到这条说说正好出现在输入框上方为止。
如果不这么做,这一条说说就会被键盘挡住,这样肯定不行。
能考虑到这一点只是初级的,中级的是第二幅图。(你问我高级的是啥?我也在思考中)
你给别人评论的时候,textView 肯定不是一开始就很多行吧?(还是那句话,打开朋友圈或者空间自己看看就知道了,事实胜于雄辩啊)
什么?你问我为啥不用 textField?textField 只能有一行,这正是它和 textView 的主要区别之一。如果你不知道这个,那说明你的基础知识还有很多欠缺哦~
言归正传,你给别人评论,如果写了好几行怎么办?
首先 textView 要高度自适应,能想到这一点只是初级的。中级的是,textView 变高了,那岂不是又会挡住这条说说了吗?所以此时还要让 tableView 自动滚动。(看第二幅图)
上面这些都是纸上谈兵。说了那么多,有的人会问:“tableView 到底怎么滚动?”
简单来说就一句话:
[self.myTableView selectRowAtIndexPath:indexPath animated:YES scrollPosition:UITableViewScrollPositionBottom];
- (void)selectRowAtIndexPath:(nullable NSIndexPath *)indexPath animated:(BOOL)animated scrollPosition:(UITableViewScrollPosition)scrollPosition;
它有三个参数,indexPath 就是你想滚动的那个cell,animated 决定是否有动画效果,scrollPosition 是一个枚举:
typedef NS_ENUM(NSInteger, UITableViewScrollPosition) { UITableViewScrollPositionNone, UITableViewScrollPositionTop, UITableViewScrollPositionMiddle, UITableViewScrollPositionBottom };
注意,它不是滚动的方向,而是滚动结束后这个 cell 相对于 tableVIew 的位置。比如说设置为 UITableViewScrollPoistionTop,那么滚动结束后这个 cell 就会处于屏幕最上面。如果设置为 UITableViewScrollViewPositionNone,那么 tableView 就不会滚动。显然我们需要设置为 UITableViewScrollPositionBottom。
我们先说简单的,假设评论永远只有一行,此时我们只需要考虑键盘弹出来以后让 tableVIew 滚动就行了。
那要怎么做呢?监听键盘。
- (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didShowKey:) name:UIKeyboardDidChangeFrameNotification object:nil]; }
- (void)didShowKey:(NSNotification *)notific { NSDictionary *userInfo = notific.userInfo; // Get the origin of the keyboard. CGRect rect = [[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue]; CGFloat h = 0.0f; if (fabs(rect.origin.y - ScreenHeight) < 1e-3) { self.tableView.frame = CGRectMake(0, 0, ScreenWidth, ScreenHeight); } else { h = fmax(self.commentInputView.frame.size.height, 60.0f); self.tableView.frame = CGRectMake(0, 0, ScreenWidth, rect.origin.y - h + 44); } [self.tableView selectRowAtIndexPath:self.selectedIndexPath animated:YES scrollPosition:UITableViewScrollPositionBottom]; // Get the duration of the animation. NSTimeInterval duration = [[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue]; [UIView animateWithDuration:duration animations:^{ self.commentInputView.frame = CGRectMake(0, rect.origin.y - h, ScreenWidth, h); }]; }
从代码中我们可以看到,其实当键盘弹出来时,我们是把 tableView 的高度变小了,然后让对应的 cell 滚动到 tableView 的下面。当收回键盘时,再把 tableView 的高度变回为屏幕的高度即可。
那么如果评论有多行怎么办?
我们需要实现 UITextViewDelegate 中的两个方法:
1. - (void)textViewDidChange:(UITextView *)textView;
2. - (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text;
在这两个方法中动态计算 textView 中的文字高度(具体计算方法见我的另一篇文章:传送门),然后通过代理或者block,把高度返回给 controller,在 controller 中再次修改 tableView 的高度即可。
举个栗子��吧:
- (void)textViewDidChange:(UITextView *)textView { CGFloat height = [self contentSizeOfText:textView.text]; _offsetY = self.frame.origin.y; if (height != self.frame.size.height) { _offsetY = _offsetY - (height - self.frame.size.height); self.frame = CGRectMake(0, _offsetY, [UIScreen mainScreen].bounds.size.width, height); if (self.changeFrame) { self.changeFrame(_offsetY); } } textView.frame = CGRectMake(0, 0, self.frame.size.width, height); }
if (height != self.frame.size.height) 就是说,如果 textView 的高度发生了改变,那么我就需要把新的高度传给 controller。
在这里我们就举个 block 的栗子吧,changeFrame 就是我们自定义的 block,它有一个参数,这个参数就是我们需要传给 controller 的 textView 的高度。然后在 controller 中实现这个 block 即可:
__weak typeof(self) weakSelf = self; _commentInputView.inputViewFrameChanged = ^(CGRect aFrame) { CGRect frame = weakSelf.tableView.frame; frame.size.height = aFrame.origin.y + 44; weakSelf.tableView.frame = frame; [weakSelf.tableView scrollToRowAtIndexPath:weakSelf.selectedIndexPath atScrollPosition:UITableViewScrollPositionBottom animated:YES]; };