引文:
照片瀏覽滑動(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í)候)
引文:
觸摸事件:?
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)利用
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 views 和 decoration 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)尺