UIScrollView && UITableView && UICollectionView

引文:

照片瀏覽滑動(dòng)效果UIScrollView和UIPageControl組合 -- tada

使用UIScrollView 結(jié)合 UIImageView 實(shí)現(xiàn)圖片循環(huán)滾動(dòng)?-- 兩個(gè)和三個(gè)ImgView哪個(gè)性能更好?

UIScrollView新手教程? --? 不錯(cuò)

理解Scroll View? -- 光柵化和組合,原理好文

計(jì)算機(jī)圖形渲染的流程? -- ?知識(shí)拓展,很通俗

繪制像素到屏幕上? -- ?底層繪圖

ScrollView 與 Autolayout? -- ?一個(gè)坑,應(yīng)該先加一個(gè)contentView

UIScrollView 實(shí)踐經(jīng)驗(yàn)? -- ?好好領(lǐng)悟


UIScrollView


首先正好說一下 frame 和 bounds的區(qū)別:


contentOffset


巧妙的通過改變scrollView的bounds,每個(gè)單獨(dú)的子視圖都被移動(dòng)了。這正是一個(gè)scrollView的工作原理。當(dāng)設(shè)置contentOffset時(shí),它改變scrollView.bounds.origin。(子視圖的frame是相對(duì)于父視圖bounds的布局)


contentSize


contentSize > bounds,可以滾動(dòng)視圖


contentOffset就等同于bounds.origin


contentInsets


contentInset屬性可以改變contentOffset的最大值和最小值,這樣便可以滾動(dòng)出可滾動(dòng)的區(qū)域。新手小例子中用contentInsets來控制縮小的圖片居中。

UITableView 刷新原理

tableView為了適應(yīng)每個(gè)cell,可滾動(dòng)區(qū)域是通過精心計(jì)算的。當(dāng)你滾動(dòng)經(jīng)過tableView的第一個(gè)或最后一個(gè)cell邊界時(shí),tableView將contentOffset彈回并復(fù)位,所以cells又一次緊貼scrollView的bounds。所以,必須將?refresh control放在可滾動(dòng)區(qū)域的上方。這將允許首先contentOffset彈回第一行。

當(dāng)向下拉動(dòng)出足夠多的區(qū)域時(shí),tableView設(shè)置contentInsets,擴(kuò)大了contentOffset的區(qū)域,refreshControl的區(qū)域就被包含進(jìn)來。刷新完成后,contentInsets恢復(fù)原始值,contentOffset也恢復(fù)。(EGOTableViewPullRefresh實(shí)現(xiàn))


小應(yīng)用

當(dāng)鍵盤出現(xiàn)在界面上的時(shí)候,擋住了scrollView原本顯示的一部分。通過設(shè)置contentInsets.bottom = 鍵盤的高度,可以暫時(shí)擴(kuò)大contentOffset的向下最大可視范圍,從而能拖動(dòng)看到被鍵盤擋住的那部分。鍵盤消失時(shí),再恢復(fù)contentInsets。


ScrollView && AutoLayout?


對(duì)于 UIScrollView 的 subview 來說,它的 leading/trailing/top/bottom space 是相對(duì)于 UIScrollView 的 contentSize 而不是 bounds 來確定的,所以當(dāng)你嘗試用 UIScrollView 和它 subview 的 leading/trailing/top/bottom 來互相決定大小的時(shí)候,就會(huì)出現(xiàn) ? ? ? ? ? ? ? ? ?「Has ambiguous scrollable content width/height」的 warning。

正確的姿勢是用 UIScrollView 外部的 view 或 UIScrollView 本身的 width/height 確定 subview 的尺寸,進(jìn)而確定 contentSize。

因?yàn)?UIScrollView 本身的 leading/trailing/top/bottom 變得不好用,所以我習(xí)慣的做法是在 UIScrollView 和它原來的 subviews 之間增加一個(gè) content view


delegate調(diào)用順序


scrollViewDidScroll:? --? 在任何方式觸發(fā) contentOffset 變化的時(shí)候都會(huì)被調(diào)用

