發(fā)布模塊
##
- 點(diǎn)擊發(fā)布按鈕,modal彈出一個(gè)控制器,有發(fā)布微博、選擇相冊(cè)照片、表情鍵盤(pán)功能
- 1.發(fā)布按鈕屬于單獨(dú)的模塊,新建一個(gè)文件來(lái)管理,創(chuàng)建一個(gè)ComposeViewController,繼承自UIViewController,由于界面是固定的,使用XIB描述
- 2.點(diǎn)擊發(fā)布按鈕時(shí),創(chuàng)建ComposeViewController對(duì)象,并包裝為導(dǎo)航控制器,然后modal彈出
- 3.給導(dǎo)航欄添加【關(guān)閉】和【發(fā)布】按鈕,默認(rèn)情況下用戶沒(méi)有輸入文字時(shí)【發(fā)布】按鈕不能點(diǎn)擊‘
- 4.導(dǎo)航標(biāo)題上有兩個(gè)Label,上面顯示【發(fā)微博】,下面顯示【開(kāi)發(fā)者的昵稱】,由于導(dǎo)航的title只能顯示一個(gè)Label,所以這里需要自定UIView來(lái)添加兩個(gè)按鈕
- 5.創(chuàng)建ComposeTitleView,繼承UIView,重寫(xiě)initWithFrame,懶加載這兩個(gè)Lable,將這兩個(gè)控件添加到ComposeTitleView中,這里不要設(shè)置Frame,因?yàn)檫@兩個(gè)Label還要有間距,如果直接設(shè)置Frame,下面一個(gè)控件的Y值就不好計(jì)算了,所以可以使用約束,導(dǎo)入SnapKit框架,接下來(lái)將控件的屬性也封裝在里面
- 6.在ComposeViewController中懶加載ComposeTitleView
##
- 7.使用TextView作為輸入框,但是TextView在沒(méi)有輸入文字時(shí)并沒(méi)有【占位文字】,需要自定義TextView,往里面添加一個(gè)Label子控件作為占位文字
- 8.在XIB上添加TextView,設(shè)置約束距離控制器View上下左右為0
- 9.創(chuàng)建一個(gè)繼承自UITextView的類ComposeTextView,然XIB上的TextView控件的Class成為ComposeTextView
- 10.在awakeFromNib或initWithCoder中初始化,當(dāng)一個(gè)控件從XIB中加載時(shí),會(huì)先執(zhí)行initWithCoder,再執(zhí)行awakeFromNib方法,用哪個(gè)都可以,但是如果是添加子控件用initWithCoder方法,如果是對(duì)子控件進(jìn)行初始化操作用awakeFromNib方法
- 11.懶加載placeHolderLabel控件,將其添加到ComposeTextView中,設(shè)置其約束及屬性及文字
- 12.設(shè)置TextView的文本內(nèi)容的內(nèi)邊距屬性textContainerInset,使用決定文字輸入后的文字在什么位置,
- 13.在控制器的ViewDidAppear顯示完成的時(shí)候讓TextView成為第一響應(yīng)者彈出鍵盤(pán)
- 14.開(kāi)始輸入文字時(shí),占位Label隱藏,可以通過(guò)代理或通知,這里我們使用代理,讓控制器成為T(mén)extView的代理,判斷當(dāng)TextView中有文字就隱藏,沒(méi)有文字就顯示,可以使用textView的hasText方法判斷有沒(méi)有文字,而且此時(shí)還要判斷是否有文字hasText來(lái)決定導(dǎo)航條發(fā)布按鈕是否可以點(diǎn)擊
- 15.滾動(dòng)textView時(shí),讓鍵盤(pán)隱藏,但此時(shí)textView并不能滾動(dòng),原因:雖然textView繼承自UIScrollView,由于當(dāng)前textView的contentSize內(nèi)容不夠所以不能滾動(dòng),但是我們也可以在不設(shè)置contentSize的情況下讓textView滾動(dòng),有兩個(gè)屬性:開(kāi)啟【Bounce horizontally】可以左右滾動(dòng),開(kāi)啟【Bounce vertically】可以上下滾動(dòng),這兩個(gè)屬性都是UIScrollView的
- 16.調(diào)用scrollView的滾動(dòng)代理方法讓textView失去第一響應(yīng)者即可
##
- 17.實(shí)現(xiàn)底部工具欄:
-(1)在XIB中在TextView底部拖一個(gè)UIToolBar,刪除ToolBar中的item,拖UIButton進(jìn)去,因?yàn)門(mén)oolBar自帶的item沒(méi)有點(diǎn)擊時(shí)高亮狀態(tài),設(shè)置約束,距離底、右、左為0,ToolBar高度默認(rèn)為44
-(2)在ToolBar中添加幾個(gè)需要的Button,但是我們需要Button之間有間距,如果使用彈簧的話兩邊距離控制器view間距會(huì)變得很大,如果不希望這兩邊間距很大,可以使用小技巧:往view兩邊與按鈕之間各拖入一個(gè)item,刪除item的文字,給item輸入幾個(gè)固定的空格即可
-(3)當(dāng)鍵盤(pán)彈出時(shí),讓toolBar一起上移:監(jiān)聽(tīng)鍵盤(pán)即將改變frame的通知
##
- 18.選中照片的布局
-(1)ToolBar上第一個(gè)按鈕點(diǎn)擊可以選照片
點(diǎn)擊選中照片時(shí):第一先退出鍵盤(pán),第二彈出一個(gè)界面可以選擇照片,這個(gè)界面并沒(méi)有將工具欄隱藏及蓋上,反而工具欄在最底部,界面上有一個(gè)?按鈕,點(diǎn)擊后彈出相冊(cè)可以選照片
-(2)在XIB上面拖一個(gè)CollectionView,讓它在ToolBar的后面(不要蓋住ToolBar),默認(rèn)情況下設(shè)置約束為左、右、底部(view)、高度為0,目的是默認(rèn)不顯示出來(lái),當(dāng)點(diǎn)擊ToolBar上的選照片按鈕時(shí)改變CollectionView高度約束讓它顯示(最好讓高度約束為屏幕高度的比例0.65)
-(3)給CollectionView設(shè)置數(shù)據(jù)源,如果讓發(fā)布控制器成為其數(shù)據(jù)源,那發(fā)布控制器的代碼會(huì)很多,所以我們可以自定義UICollectionView,讓它來(lái)管理XIB的CollectionView,并讓它自己成為自己的代理
-(4)注冊(cè)cell,XIB中CollectionView上沒(méi)有cell,storyboard中CollectionView有cell,所以只能通過(guò)代碼注冊(cè)cell類型
-(5)給collectionView設(shè)置布局,取出collectionView的布局轉(zhuǎn)為流水布局,設(shè)置一共有3列item,每個(gè)item的間距為10,計(jì)算item的寬度和高度為(屏幕的寬度 - 4個(gè)間距) / 3,這里有個(gè)問(wèn)題:item之間的間距太多,但是設(shè)置最小間距不好解決,可以設(shè)置collectionView的contentInset解決,讓邊上的內(nèi)容往里面擠
-(6)自定義cell類并使用XIB搭建,并讓collectionView的cell注冊(cè)為這個(gè)XIB,往XIB上添加兩個(gè)按鈕,一個(gè)【?】按鈕,一個(gè)【X】按鈕,默認(rèn)【X】按鈕隱藏,
-(7)點(diǎn)擊【?】按鈕時(shí)彈出照片選擇器:需要監(jiān)聽(tīng)【?】按鈕的點(diǎn)擊事件,但是由于cell并不是控制器無(wú)法彈出照片瀏覽控制器,而cell的父控件是collectionView也不是控制器,但collectionView的父控件是發(fā)布控制器,這里可以使用代理、閉包、通知,其中一種方式讓發(fā)布控制器彈出照片瀏覽控制器,用什么方法會(huì)好些呢:由于這里涉及到多層傳遞,多層之間傳遞使用通知最好
-(8)點(diǎn)擊【?】按鈕時(shí)發(fā)布通知,在發(fā)布控制器中注冊(cè)通知,當(dāng)發(fā)布控制器接收到通知后彈出選擇照片控制器
-(9)顯示選中的照片:在發(fā)布控制器中定義一個(gè)UIImage類型的懶加載數(shù)組images變量,當(dāng)選擇照片時(shí),將選中的照片添加到這個(gè)數(shù)組中,在collectionView中定義一個(gè)images變量,然后將這個(gè)數(shù)組賦值給collectionView的images,讓collectionView去展示數(shù)據(jù),監(jiān)聽(tīng)collectionView的images發(fā)送改變,只要改變就刷新數(shù)據(jù)
-(10)在cell中定義一個(gè)image變量,監(jiān)聽(tīng)image發(fā)生改變,只要image發(fā)生改變就將image設(shè)置為?按鈕的背景,在collectionView中給cell的image傳值
-(11)存在問(wèn)題:collectionView上選擇后的照片被壓扁了,這里不能通過(guò)改變?按鈕的contentModl來(lái)解決,因?yàn)楫?dāng)前是把照片設(shè)置在?按鈕的背景上了,并不是按鈕內(nèi)部的圖片上
解決步驟:
(1)在XIB中拖一個(gè)imageView到加號(hào)按鈕上面(不是子控件哦),且在刪除按鈕后面,不能蓋住刪除按鈕,設(shè)置這個(gè)imageView的contentModl為填充Fill模式,并將其拖線到cell中,然后給其設(shè)置圖片即可
(2)在監(jiān)聽(tīng)cell的image屬性發(fā)送改變時(shí),當(dāng)有圖片就把圖片設(shè)置給這個(gè)imageView,不要設(shè)置給?按鈕了,防止循環(huán)利用,沒(méi)有圖片時(shí)設(shè)置imageView的image為nil
(3)當(dāng)有時(shí)明明把刪除按鈕放在了imageView的上面,但是還是不顯示刪除按鈕時(shí),可以嘗試在XIB中拖動(dòng)下imageView的順序,在拖回去,這一般是小bug
##
- 19.刪除選中照片
-(1)刪除選中照片的按鈕默認(rèn)設(shè)置為隱藏,并在cell的image監(jiān)聽(tīng)器中根據(jù)只要image不為nil就讓刪除按鈕顯示,image為nil時(shí)隱藏
-(2)將刪除按鈕拖線到cell中,點(diǎn)擊刪除按鈕時(shí)刪除這張照片,刪除照片應(yīng)該從發(fā)布控制器的images數(shù)組中刪除,刪除后刷新表格
-(3)由于刪除按鈕的點(diǎn)擊事件是在cell類中,要想點(diǎn)擊了cell上按鈕讓控制器去做事情時(shí),可以使用通知,這里就發(fā)布一個(gè)點(diǎn)擊了刪除按鈕的通知,然后在發(fā)布控制器中監(jiān)聽(tīng)這個(gè)通知,接收到通知后將當(dāng)前點(diǎn)擊刪除按鈕上的image從images數(shù)組中刪除,然后刷新表格
-(4)問(wèn)題:點(diǎn)擊刪除按鈕要?jiǎng)h除對(duì)應(yīng)的那個(gè)照片,但是那個(gè)照片在數(shù)組中的索引我們并不知道
解決方法:
(1)在發(fā)布通知時(shí),將點(diǎn)擊的imageView.image通過(guò)object參數(shù)傳出去,
(2)在接收到通知的方法中,通過(guò)object參數(shù)獲取這個(gè)image,當(dāng)然也可以通過(guò)發(fā)布通知的userInfo參數(shù)傳出來(lái),但是userInfo一般適合傳多個(gè)參數(shù)的,所有使用object參數(shù)
(3)通過(guò)object參數(shù)獲取的是AnyObject的可選對(duì)象,需要使用guard做校驗(yàn),獲取不到就return,校驗(yàn)的同時(shí)并轉(zhuǎn)為UIImage對(duì)象
(4)通過(guò)images數(shù)組調(diào)用indexOf方法將這個(gè)image傳進(jìn)去就可以獲取這個(gè)image在images數(shù)組中的下標(biāo)值(索引),獲取的是可選類型,需要使用guard做校驗(yàn),獲取不到就return
-(5)通過(guò)獲取的下標(biāo)值將其從images數(shù)組中remove掉
-(6)重新把移除后的images數(shù)組賦值給collectionView的images
-(7)其實(shí)重新賦值完成后,collectionView的images值就會(huì)發(fā)生變化,此時(shí)它的監(jiān)聽(tīng)器監(jiān)聽(tīng)到發(fā)生改變后就會(huì)調(diào)用reloadData刷新表格,就會(huì)更新界面
##
- 20.點(diǎn)擊toolBar上的表情按鈕時(shí),切換鍵盤(pán)為表情鍵盤(pán),再點(diǎn)擊表情按鈕時(shí),鍵盤(pán)再切換回去,【切換鍵盤(pán)要通過(guò)UITextView或UITextField的[inputView]屬性設(shè)置,當(dāng)設(shè)置為nil時(shí)就是默認(rèn)的普通鍵盤(pán)哦】
-(1)監(jiān)聽(tīng)toolBar上表情按鈕的點(diǎn)擊,拖線到發(fā)布控制器中
-(2)當(dāng)點(diǎn)擊表情按鈕時(shí):(1.先退出鍵盤(pán) 2.切換鍵盤(pán),3.彈出鍵盤(pán))
-(3)注意:要想切換鍵盤(pán),必須要先將鍵盤(pán)退出,才能切換,切換以后再?gòu)棾銮袚Q好的鍵盤(pán)
-(4)切換鍵盤(pán)可以通過(guò)判斷textView的inputView是否為nil,當(dāng)為nil時(shí)就設(shè)置為自定義的鍵盤(pán),不為nil時(shí)就讓其為nil(nil時(shí)鍵盤(pán)為默認(rèn)的普通鍵盤(pán)),所以可以使用三目運(yùn)算符做判斷
##
- 21.自定義表情鍵盤(pán)Emoticon
建議:由于表情鍵盤(pán)做起來(lái)比較復(fù)雜,可以單獨(dú)創(chuàng)建一個(gè)項(xiàng)目,并與當(dāng)前項(xiàng)目界面差不多,然后在新的項(xiàng)目中封裝好以后,再拖到這個(gè)項(xiàng)目中使用即可
-(1)由于表情鍵盤(pán)上的業(yè)務(wù)邏輯較為復(fù)雜,需要自定義一個(gè)控制器來(lái)管理鍵盤(pán)表情,讓inputView成為控制器的view,并且專門(mén)給器創(chuàng)建一個(gè)文件夾
-(2)自定義一個(gè)EmoticonViewController類,繼承自UIViewController,如果想要把某個(gè)單獨(dú)功能封裝起來(lái)方便其他項(xiàng)目再次使用的話,最好不要使用XIB

