自动滚动的 tableView 和高度自适应的 textView

来源:互联网 发布:淘宝买真货退假货犯法 编辑:程序博客网 时间:2024/06/05 09:49

先来看看效果图:



每一行 cell 类似于空间里的一条说说。

从第一幅图中可以看到,当点击某一条说说的评论按钮时,键盘弹出来后 tableView 会自动滚动,直到这条说说正好出现在输入框上方为止。

如果不这么做,这一条说说就会被键盘挡住,这样肯定不行。


能考虑到这一点只是最基础的。你给别人评论的时候,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];}

当键盘的 frame 发生变化时,就会调用我们自定义的 didShowKey:方法。

- (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);    }];}


参数 notific 是 NSNotification 类型的,在这里我们要用到它的两个属性,一个是键盘的 origin.y,也就是离屏幕上方的距离。另一个是键盘弹出时动画的时间 duration。我们要设置评论输入框的动画时间和这个一样,这样看起来才会显得流畅。


从代码中我们可以看到,其实当键盘弹出来时,我们是把 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);}

contentSizeOfText:是自定义的计算指定文字高度的方法。

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];};

主要的部分就是这些,当然还有很多细节部分,完整的代码见我的 github:传送门。别忘了点击右上角的 star 哦~




1 0
原创粉丝点击