UITextView自增高,類似微信輸入框的效果

前言

最近在看以前的代碼的時候秩彤,發(fā)現(xiàn)自增高的實現(xiàn)有點(diǎn)復(fù)雜叔扼。在計算高度的時候有些數(shù)值是自己估摸著實現(xiàn)的,反正代碼看著很不友好漫雷,就想著重構(gòu)一下瓜富。完整測試代碼在文章最下方!

那么要實現(xiàn)UITextView輸入框有兩個要點(diǎn):

  • 占位符
  • 自增高

首先我們來看一下UITextView這個東西降盹,乍一看与柑,它跟UITextFiled好像是一家人,其實他們的父類都不是同一個蓄坏。UITextView是繼承自UIScrollView的仅胞,而UITextField是繼承自UIControl的。

Placeholder

做輸入框最基本的就是placeholder(占位符)剑辫,當(dāng)然也有些輸入框是沒有占位符提示的干旧,比如微信,我們不管它妹蔽,我就是要搞個占位符椎眯。那么問題來了挠将,UITextView是沒有placeholder這個屬性的。這就是最蛋疼的地方编整,你一個輸入框的類舔稀,竟然連placeholder都沒有,就想UITextFiled沒有textFieldDidChange:這個方法一樣??掌测!

好吧内贮,那我們就來手動實現(xiàn)一下placeholder吧。要想添加一個placeholder其實有很多方法汞斧,其中最常用的方法就是給UITextView上加一個UILabel夜郁,然后在textViewDidChange: 方法里面來控制他的顯示和隱藏。那有沒有更方便簡潔粘勒,看起來又比較牛逼的方法呢竞端?有的!

首先庙睡,我們來遍歷一下UITextView這個類里的成員變量事富,用到的是runtime里的入門小知識

var count: UInt32 = 0
let 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);
}

下面是打印出來的結(jié)果

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")

是不是一大堆不知所云的東西,其他的不用管乘陪,只要看其實用###標(biāo)出來的最關(guān)鍵的兩個成員變量_placeholderLabel_containerView统台。先不管_containerView,先來看_placeholderLabel啡邑,這不就是占位符嗎贱勃。不過蘋果沒有把這個暴露出這個屬性給開發(fā)者使用,那么我們怎么使用者個私有的成員變量呢谣拣?當(dāng)然使用KVC了!這個時候發(fā)現(xiàn)KVC是個神器了吧族展!

setValue(placeHolderLabel, forKey: "_placeholderLabel")

這個系統(tǒng)自帶的placeholderLabelUITextFieldplaceholder一樣森缠。你只要自己寫個label賦值給他就可以了,他的顯示和消失有系統(tǒng)控制仪缸!

那么placeholder就搞定了贵涵,非常簡單吧,一個KVC輕松解決恰画。

自增高

那么如何來實現(xiàn)自增高呢宾茂?這個時候大家應(yīng)該已經(jīng)想到了上面打印出來的那個被###出來的兩個成員變量之一_containerView。顧名思義拴还,這個東西就是個內(nèi)容視圖跨晴。你用View UI Hierarchy查看一下就會驚奇的發(fā)現(xiàn),他的frame是根據(jù)文字高度變化的片林,也就是說_containerView是自增高的端盆!那么問題就解決了怀骤,我們用KVC把這個_containerView取出來。

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

然后再用KVO監(jiān)聽containerView

// 注冊監(jiān)聽
containerView.addObserver(self, forKeyPath: "frame", options: .new, context: nil)

// 實現(xiàn)監(jiān)聽
open override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {}

這樣我們就完美的實現(xiàn)了UITextView的自增高焕妙,代碼又少又簡單蒋伦!

Tips

這里有個關(guān)于KVO的小貼士,蘋果在官方文檔里已經(jīng)說明了iOS9.0以后已經(jīng)不需要手動removeObserver:了焚鹊,除了addObserverForName:object:queue:usingBlock:方法痕届,因為這個方法在通知中心注冊的時候還是強(qiáng)引用的,所以要手動移除末患。

為什么iOS9.0以后不需要手動移除Observer了呢研叫?
因為在iOS9.0以前,注冊Observer時阻塑,通知中心對Observerunsafe_unretained引用蓝撇,而iOS9.0以后,通知中心對Observer實現(xiàn)了weak引用陈莽,這兩個引用的區(qū)別在與渤昌,weak在對象釋放掉之后會置nil,而unsafe_unretained在對象釋放掉之后會變成野指針走搁,所以需要在對象釋放掉之前將Observer移除独柑,防止野指針通信,造成Crash私植。

Discussion

這段代碼有一個致命的缺陷是不支持設(shè)置contentInset屬性忌栅,若是設(shè)置了contentInset文字會上下跳,有興趣的同學(xué)可以在Demo里面測試一下曲稼,要是能解決這個問題就再好不過了索绪,謝謝大家啦!
本文Demo僅供交流使用贫悄,切勿直接扔進(jìn)項目里瑞驱!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市窄坦,隨后出現(xiàn)的幾起案子唤反,更是在濱河造成了極大的恐慌,老刑警劉巖鸭津,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件彤侍,死亡現(xiàn)場離奇詭異,居然都是意外死亡逆趋,警方通過查閱死者的電腦和手機(jī)盏阶,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來闻书,“玉大人般哼,你說我怎么就攤上這事吴汪。” “怎么了蒸眠?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵漾橙,是天一觀的道長。 經(jīng)常有香客問我楞卡,道長霜运,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任蒋腮,我火速辦了婚禮淘捡,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘池摧。我一直安慰自己焦除,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布作彤。 她就那樣靜靜地躺著膘魄,像睡著了一般。 火紅的嫁衣襯著肌膚如雪竭讳。 梳的紋絲不亂的頭發(fā)上创葡,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天,我揣著相機(jī)與錄音绢慢,去河邊找鬼灿渴。 笑死,一個胖子當(dāng)著我的面吹牛胰舆,可吹牛的內(nèi)容都是我干的骚露。 我是一名探鬼主播,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼缚窿,長吁一口氣:“原來是場噩夢啊……” “哼棘幸!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起滨攻,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤够话,失蹤者是張志新(化名)和其女友劉穎蓝翰,沒想到半個月后光绕,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡畜份,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片拓提。...
    茶點(diǎn)故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖愕鼓,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情慧起,我是刑警寧澤菇晃,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站蚓挤,受9級特大地震影響磺送,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜灿意,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一估灿、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧缤剧,春花似錦馅袁、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至兄纺,卻和暖如春大溜,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背估脆。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工钦奋, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人疙赠。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓付材,卻偏偏與公主長得像,于是被迫代替她去往敵國和親圃阳。 傳聞我的和親對象是個殘疾皇子厌衔,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評論 2 344

推薦閱讀更多精彩內(nèi)容

  • 看綜藝節(jié)目《二胎時代》的時候富寿,有一個情節(jié)我印象很深刻,潘長江的外孫小石頭锣夹,他用罐子蓋高樓页徐,媽媽問你的樓里都住著誰呀...
    勝藍(lán)閱讀 937評論 6 1
  • 一、卸載軟件 如果知道軟件具體名稱银萍,可以使用下面的命令: 如果不知道軟件具體名稱变勇, 二、清理殘留數(shù)據(jù)
    GinkWang閱讀 509評論 0 0