Snip20161002_16.png
-(3)Emoticon鍵盤(pán)分為兩個(gè)部分:上面為表情,下面是工具欄(選擇表情類型的),上面的部分可以使用collectionView,因?yàn)槊總€(gè)表情就可以是一個(gè)cell,下面的控件用ToolBar
-(4)在EmoticonViewController控制器中,添加這兩個(gè)控件,先進(jìn)行懶加載,注意collectionView需要布局,懶加載時(shí)給其傳一個(gè)布局,由于frame不確定,所以可以使用CGRectZero
-(5)在viewDidLoad方法中添加兩個(gè)子控件,通過(guò)代碼給子控件設(shè)置約束,封裝功能或框架最后不要使用第三方框架,不然產(chǎn)生依賴,不方便拿到其他項(xiàng)目中復(fù)用
-(6)注冊(cè)cell和設(shè)置collectionView的數(shù)據(jù)源為當(dāng)前控制器
-(7)設(shè)置collectionView的布局,直接在當(dāng)前控制器中,自定義EmoticonCollectionViewLayout,繼承自UICollectionViewFlowlayout
-(1)重寫(xiě)prepareLayout方法(準(zhǔn)備布局時(shí)調(diào)用),在這個(gè)方法中#1先計(jì)算每個(gè)item的寬高,由于當(dāng)前需求每個(gè)item之間沒(méi)有間距,且每列有7個(gè)item,所有寬高等于屏幕的寬度除以7即可,然后設(shè)置為itemSzie,最小間距為0。#2設(shè)置collectionView的相關(guān)屬性
此時(shí)有個(gè)問(wèn)題:每行item之間有間隔,雖然設(shè)置了最小間距為0,但是大于0也是成立的,所以產(chǎn)生間隔正常,如果不要這個(gè)分割,可以設(shè)置collectionView的contentInset,讓它的頂部和底部有這個(gè)間距內(nèi)邊距,間隔高度的的計(jì)算方法:collectionView的高度除以3(共3行)行item的高度,再除以2即可
注意:如果使用自定義的布局,在創(chuàng)建的collectionView時(shí)也要使用這個(gè)自定義的布局類來(lái)創(chuàng)建,不然等于白做
##
- 22.表情鍵盤(pán)上的toolBar
-(1)先定義一個(gè)標(biāo)題數(shù)組專門(mén)存放toolBar上的標(biāo)題字符串
-(2)遍歷標(biāo)題數(shù)組,取出每一個(gè)標(biāo)題,遍歷時(shí)創(chuàng)建UIBarButtonItem添加到toolBar上,并把每個(gè)標(biāo)題設(shè)置為UIBarButtonItem的title,綁定每個(gè)UIBarButtonItem的tag為遍歷的索引
注意:(1)如果將創(chuàng)建的UIBarButtonItem添加到toolBar的items屬性中不能顯示toolBar上的按鈕時(shí),可以將創(chuàng)建的UIBarButtonItem先添加到一個(gè)臨時(shí)[UIBarButtonItem]類型數(shù)組,再將這個(gè)臨時(shí)數(shù)組賦值給toolBar的items屬性
-(3)創(chuàng)建UIBarButtonItem時(shí),每添加一個(gè)彈簧,但是最后一個(gè)不需要彈簧,可以在創(chuàng)建好以后,從toolBar的items數(shù)組中刪除最后一個(gè)
##
- 23.加載表情數(shù)據(jù)
-(1)emoticon表情其實(shí)就是字符串,一般存儲(chǔ)在一個(gè)plist文件中,對(duì)應(yīng)的code就是emotion的字符串,但是需要轉(zhuǎn)換 ,浪小花和默認(rèn)表情是png圖片
-(2)表情分為:默認(rèn)表情、emoticon表情、浪小花表情、最近表情,所以創(chuàng)建模型時(shí)要有對(duì)應(yīng)的組,
-(3)創(chuàng)建3個(gè)模型:第1個(gè)是EmoticonManager模型用來(lái)管理所有的組的(4個(gè)組),第2個(gè)是EmoticonPackage組模型,組里面放的是每一個(gè)的表情模型,第3個(gè)是Emoticon表情的模型,每一個(gè)表情也是一個(gè)模型
##
- 24.自定義cell展示表情
##
- 25.每20個(gè)表情后面添加一個(gè)刪除按鈕:
-(1)修改模型:在EmoticonPackage添加模型的代碼中,判斷當(dāng)索引為20時(shí),添加一個(gè)刪除表情按鈕模型,并且把索引重置為0
##
- 26.如果是第一中類型表情一共107個(gè),顯示5頁(yè),每頁(yè)21個(gè),這樣會(huì)空兩個(gè)出來(lái)沒(méi)有表情
解決方法:添加空白表情,拿到所有表情的個(gè)數(shù) 抹去% 21,當(dāng)%后的值沒(méi)有的話,就沒(méi)有空白,不用添加,當(dāng)%后有值,就添加21-抹去后的值的個(gè)數(shù)
注意:最近分組也需要添加空白表情
##
- 27.當(dāng)選中一個(gè)表情時(shí)不光有插入到文本,還有放到最近表情里面
-(1)監(jiān)聽(tīng)cell的點(diǎn)擊:設(shè)置cell的代理方法,
-(2)取出點(diǎn)擊表情模型
-(3)將點(diǎn)擊的表情模型插入到最近分組中,但是需要判斷如果是空白表情或刪除按鈕,只要有其中一個(gè)都不需要插入
-(4)將點(diǎn)擊的模型表情插入到最新分組的第0個(gè)位置,但是在插入之前要?jiǎng)h除一個(gè)表情,判斷最新分組中有沒(méi)有包含點(diǎn)擊的那個(gè)表情,如果有包含表示原來(lái)有這個(gè)表情,當(dāng)沒(méi)有包含原來(lái)的表情時(shí),就remove第19個(gè)(因?yàn)樗饕龔?開(kāi)始,第20個(gè)是刪除按鈕,所以刪除倒數(shù)第二個(gè)),如果包含原來(lái)的表情,就刪除自己
##
- 28.點(diǎn)擊emoticon表情時(shí),將表情插入到文本輸入框中
這里說(shuō)的外面是外界調(diào)用我封裝好的這個(gè)emoticon的控制器,內(nèi)部指的就是emoticonViewController
-(1)分析:插入的輸入框一般在最外界的控制器里面,需要在當(dāng)我點(diǎn)擊這個(gè)表情時(shí),將表情回調(diào)到外面的控制器中
-(2)采取閉包的方式回調(diào):在外面封裝一個(gè)閉包(閉包就是一個(gè){代碼塊},去內(nèi)部拿到閉包的引用,之后通過(guò)這個(gè)閉包的引用來(lái)調(diào)用閉包、執(zhí)行閉包
-(3)點(diǎn)擊某個(gè)表情時(shí)回調(diào)到外面:在創(chuàng)建emoticonViewController時(shí),就傳進(jìn)去一個(gè)閉包,可以重寫(xiě)emoticonViewController構(gòu)造函數(shù),在參數(shù)中添加一個(gè)閉包,因?yàn)橛锌赡苓€有在其他地方使用這個(gè)閉包可以emoticonViewController中定義一個(gè)屬性保存這個(gè)閉包,由于需要將emoticon模型傳到外界,所以閉包中添加一個(gè)emoticon模型參數(shù),注意:自定義控制器的構(gòu)造函數(shù),必須調(diào)用super設(shè)計(jì)好的構(gòu)造方法super.init(inbName:nil bundle:nil)
-(4)在collectionView的代理方法中當(dāng)點(diǎn)擊了表情時(shí),調(diào)用閉包并傳入點(diǎn)擊的表情模型,就完成回調(diào)閉包
-(5)當(dāng)外界需要拿到emoticonViewController的view設(shè)置為鍵盤(pán)時(shí),需要先通過(guò)剛剛的自定義構(gòu)造函數(shù)創(chuàng)建,通過(guò)里面的閉包的參數(shù)就可以拿到內(nèi)部傳遞出去的表情
-(6)注意循環(huán)引用:由于在外界要使用這個(gè)閉包回調(diào)的表情模型參數(shù)設(shè)置給當(dāng)前控制器self.textView控件,此時(shí)外界就對(duì)閉包產(chǎn)生強(qiáng)引用,那內(nèi)部emoticonViewController也對(duì)閉包產(chǎn)生強(qiáng)引用,就會(huì)導(dǎo)致循環(huán)引用,所以在外界要使用weak self來(lái)修飾
-(7)emoji表情不需要做圖文混排,因?yàn)楸旧砭褪亲址?,普通表情需要圖文混排
-(8)插入表情到文本輸入框光標(biāo)所在的位置
-1.先判斷是不是空白表情,如果是就return
-2.判斷是不是刪除按鈕,如果是先刪除光標(biāo)前面的文字:textView.delegateBackward,然后return
-3.判斷是不是emoji表情,如果是就獲取光標(biāo)所在的位置,再把光標(biāo)所在的位置替換為emoji表情,然后return
注意:外界使用時(shí)一定要先取出emoticonViewController的view,再給textView設(shè)置inputView為它的view,不然不會(huì)顯示
##
- 29.圖文混排實(shí)現(xiàn)
創(chuàng)建圖文混排的類是NSTextAttachment,屬性字符串的類NSAttributedString,通過(guò)創(chuàng)建一個(gè)屬性字符串時(shí),將NSTextAttachment類的images傳進(jìn)一個(gè)屬性字符串中,由于屬性字符串之間不能相加拼接,還需要?jiǎng)?chuàng)建NSMutableAttributedString可變的屬性字符串,然后將圖文混排的屬性字符串與普通的屬性字符串之間拼接,最后將拼接后的屬性字符串賦值給控件的attributedText屬性
##
- 30.通過(guò)圖文混排將普通表情添加到文本輸入框中
-(1).根據(jù)表情圖片的全路徑pngPath創(chuàng)建圖文混排的屬性字符串
-(2).如果直接將創(chuàng)建好的圖片屬性字符串直接賦值給textView的attributedText屬性,就會(huì)將teextView上所有的文字全部替換掉了,我們想要的效果是,將圖片屬性字符串替換到光標(biāo)所在的位置,所以我們需要先拿到textView的attributedText屬性,就等于拿到了textView上的所有文本,然后將其轉(zhuǎn)為可變的屬性字符串,如果表情太大可以將textView的font取出,將字體的lineHeight設(shè)置為圖片混排的bounds屬性即可
-存在的問(wèn)題1:當(dāng)輸入表情后,文字會(huì)跟隨表情的大小而改變字體大小
-解決方法:設(shè)置表情的大小時(shí),先取出textView的字體用常量保存,設(shè)置完成后,再將取出的這個(gè)字體賦值回去給textView的font即可
-存在的問(wèn)題2:選擇某個(gè)表情替換到textView的指定位置后,光標(biāo)跑到文本的最后面了
-解決方法:將光標(biāo)設(shè)置回原來(lái)的位置+1,就是插入的表情后面,設(shè)置textView的selectedRange屬性
##
- 31.獲取表情文字字符串
-(1)當(dāng)點(diǎn)擊發(fā)布按鈕時(shí),會(huì)將編輯好的文本發(fā)送到服務(wù)器,假如文本中有表情的話,是不能圖片屬性字符串將其發(fā)送到服務(wù)器的,應(yīng)該將表情替換為對(duì)應(yīng)文字
-(2)當(dāng)點(diǎn)擊發(fā)布按鈕時(shí),獲取textView的屬性字符串,在遍歷屬性字符串
##
- 32.封裝插入和獲取表情的方法:
插入表情是往textView中插入,獲取表情是從textView中獲取,由于都是和textView有關(guān)系,可以先給textView擴(kuò)充Extension,再給textView擴(kuò)充兩個(gè)方法:給textView插入表情方法和獲取textView屬性字符串對(duì)應(yīng)的表情字符串方法
##
- 33.項(xiàng)目集成表情鍵盤(pán)
將單獨(dú)封裝的emoticon拿到項(xiàng)目中使用即可
- 問(wèn)題1: 插入表情時(shí),占位文字需要隱藏掉
- 解決方法: 這是因?yàn)椴迦氡砬闀r(shí),并沒(méi)有觸發(fā)textView的代理方法,可以在插入表情時(shí),主動(dòng)調(diào)用textView的代理方法
##
- 34.點(diǎn)擊發(fā)布按鈕時(shí),發(fā)布一條文字微博
- (1)到新浪開(kāi)發(fā)者網(wǎng)站找到微博接口,里面有寫(xiě)入接口,找到發(fā)布一條微博
- (2)在網(wǎng)絡(luò)工具類中,寫(xiě)一個(gè)發(fā)微博的方法
##
- 35.點(diǎn)擊發(fā)布按鈕時(shí),發(fā)布一條圖片文字微博
-(1)如果點(diǎn)擊發(fā)布按鈕發(fā)送的是圖片微博,需要更換接口為上傳拖并發(fā)布一條微博,這個(gè)接口只能發(fā)一張圖片,因?yàn)楫?dāng)前項(xiàng)目使用的是授權(quán)方式,而不是官方api接口
-(2)在網(wǎng)絡(luò)工具類中,寫(xiě)一個(gè)發(fā)送微博并且攜帶圖片的方法
##
- 36.正則表達(dá)式
-(1)創(chuàng)建正則表達(dá)式規(guī)則
-(2)創(chuàng)建正則表達(dá)式對(duì)象,(一般使用regex常量名),注意:創(chuàng)建時(shí)方法后面有throws,是拋出異常,需要使用try? 、try!、try處理異常,一般使用try?,返回的是可選類型需要進(jìn)行校
-(3)匹配字符串內(nèi)容,想整個(gè)字符串都進(jìn)行匹配,可以從0的位置loction開(kāi)始匹配,到這個(gè)字符串長(zhǎng)度lenght,switft中要想拿到字符串的,可以通過(guò)字符串的characters.count獲取,匹配的結(jié)果會(huì)在數(shù)組中返回
-(4)遍歷匹配結(jié)果數(shù)組,獲取結(jié)果
swift中如果字符中寫(xiě)\會(huì)報(bào)錯(cuò),可以在\后面在加一個(gè)\進(jìn)行轉(zhuǎn)譯就可,也就是兩個(gè)\\
##
微博表情顯示
-(1)分析:微博正文一般會(huì)有表情需要顯示,但是客戶端發(fā)送給服務(wù)器的一般是類似[哈哈]文字的,其實(shí)[哈哈]在表情info.plist中是chs,我們可以通過(guò)chs獲取表情的pngPath圖片路徑,通過(guò)圖片路徑創(chuàng)建UIImage對(duì)象,在通過(guò)UIImage對(duì)象創(chuàng)建NSTextAttachment,再根據(jù)NSTextAttachment創(chuàng)建屬性字符串,最終顯示的是屬性字符串
使用圖片瀏覽器展示微博圖片
## 一、點(diǎn)擊圖片時(shí),將圖片所在的控件cell的數(shù)據(jù)傳遞給圖片瀏覽器
-(1)需求:點(diǎn)擊微博上的圖片時(shí),進(jìn)入圖片瀏覽器,圖片可以滾動(dòng)查看下一張,當(dāng)從第一張圖片開(kāi)始展開(kāi),瀏覽到其他位置圖片時(shí),點(diǎn)擊關(guān)閉按鈕,圖片會(huì)以動(dòng)畫(huà)顯示到點(diǎn)擊關(guān)閉的那個(gè)圖片
-(2)圖片是在collectionViewCell上的,所以需要監(jiān)聽(tīng)cell的點(diǎn)擊,設(shè)置cell的代理,由于cell是UIView不能彈出控制器,所以當(dāng)cell發(fā)生點(diǎn)擊時(shí)通知homeViewController來(lái)彈出控制器(原則:內(nèi)部子控件發(fā)生點(diǎn)擊通知外界來(lái)做事情)
-(3)使用通知的方式通知外界:由于cell的父控件是collectionView,collectionView的父控件是tableViewCell,tableViewCell的父控件是tableView,tableView的父控件才是homeViewController,層級(jí)關(guān)系比較多,所以最好使用通知
-(4)點(diǎn)擊cell時(shí)發(fā)布通知,并傳遞參數(shù)
-(5)獲取通知需要傳遞的參數(shù):1.點(diǎn)擊圖片是第幾個(gè):index,2. 所有圖片的url:picURLs,當(dāng)數(shù)據(jù)比較多放大通知的userInfo參數(shù)里,當(dāng)數(shù)據(jù)只有一個(gè)對(duì)象時(shí)就放到通知的object參數(shù),注意:通知名和需要傳遞的參數(shù)的key要寫(xiě)成常量,這是開(kāi)發(fā)規(guī)則
-(6)在HomeViewController中監(jiān)聽(tīng)通知
-(7)自定義photoBrowserViewController(圖片瀏覽器),繼承自UIViewController
-(8) homeViewController中接收cell發(fā)來(lái)的通知時(shí),彈出photoBrowserViewController
-(9)在監(jiān)聽(tīng)通知方法中需要通過(guò)key取出cell傳遞的數(shù)據(jù),然后傳遞給photoBrowserViewController
-(10)#在photoBrowserViewController類中自定義init構(gòu)造函數(shù),并添加兩個(gè)參數(shù):picURLs和indexPath,這樣外界在使用photoBrowserViewController創(chuàng)建對(duì)象時(shí),就必須要傳入這兩個(gè)參數(shù)才可以,注意:由于當(dāng)前自定義控制器的init構(gòu)造函數(shù),所有必須調(diào)用super的init(initName:nil,bundle:nil)
-(11)#在photoBrowserViewController類中定義兩個(gè)屬性,用于保存外界傳遞的indexPath和picURLs,注意:在自定義的構(gòu)造函數(shù)中需要將init的參數(shù)賦值給定義的這兩個(gè)屬性,這樣最終就實(shí)現(xiàn):當(dāng)外界創(chuàng)建photoBrowserViewController時(shí)就會(huì)先將indexPath和picURLs傳遞進(jìn)去,然后再賦值給了photoBrowserViewController的兩個(gè)參數(shù),那么就可以在類中使用這兩個(gè)參數(shù)了
##二、在圖片瀏覽器中展示微博圖片
-(1)圖片瀏覽器界面分析:由于圖片可以拖動(dòng)切換下一張,頂部還有兩個(gè)控件關(guān)閉和保存按鈕,拖動(dòng)圖片的控件使用collectionView,每個(gè)圖片就是一個(gè)cell,關(guān)閉和保存按鈕使用UIButton,所以共三個(gè)控件
-(2)photoBrowserViewController中懶加載這三個(gè)控件并創(chuàng)建
-(3)設(shè)置UI界面:1.添加這三個(gè)子控件到控制器view上(注意要先添加collectionView,再添加兩個(gè)button,不然collectionView會(huì)蓋住這兩個(gè)button),2.設(shè)置frame,collectionView占據(jù)整個(gè)view可以設(shè)置為view的bounds,兩個(gè)按鈕在底部可以使用約束設(shè)置,3.設(shè)置兩個(gè)按鈕的背景顏色、文字、及字體大小,由于設(shè)置button的代碼較多,可以在UIButton的Extension分類中,給UIButton增加一個(gè)便利構(gòu)造函數(shù)init,在init中添加這三個(gè)參數(shù),便利構(gòu)造函數(shù)里面是調(diào)用self的init(),而不是super的哦,在便利構(gòu)造函數(shù)中把這三個(gè)參數(shù)設(shè)置為UIButton的相關(guān)屬性即可,那這樣就可不用在初始化UI界面的設(shè)置關(guān)閉和保存按鈕的屬性了,在懶加載時(shí)使用構(gòu)造函數(shù)創(chuàng)建出來(lái)就可以設(shè)置了,
-(4)設(shè)置collectionView的數(shù)據(jù)源及注冊(cè)collectionView: cell的個(gè)數(shù)就是picURLs數(shù)組的總個(gè)數(shù)
-(5)自定義的流水布局并在創(chuàng)建collectionView時(shí)使用這個(gè)自定義的布局,重寫(xiě)prepare方法(準(zhǔn)備布局時(shí)調(diào)用):1.設(shè)置itemSize為collectionView的size(占據(jù)屏幕),滾動(dòng)方向?yàn)樗綕L動(dòng)
-(6)自定義collectionViewCell并在注冊(cè)collectionView的cell時(shí)使用自定義的cell,
-(7)在自定義cell中定義picURL屬性,把collectionView的picURLs對(duì)應(yīng)的url傳遞給cell,讓cell自己去展示圖片
-(8)重寫(xiě)cell的initWithFrame構(gòu)造函數(shù)添加子控件,給cell的contentView添加scrollView,給scrollView添加imageView展示圖片:為什么使用scrollView,這是因?yàn)?,有些長(zhǎng)圖片是需要上下滾動(dòng)查看的,注意:1.子控件使用懶加載的方式創(chuàng)建2.imageView需要根據(jù)圖片的比例設(shè)置frame,目前已經(jīng)image的x值0,寬度為屏幕的寬度,未知y和高度,由于每個(gè)圖片大小不同,無(wú)法固定器frame,所以需要在cell的picURL屬性監(jiān)聽(tīng)器didSet中設(shè)置,只要圖片發(fā)送了改變,就根據(jù)圖片的等比例去計(jì)算高度及y值,不過(guò)y值分兩種情況1是當(dāng)圖片的高沒(méi)有超過(guò)屏幕時(shí)就讓其居中,當(dāng)超過(guò)屏幕高度時(shí)y值就為0,圖片的真實(shí)高度需要通過(guò)SDWebImage從沙盒緩存中獲取出image就能拿到真實(shí)寬高
-(9)設(shè)置imageView的image從沙盒中取出的圖片,即可展示圖片了
##三、下載大圖及繪制下載圖片進(jìn)度
-(1)由于當(dāng)前展示的圖片的url是縮略圖的,導(dǎo)致展示的圖片非常模糊,所以在設(shè)置imageView的image時(shí)需要設(shè)置為大圖片
-(2)新浪的圖片URL是有規(guī)則的:小圖的URL有thumbnail字符,中圖有bmiddle,大圖是large,所以我們把小圖的url字符串中thumbnail字符替換為bmiddle就可以獲取大=中圖的url
-(3)繪制圖片的下載進(jìn)度:
-1.自定義ProgressView繼承自UIView,使用drawRect方法繪制一個(gè)圓
-2.創(chuàng)建貝塞爾曲線對(duì)象進(jìn)行繪制
-3.在cell類中懶加載ProgressView,并將ProgressView添加到cell的contentView上,設(shè)置ProgressView的center為cell的屏幕的中心點(diǎn),寬高為50
-4.讓ProgressView默認(rèn)隱藏,并清空背景顏色
-5.當(dāng)下載圖片時(shí)顯示ProgressView,下載完成后隱藏
##四、問(wèn)題解決
-(1).當(dāng)點(diǎn)擊微博的圖片時(shí),不管點(diǎn)擊的哪一張展示的都是第一張圖片
-解決方法:在photoBrowserViewController中讓collectionView滾動(dòng)到對(duì)應(yīng)的位置,調(diào)用collectionView的scrollToItemAtIndexPath方法,然后將之前保存的indexPath屬性傳進(jìn)去、再傳入左對(duì)齊、不需要?jiǎng)赢?huà),即可
-(2).每張圖片之間的挨的太緊不美觀,讓圖片之間產(chǎn)生間距
-解決方法:設(shè)置photoBrowserViewController的view.frame.size+=20,重寫(xiě)loadView方法中設(shè)置,再在cell中設(shè)置scrollView.frame.size.width -= 20,注意:不要通過(guò)bounds來(lái)修改,一定要使用frame,不然會(huì)出現(xiàn)問(wèn)題,這是因?yàn)樾薷腷ounds的width時(shí),會(huì)以中心點(diǎn)為原點(diǎn),控件的左邊加10的寬,右邊加10的寬,而frame是以左上角為原點(diǎn)增加寬度,所以最終是右邊加20寬度
-(3).點(diǎn)擊圖片時(shí)關(guān)閉圖片瀏覽器
-分析:設(shè)置cell的代理,didSelectItemAtIndexPath方法中調(diào)用closeButtonClick,但是此時(shí)點(diǎn)擊圖片時(shí)并沒(méi)有反應(yīng),這是因?yàn)閕mageView的父控件把事件劫走了,而且我們想要的消失是只有點(diǎn)擊圖片才可以退出界面,點(diǎn)擊圖片以外的無(wú)反應(yīng),所以最終是給imageView添加點(diǎn)按手勢(shì)
-解決方法:當(dāng)點(diǎn)擊imageView時(shí),通知cell的代理關(guān)閉圖片瀏覽器
-1.創(chuàng)建代理協(xié)議
protocol XYPhotoBrowerCellDelegate {
func photoBrowerImageViewClick()
}
var delegate : XYPhotoBrowerCellDelegate? // 代理屬性
##五、點(diǎn)擊保存按鈕時(shí)將圖片保存到相冊(cè)中
-(1).獲取當(dāng)前正在顯示的image:通過(guò)collectionView的visbibleCells,這個(gè)方法會(huì)返回所有在顯示的cell的數(shù)組,由于當(dāng)前正在顯示的只要一個(gè)imageView,所以可以取出數(shù)組中first,通過(guò)cell獲取到imageView的image
##六、自定義彈出和消失動(dòng)畫(huà)
-(1).需求當(dāng)點(diǎn)擊圖片時(shí),從圖片的位置慢慢放大顯示圖片,而且圖片后面的界面不可以消失
-解決方法(一、設(shè)置漸變效果顯示和關(guān)閉控制器):
1.修改modal的彈出樣式presentationStyle為自定義custom,這樣在modal出控制器時(shí)就不會(huì)隱藏控制器的view了
2.使用自定義轉(zhuǎn)場(chǎng),讓圖片位置慢慢放大圖片,設(shè)置轉(zhuǎn)場(chǎng)代理
3.創(chuàng)建PhotoBrowerAnimation類繼承自NSObject,用于管理轉(zhuǎn)場(chǎng)動(dòng)畫(huà)
4.在Home控制器中,懶加載屬性PhotoBrowerAnimation對(duì)象,在彈出圖片瀏覽器的方法中,設(shè)置圖片瀏覽器photoBrowerVc的轉(zhuǎn)場(chǎng)代理為PhotoBrowerAnimation對(duì)象,在PhotoBrowerAnimation類中實(shí)現(xiàn)轉(zhuǎn)場(chǎng)的代理方法
-(2).從圖片的位置慢慢放大顯示圖片,而且圖片后面的界面不可以消失
-解決方法(最終方法):
1.需要拿到三個(gè)東西:(1)圖片開(kāi)始始frame,(2)最終要顯示在照片瀏覽器中的frame,(3)臨時(shí)創(chuàng)建一個(gè)imageVew用于做動(dòng)畫(huà)的
2.把這個(gè)臨時(shí)的imageView的frame設(shè)置為圖片開(kāi)始的frame,然后將imageView慢慢放大到圖片顯示瀏覽器中的frame,放大完后再移除這個(gè)臨時(shí)imageView
3.由于我們是在XYPhotoBrowerAnimation類中執(zhí)行動(dòng)畫(huà)的,而這里拿不到圖片的起始和最終frame及圖片本身
4.面向協(xié)議開(kāi)發(fā):可以找一個(gè)人幫我拿這些東西(swift和oc叫做面向協(xié)議開(kāi)發(fā)),(1)在XYPhotoBrowerAnimation中定義協(xié)議protocol,繼承自基協(xié)議NSObjectProtocol,目的:讓別人準(zhǔn)守協(xié)議,只要?jiǎng)e人遵守這個(gè)協(xié)議,就可以有協(xié)議中的方法,那最終就可以通過(guò)協(xié)議方法拿到想要的東西
5.協(xié)議中提供三個(gè)方法:用于獲取開(kāi)始位置、最終位置、以及獲取UIImageView對(duì)象的三個(gè)方法,再提供代理屬性,代理屬性必須遵守協(xié)議
6.起始位置只有cell的父控件picCollectionView可以提供,讓picCollectionView遵守協(xié)議,并實(shí)現(xiàn)協(xié)議中的三個(gè)方法
7.讓picCollectionView成為XYPhotoBrowerAnimation的代理:在picCollectionView中發(fā)布顯示圖片瀏覽器的通知中,把picCollectionView自己傳入object通知參數(shù)中,然后在HomeViewController接收到通知的方法中,取出這個(gè)object就是picCollectionView,然后就可以設(shè)置XYPhotoBrowerAnimation的代理為picCollectionView了,
8.在XYPhotoBrowerAnimation中定義一個(gè)indexPath屬性,將HomeViewController中的indexPath賦值給他,這樣完成傳值
9.獲取起始的frame:picCollectionView中cell相對(duì)于整個(gè)屏幕的frame,所以我們需要先通過(guò)cellForItemIndexPath獲取cell,再將cell的frame轉(zhuǎn)換坐標(biāo)為相對(duì)keyWindow的frame
10.獲取結(jié)束位置:先通過(guò)indexPath拿到picURLs中對(duì)應(yīng)的額圖片url,可以通過(guò)SDWebImage獲取url對(duì)應(yīng)的圖片,然后再計(jì)算長(zhǎng)圖時(shí)圖片的frame,短圖時(shí)圖片的frame
11.獲取UIImageView對(duì)象: 先創(chuàng)建UIImageView對(duì)象,再通過(guò)SDWebImage從磁盤(pán)中獲取image,設(shè)置imageView的屬性:將獲取的image設(shè)置為imageView的image,并設(shè)置contentMode為按照原來(lái)的寬高比填充scaleAspectFill,如果有超出的部分剪掉,然后返回這個(gè)imageView即可
12.在彈出動(dòng)畫(huà)方法中拿到起始frame和結(jié)束frame,將這iamgeView添加到容器視圖中,給imageView做從起始到結(jié)束frame的動(dòng)畫(huà)效果,動(dòng)畫(huà)結(jié)束后讓這個(gè)imageView從父控件中移除
##七、消失動(dòng)畫(huà)
-(1).需求:點(diǎn)擊照片瀏覽器中圖片或關(guān)閉按鈕時(shí),圖片從照片瀏覽器中慢慢消失到圖片在HomeviewController中的位置
-(2).分析:做消失動(dòng)畫(huà)我們需要兩個(gè)東西,一個(gè)是當(dāng)前即將消失的圖片在photoBrower中的indexPath,另外一個(gè)是需要一個(gè)臨時(shí)的imageView,讓imageView的frame與photoBrowerCell的imageView的frame相同,并把photoBrower中cell的imageView的image賦值給臨時(shí)imageView的image,那我們拿到這兩個(gè)屬性后就可以直接做消失動(dòng)畫(huà)了
-(3).誰(shuí)做消失動(dòng)畫(huà)的代理做合適:photoBrowerController,因?yàn)閏ell的父控件是collectionView,而collectionView是photoBrowerController的view的子控件,且是在photoBrowerController是collectionView的數(shù)據(jù)源,所以只有最合適,這就是面向協(xié)議開(kāi)發(fā)