UITextView使用中的那些坑

UITextView使用中的那些坑

本文主要是介绍在使用UITextView的时候遇到的问题, 这也主要是源于前几天项目需要使用UITextView进行文本展示, 首先来说说UITextView的几点好处吧.

1.在面对多行文本展示的时候, 相比UILabel更加灵活, 特别需要动态输入的时候, 高度动态变化的时候, UITextView相比UITextField而言, 使用更加方便, 代理方法也更加丰富, 一般遇到这种情况, 推荐大家使用UITextView.

2.针对文本样式改变的时候, 比如链接, 下划线, 着色等类似的场景的时候, 可能更加便利.

UITextView的使用场景大致介绍一下, 但是我们在使用UITextView的时候也会遇到很多坑爹的地方. 这里就介绍一个比较常见的问题.

UITextView设置文本后无法滚动到顶部

这里主要介绍的是, 由于UITextView高度限制, 可能无法显示全部的文本, 这时候UITextView是可以滚动的, 因为实际上它就是UIScrollView, 那么问题就出现在这里?

我们希望设置完文本之后, UITextView滚动到顶部, 然后可以滑动显示, 这里很自然我们会想到这样解决:

    [self.textView setContentOffset:CGPointZero];

或者

 [self.textView scrollRangeToVisible:NSMakeRange(0, 0)];

其实二者的解决原理是差不多的, 但是结果如何捏? 答案是无效!!!!
我们看到的效果还是滚动到了底部, 那么这到底是为什么捏?

这里可能跟UITextView本身的机制有关, 具体的本人也没有仔细去研究, 其实我们可以跟踪UITextView的contentOffset发现一些问题:

这里我们采用KVO的方式去查看contentOffset的变化过程,

这里我们检测contentOffset的变化:

    [self.textView addObserver:self forKeyPath:@"contentOffset" options:NSKeyValueObservingOptionNew context:nil];
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context{
    NSLog(@"%@",change);
}

从log出来的结果可以发现:

2016-06-04 21:03:04.512 UITextViewTest[1875:306811] {
    kind = 1;
    new = "NSPoint: {0, 0}";
}
2016-06-04 21:03:04.513 UITextViewTest[1875:306811] {
    kind = 1;
    new = "NSPoint: {0, 0}";
}
2016-06-04 21:03:04.514 UITextViewTest[1875:306811] {
    kind = 1;
    new = "NSPoint: {0, 91.666666666666671}";
}

其实UITextView的contentOffset的变化经历了阶段, 至于每个阶段具体的事情, 本人也不是很清楚, 但是有一点可以确定的是, 我们之所以设置contentOffset没有效果, 是因为在我们设置的时候UITextView本身的contentOffset依然是0, 那么什么时候有效果捏?

当然是在contentOffset不为0的时候, 也就是UITextView已经滚动到底部的时候, 这个时候设置才是有效的, 至于具体的原因, 我们可以通过解决方法去探究:

一下是我在stack overflow上找到的解决方法:

I setup a delegate to the textview to monitor the scroll event, and noticed that after my operation to restore the offset, the offset is reset to 0 again. So I instead use the main operation queue to make sure my restore operation happens after the “reset to 0” option.
Here’s my solution that works for iOS 8.0.

CGPoint offset = self.textView.contentOffset;
self.textView.attributedText = replace;
[[NSOperationQueue mainQueue] addOperationWithBlock: ^{
    [self.textView setContentOffset: offset];
}];

这里作者采用了多线程的方式去设定contentOffset, 我们可以猜想可能UITextView的渲染方式是异步的, 这个也很容易理解, 如果UITextView文本内容特别长, 其实渲染是需要花费一定时间的, 异步的方式也不会阻塞主线程, 但是这就给我们带来了一定的麻烦, 因为我们不知道UITextView什么时候渲染完成, 然后去改变它的contentOffset来达到我们想要的效果.

总结

其实从之前的分析中, 我们也可以渐渐发现, 其实一旦UITextView渲染完成, 它的contentOffset不为0, 这个其实可以作为我们判断的标准.

至于解决方法, 以上是一种实现方式, 个人感觉不是很靠谱, 但是使用起来也没什么问题, 觉得不靠谱的原因是因为: 以上是在主队列中加入操作去执行, 可能之前UITextView的渲染也是主队列中的操作, 因为是串行的, 所以有执行顺序, 也没出现什么问题.

但是个人觉得我们解决问题的根本是contentOffset改变的问题, 我们应该在contentOffset不为CGPointZero的时候, 去修改一次contentOffset, 让它滚动到顶部, 下面提供两种解决思路:

1.采用KVO的方式, 去检测contentOffset的变化, 在发现其值不为0之后, 让其滚动到顶部(这个过程只能初始化执行一次, 不然以后滚动的时候contentOffset也会变化)

2.采用定时询问的方式, 可以采用定时器去间隔查询contentOffset的值, 如果一旦发现其值不为0了, 就可以让其滚动到顶部, 然后关闭定时器, 也是初始化的时候执行一次.

以上两种方式本人都测试过, 希望对大家有帮助, 另外希望苹果官方能够解决这个问题, 个人感觉像是UITextView的bug, 因为毕竟iOS不开源,我们无法查看具体的实现流程, 只能根据自己的推断去查找解决方法, 希望官方能够给出完美的解决方法.

参考

stackoverflow上的解决方法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值