scrollViewWillBeginDragging:? --? dragging noDecelerating

scrollViewWillEndDragging: withVelocity: targetContentOffset:? --? dragging noDecelerating

scrollViewDidEndDragging: willDecelerate:? --? dragging noDecelerating

scrollViewWillBeginDecelerating:? --? dragging decelerating

scrollViewDidEndDecelerating:? --? noDragging noDecelerating


分頁優(yōu)化


pagingEnabled

缺點(diǎn)蠻多,具體padding還不懂

Snap

最后沒到位,動(dòng)畫會(huì)突然到位,是突兀

targetContentOffset

更平滑,之前計(jì)算好,動(dòng)畫自然


視覺差


通過 scrollViewDidScroll 實(shí)時(shí)改變另一個(gè)scrollView的contentOffSet,和新手教程里的原理類似。


重用


維護(hù)一個(gè)重用隊(duì)列

當(dāng)元素離開可見范圍時(shí),removeFromSuperview 并加入重用隊(duì)列(enqueue)

當(dāng)需要加入新的元素時(shí),先嘗試從重用隊(duì)列獲取可重用元素(dequeue)并且從重用隊(duì)列移除

如果隊(duì)列為空,新建元素

這些一般都在 scrollViewDidScroll: 方法中完成


note that:


當(dāng)重用對(duì)象為 view controller 時(shí),記得 addChildViewController

當(dāng) view 或 view controller 被重用但其對(duì)應(yīng) model 發(fā)生變化的時(shí)候,需要及時(shí)清理重用前留下的內(nèi)容

數(shù)據(jù)可以適當(dāng)做緩存,在重用的時(shí)候嘗試從緩存中讀取數(shù)據(jù)甚至之前的狀態(tài)(如 table view 的 contentOffset),以得到更好的用戶體驗(yàn)

當(dāng) on screen 的元素?cái)?shù)量可確定的時(shí)候,有時(shí)候可以提前 init 這些元素,不會(huì)在 scroll 過程中遇到因?yàn)?init 開銷帶來的卡頓(尤其是以 view controller 為重用對(duì)象的時(shí)候)




引文:

IOS之觸摸事件和手勢

觸摸事件:?

touchesBegan:withEvent:

touchesMoved:withEvent:

touchesEnded:withEvent:

touchesCancelled:withEvent: ?-- ?電話導(dǎo)入取消

(1) 單tap下,touches == event.allTouches。 兩指,event.allTouches.count == 2;

(2) 連tap兩下,touches -> touch -> tapCount == 2 ?-- 想到很多連擊游戲,如果可以MS到方法里把tapCnt改個(gè)100。

通過實(shí)現(xiàn)一個(gè)TableView來理解iOS UI編程

(1)UIView中:

initWithFrame:-- ?初始化

layoutSubviews ?-- ?布局 (setNeedsLayout)

CGGeometry.h -- CGRectGetMaxX


(2)首先將編譯選項(xiàng)改為 OC++。

