UITextView自增高,相似微信输入框的效果

作者 : 开心源码 本文共3803个字,预计阅读时间需要10分钟 发布时间: 2022-05-12 共200人阅读

前言

最近在看以前的代码的时候,发现自增高的实现有点复杂。在计算高度的时候有些数值是自己估摸着实现的,反正代码看着很不友好,就想着重构一下。完整测试代码在文章最下方!

那么要实现UITextView输入框有两个要点:

  • 占位符
  • 自增高

首先我们来看一下UITextView这个东西,乍一看,它跟UITextFiled如同是一家人,其实他们的父类都不是同一个。UITextView是继承自UIScrollView的,而UITextField是继承自UIControl的。

Placeholder

做输入框最基本的就是placeholder(占位符),当然也有些输入框是没有占位符提醒的,比方微信,我们不论它,我就是要搞个占位符。那么问题来了,UITextView是没有placeholder这个属性的。这就是最蛋疼的地方,你一个输入框的类,竟然连placeholder都没有,就想UITextFiled没有textFieldDidChange:这个方法一样??!

好吧,那我们就来手动实现一下placeholder吧。要想增加一个placeholder其实有很多方法,其中最常用的方法就是给UITextView上加一个UILabel,而后在textViewDidChange: 方法里面来控制他的显示和隐藏。那有没有更方便简洁,看起来又比较牛逼的方法呢?有的!

首先,我们来遍历一下UITextView这个类里的成员变量,用到的是runtime里的入门小知识

var count: UInt32 = 0let ivars = class_copyIvarList(UITextView.self, &count)for i in 0..<count {    let ivar = ivars![Int(i)];    let name = ivar_getName(ivar);    let objcName = String(utf8String: name!)    print(objcName as Any);}

下面是打印出来的结果

Optional("_private")Optional("_textStorage")Optional("_textContainer")Optional("_layoutManager")### Optional("_containerView") ###Optional("_inputDelegate")Optional("_tokenizer")Optional("_inputController")Optional("_interactionAssistant")Optional("_textInputTraits")Optional("_autoscroll")Optional("_tvFlags")Optional("_contentSizeUpdateSeqNo")Optional("_scrollTarget")Optional("_scrollPositionDontRecordCount")Optional("_scrollPosition")Optional("_offsetFromScrollPosition")Optional("_linkInteractionItem")Optional("_dataDetectorTypes")Optional("_preferredMaxLayoutWidth")### Optional("_placeholderLabel") ###Optional("_inputAccessoryView")Optional("_linkTextAttributes")Optional("_streamingManager")Optional("_characterStreamingManager")Optional("_siriAnimationStyle")Optional("_siriAlignment")Optional("_siriParameters")Optional("_firstBaselineOffsetFromTop")Optional("_lastBaselineOffsetFromBottom")Optional("_intrinsicSizeCache")Optional("_cuiCatalog")Optional("_beforeFreezingTextContainerInset")Optional("_duringFreezingTextContainerInset")Optional("_beforeFreezingFrameSize")Optional("_unfreezingTextContainerSize")Optional("_animatingPaste")Optional("_frameOfTrailingWhitespace")Optional("_textDragDropSupport")Optional("_topContentPadding")Optional("_bottomContentPadding")Optional("_scrollEndDraggingVelocity")Optional("_adjustsFontForContentSizeCategory")Optional("_clearsOnInsertion")Optional("_pasteDelegate")Optional("_multilineContextWidth")Optional("_textDragOptions")Optional("_textDragDelegate")Optional("_textDropDelegate")Optional("_inputView")Optional("_visualStyle")

是不是一大堆不知所云的东西,其余的不用管,只需看其实用###标出来的最关键的两个成员变量_placeholderLabel_containerView。先不论_containerView,先来看_placeholderLabel,这不就是占位符吗。不过苹果没有把这个暴露出这个属性给开发者使用,那么我们怎样使用者个私有的成员变量呢?当然使用KVC了!这个时候发现KVC是个神器了吧!

setValue(placeHolderLabel, forKey: "_placeholderLabel")

这个系统自带的placeholderLabelUITextFieldplaceholder一样。你只需自己写个label赋值给他即可以了,他的显示和消失有系统控制!

那么placeholder就搞定了,非常简单吧,一个KVC轻松处理。

自增高

那么如何来实现自增高呢?这个时候大家应该已经想到了上面打印出来的那个被###出来的两个成员变量之一_containerView。顾名思义,这个东西就是个内容视图。你用View UI Hierarchy查看一下就会惊奇的发现,他的frame是根据文字高度变化的,也就是说_containerView是自增高的!那么问题就处理了,我们用KVC把这个_containerView取出来。

let containerView = setValue(placeHolderLabel, forKey: "_containerView")

而后再用KVO监听containerView

// 注册监听containerView.addObserver(self, forKeyPath: "frame", options: .new, context: nil)// 实现监听open override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {}

这样我们就完美的实现了UITextView的自增高,代码又少又简单!

Tips

这里有个关于KVO的小贴士,苹果在官方文档里已经说明了iOS9.0以后已经不需要手动removeObserver:了,除了addObserverForName:object:queue:usingBlock:方法,由于这个方法在通知中心注册的时候还是强引用的,所以要手动移除。

为什么iOS9.0以后不需要手动移除Observer了呢?
由于在iOS9.0以前,注册Observer时,通知中心对Observerunsafe_unretained引用,而iOS9.0以后,通知中心对Observer实现了weak引用,这两个引用的区别在与,weak在对象释放掉之后会置nil,而unsafe_unretained在对象释放掉之后会变成野指针,所以需要在对象释放掉之前将Observer移除,防止野指针通信,造成Crash

Discussion

这段代码有一个致命的缺陷是不支持设置contentInset属性,若是设置了contentInset文字会上下跳,有兴趣的同学可以在Demo里面测试一下,要是能处理这个问题就再好不过了,谢谢大家啦!
本文Demo仅供交流使用,切勿直接扔进项目里!

说明
1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是摆设,本站源码仅提供给会员学习使用!
7. 如遇到加密压缩包,请使用360解压,如遇到无法解压的请联系管理员
开心源码网 » UITextView自增高,相似微信输入框的效果

发表回复