如果頭文件中用 @class 聲明一個(gè)類,那么這個(gè)類中的對(duì)象在被外界訪問的時(shí)候就會(huì)出現(xiàn) Member access into incomplete type "~~"`


(3)子類化UIScrollView實(shí)現(xiàn)對(duì)Cell的布局 ?-- ?高度和Y值進(jìn)行布局

(4)Cell的重用 ?-- ?visibleCells(刪除入隊(duì)) 和 cacheCells(獲取出隊(duì))

和VC重用思路類似。

在要使用一個(gè)Cell的時(shí)候我們先去看看tableView中有沒有可以重用的cell,如果有就用這個(gè)可以重用的cell,只有在沒有的時(shí)候才去創(chuàng)建一個(gè)Cell。這就是享元模式

享元模式可以理解成,當(dāng)細(xì)粒度的對(duì)象數(shù)量特別多的時(shí)候運(yùn)行的代價(jià)會(huì)相當(dāng)大,此時(shí)運(yùn)用共享的技術(shù)來大大降低運(yùn)行成本。比較突出的表現(xiàn)就是內(nèi)容有效的抑制內(nèi)存抖動(dòng)的情況發(fā)生,還有控制內(nèi)存增長。它的英文名字是flyweight,讓重量飛起來。

(5)響應(yīng)和處理事件? -- ?addGesture (點(diǎn)擊位置與cellFrame)

(6)接口和數(shù)據(jù)獲取 ?-- ?protocol (數(shù)據(jù)源,點(diǎn)擊事件)

(7)選中態(tài) ?-- ?事件方法判斷,backgroundView展示




UITableView 滾動(dòng)流程性優(yōu)化? -- ?O(1)

詳細(xì)整理:UITableView優(yōu)化技巧??--? 用到很多不熟悉的方法

UITableView性能優(yōu)化-一次面試后的反思總結(jié)

提升UITableView性能-復(fù)雜頁面的優(yōu)化??-- ?類似

UIScrollView 實(shí)踐經(jīng)驗(yàn)--? 好棒幾個(gè)例子,最佳技巧UITableView

如何加強(qiáng) iOS 里的列表滾動(dòng)時(shí)的順暢感?-- ?很多

iOS 保持界面流暢的技巧??-- ?ibib

一次 TableView 性能優(yōu)化經(jīng)歷

10個(gè)加速Table Views開發(fā)的Tips

阿崢教你實(shí)現(xiàn)UITableView循環(huán)利用

VVeboTableViewDemo


UITableView


1. 快速滑動(dòng)

當(dāng)用戶手動(dòng)拖動(dòng)tableView時(shí),加載cell圖片。

當(dāng)用戶快速滑動(dòng)的減速過程中, 有緩存就加載,沒緩存不加載cell圖片。

targetContentOffset位置直接設(shè)置加載,滾動(dòng)到的時(shí)候已經(jīng)開始加載。


2. LazyLoad

- fetchDataFromServer ?- AFHTTPRequestOperationManager

- heightForRowAtIndexPath

- setupCell: withIndexPath:? --? SDWebImageDownloader

- loadImageForVisibleCells

- scrollViewDelegate - 3


3. Cell重用機(jī)制

[tableView dequeueReusableCellWithIdentifier:(NSString)identifier ?forIndexPath:(NSIndexPath)indexPath];

(1) Storyboard: 定義Cell的Prototype,并設(shè)置其Reusable Identifier

(2) Xib自定義: [registerNib:(nullable UINib)nib forCellReuseIdentifier:(NSString)identifier];

(3) 代碼自定義:[registerClass:(nullable Class)cellClass forCellReuseIdentifier:(NSString *)identifier];

重寫自定義cell的 initWithStyle:withReuseableCellIdentifier: 方法進(jìn)行布局。

自定義cell時(shí),記得將其他內(nèi)容加到self.contentView 上,而不是直接添加到 cell 本身上。


4. 設(shè)計(jì)統(tǒng)一規(guī)格的Cell

- ?等高的Cell好設(shè)計(jì)

- ?動(dòng)態(tài)計(jì)算高度的Cell也應(yīng)該統(tǒng)一設(shè)計(jì)


5. 提前計(jì)算并緩存cell的UI尺寸信息

創(chuàng)建ViewModel,計(jì)算并儲(chǔ)存Cell的UI尺寸信息

tableView:heightForRowAtIndexPath: --> tableView:cellForRowAtIndexPath

使用了ViewModel來保存UI信息,Cell?高度的計(jì)算?和?使用的時(shí)機(jī)?需要特別留意。


6. 圓角,陰影,mask

(1) Cell中的view盡可能不要使用透明 -- opaque不透明的話,繪畫的時(shí)候就不會(huì)去畫下層的視圖。盡量減少cell中子視圖透明化以及做切圓操作,在layer層渲染圖層時(shí)會(huì)涉及上下文切換以及離屏渲染之類的,系統(tǒng)開銷會(huì)很大,特別是在cell視圖很復(fù)雜的時(shí)候,由于渲染問題導(dǎo)致的內(nèi)存開銷會(huì)讓你的tableview非??D。比如cell中需要設(shè)置頭像圓形直接設(shè)置圓角會(huì)很卡,一般用CG把拿到的圖片處理一遍在給cell使用就好了。圓角、陰影之類的全部 bitmap 化,或者放到后臺(tái) draw 好了再拿來用

(2) 用代碼自定義的cell,使用時(shí)要做 layer 柵格化處理

(3) 減少子視圖的層級(jí)關(guān)系

(4) 圖片載入在后臺(tái)進(jìn)程進(jìn)行,滾出可視范圍的載入進(jìn)程cancel掉

(5) 后臺(tái)對(duì)圖片先進(jìn)行解碼

UIImageView載入是惰性的說法,是對(duì)的。但是大部分開發(fā)者都沒有正確理解這一點(diǎn)。下面就詳細(xì)解釋一下:

[UIImage imageWithContentOfFile:] 出來的 UIImage 其實(shí)并沒有真正把文件解碼到內(nèi)存,而是要等到用的時(shí)候(例如去顯示或者去 scale)才會(huì)去做這件事情。但問題就在于 UIImageView 試圖去 draw 圖片的時(shí)候,它讀文件、渲染也是在主線程里做的,所以你要讀入的圖片如果很大(比如 iPad3 上的 @2x 圖),這一步就很容易會(huì)卡一下。這也就是為什么我說圖片要放到后臺(tái)進(jìn)程去解碼完之后,在主線程顯示。




iOS中線寬與像素的關(guān)系? -- ?補(bǔ)補(bǔ)補(bǔ)

UITableViewCell


1. UITableViewCell的估算機(jī)制與高度計(jì)算

iOS7?

estimatedRowHeight, SectionHeaderHeight, SectionFooterHeight ? ? ? ? ? ? ? ? ? ? iOS7中每個(gè)cell的高度會(huì)被系統(tǒng)自動(dòng)緩存起來,不會(huì)再重復(fù)計(jì)算了

[cell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize]; 可以通過約束計(jì)算到cell的高度。要求使用者對(duì)約束設(shè)置的比較熟練,要保證 contentView 內(nèi)部上下左右所有方向都有約束支撐,設(shè)置不合理的話計(jì)算的高度就成了0。

另外需要注意的是, 在iOS7下, 如果布局中有UILabel, 并且行數(shù)大于0時(shí), 需要指定preferredMaxLayoutWidth, 這樣Label才能知道自己什么時(shí)候該換行, 然后-systemLayoutSizeFittingSize才能得到正確的高度.

另一種方案就是,給cell的contentView添加一個(gè)和tableView寬度相同的寬度約束, 這樣就能在UILabel約束完備的情況下算出UILabel的寬度.(因?yàn)閏ontentView寬度的計(jì)算需要知道子控件寬度的累加,而UILabel的換行卻依賴著contentView的寬度,不換行就不知道UILabel的高度!)

iOS8

self-sizing cell


2.?制作一個(gè)可以滑動(dòng)操作的 Table View Cell

創(chuàng)建一個(gè)自定義 Cell

-- button1? button2 點(diǎn)擊傳遞給delegate去處理? -- 添加一個(gè) Delegate

-- contentView 用于覆蓋,Label用于顯示

為按鈕添加 Action

showDetailWithText: -- presentDetailNav

添加頂層視圖并添加滑動(dòng) Action

添加數(shù)據(jù) -- labelText由cell.label托管

手勢識(shí)別

-- panRecognizer - panThisCell:

-- panStartPoint 開始點(diǎn),判斷左右滑動(dòng)

-- startingRightLayoutConstraintConstant 右約束

-- contentViewLeftConstraint & contentViewRightConstraint

移動(dòng)這些約束

buttonTotalWidth

- (void)resetConstraintContstantsToZero:(BOOL)animated notifyDelegateDidClose:(BOOL)endEditing

- (void)setConstraintsToShowAllButtons:(BOOL)animated notifyDelegateDidOpen:(BOOL)notifyDelegate

UIGestureRecognizerStateBegan & UIGestureRecognizerStateChanged

Snap!

- (void)updateConstraintsIfNeeded:(BOOL)animated completion:(void(^)(BOOL finished))completion

set & reset

UIGestureRecognizerStateEnded & UIGestureRecognizerStateCancelled

更好地處理 Table View

Gesture沖突 -- shouldRecognizeSimultaneouslyWithGestureRecognizer:

重用cell出現(xiàn)打開 -- prepareForReuse

- (void)cellDidOpen:(UITableViewCell *)cell;

- (void)cellDidClose:(UITableViewCell *)cell;

- (void)openCell;

set & reset + delegate方法

cellsCurrentlyEditing -- 打開添加關(guān)閉移除沒問題,打開cell狀態(tài)滑動(dòng)tableView,會(huì)進(jìn)行重用會(huì)調(diào)用cellForRowAtIndexPath,所以檢測是否包含在currentlyEditing數(shù)組中,如果在就


3.?很炫的table view cell切換效果? SvpplyTable

看到簡單函數(shù)內(nèi)部又調(diào)用復(fù)雜函數(shù),現(xiàn)在終于想明白了,是為了對(duì)外提供簡介的API,然后復(fù)雜的邏輯在自己內(nèi)部做。我以后也要這么干。

如果多次跳轉(zhuǎn)調(diào)用函數(shù),傳遞參數(shù)名盡量不要變,不然會(huì)給閱讀增加負(fù)擔(dān)。

_ivar 比 self.ivar 的一個(gè)好處在于調(diào)試時(shí)候可以更容易看到值


4. 利用長按手勢移動(dòng) Table View Cells? --? 小而美




UICollectionView


UICollectionView Tutorial Part 1: Getting Started

UICollectionView Tutorial Part 2: Reusable Views and Cell Selection

現(xiàn)在,UICollectionViews有了簡單的重排功能? -- 太棒

葉孤城:UICollectionView自定義布局教程——Pinterest

UICollectionView 高級(jí)進(jìn)階篇? -- 各種酷炫

自定義 Collection View 布局? -- ?中規(guī)中矩

UICollectionView Custom Layout Tutorial: A Spinning Wheel? -- ?旋轉(zhuǎn)視圖, 改變錨點(diǎn), 自定義 collection view layout

Vertical? --? from left to right,horizontal? --? from top to bottom

Supplementary viewsdecoration views 必須是UICollectionReusableView的子類。

header && footer


UICollectionViewController,UICollectionView,UICollectionViewCell,UICollectionReuseableView,UICollectionViewLayout,UICollectionViewLayoutAttributes

- (void)prepareLayout? -- ?這個(gè)整個(gè)布局過程中最重要的方法之一。因?yàn)檫@里可以創(chuàng)建和存儲(chǔ)layout attributes。

- (CGSize)collectionViewContentSize

- (NSArray*)layoutAttributesForElementsInRect:(CGRect)rect

- (UICollectionViewLayoutAttributes*)layoutAttributesForItemAtIndexPath:(NSIndexPath*)indexPath

- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity

- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds


動(dòng)畫:

插入和刪除

initialLayoutAttributesForAppearingItemAtIndexPath:

initialLayoutAttributesForAppearingSupplementaryElementOfKind:atIndexPath:

initialLayoutAttributesForAppearingDecorationElementOfKind:atIndexPath:

finalLayoutAttributesForDisappearingItemAtIndexPath:

finalLayoutAttributesForDisappearingSupplementaryElementOfKind:atIndexPath:

finalLayoutAttributesForDisappearingDecorationElementOfKind:atIndexPath:


布局間切換

將一個(gè) collection view 布局動(dòng)態(tài)的切換到另外一個(gè)布局。setCollectionViewLayout:animated:(突然想到那種四角碰撞,同時(shí)形變的小例子)


發(fā)現(xiàn)快捷鍵: option + command = 在IB中動(dòng)態(tài)標(biāo)尺

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容