UIScrollView && UITableView && UICollectionView

引文:

照片浏览滑动效果UIScrollView和UIPageControl组合 -- tada

使用UIScrollView 结合 UIImageView 实现图片循环滚动 -- 两个和三个ImgView哪个性能更好?

UIScrollView新手教程  --  不错

理解Scroll View  -- 光栅化和组合,原理好文

计算机图形渲染的流程  --  知识拓展,很通俗

绘制像素到屏幕上  --  底层绘图

ScrollView 与 Autolayout  --  一个坑,应该先加一个contentView

UIScrollView 实践经验  --  好好领悟


UIScrollView


首先正好说一下 frame 和 bounds的区别:


contentOffset


巧妙的通过改变scrollView的bounds,每个单独的子视图都被移动了。这正是一个scrollView的工作原理。当设置contentOffset时,它改变scrollView.bounds.origin。(子视图的frame是相对于父视图bounds的布局)


contentSize


contentSize > bounds,可以滚动视图


contentOffset就等同于bounds.origin


contentInsets


contentInset属性可以改变contentOffset的最大值和最小值,这样便可以滚动出可滚动的区域。新手小例子中用contentInsets来控制缩小的图片居中。

UITableView 刷新原理

tableView为了适应每个cell,可滚动区域是通过精心计算的。当你滚动经过tableView的第一个或最后一个cell边界时,tableView将contentOffset弹回并复位,所以cells又一次紧贴scrollView的bounds。所以,必须将�refresh control放在可滚动区域的上方。这将允许首先contentOffset弹回第一行。

当向下拉动出足够多的区域时,tableView设置contentInsets,扩大了contentOffset的区域,refreshControl的区域就被包含进来。刷新完成后,contentInsets恢复原始值,contentOffset也恢复。(EGOTableViewPullRefresh实现)


小应用

当键盘出现在界面上的时候,挡住了scrollView原本显示的一部分。通过设置contentInsets.bottom = 键盘的高度,可以暂时扩大contentOffset的向下最大可视范围,从而能拖动看到被键盘挡住的那部分。键盘消失时,再恢复contentInsets。


ScrollView && AutoLayout 


对于 UIScrollView 的 subview 来说,它的 leading/trailing/top/bottom space 是相对于 UIScrollView 的 contentSize 而不是 bounds 来确定的,所以当你尝试用 UIScrollView 和它 subview 的 leading/trailing/top/bottom 来互相决定大小的时候,就会出现                  「Has ambiguous scrollable content width/height」的 warning。

正确的姿势是用 UIScrollView 外部的 view 或 UIScrollView 本身的 width/height 确定 subview 的尺寸,进而确定 contentSize。

因为 UIScrollView 本身的 leading/trailing/top/bottom 变得不好用,所以我习惯的做法是在 UIScrollView 和它原来的 subviews 之间增加一个 content view


delegate调用顺序


scrollViewDidScroll:  --  在任何方式触发 contentOffset 变化的时候都会被调用

scrollViewWillBeginDragging:  --  dragging noDecelerating

scrollViewWillEndDragging: withVelocity: targetContentOffset:  --  dragging noDecelerating

scrollViewDidEndDragging: willDecelerate:  --  dragging noDecelerating

scrollViewWillBeginDecelerating:  --  dragging decelerating

scrollViewDidEndDecelerating:  --  noDragging noDecelerating


分页优化


pagingEnabled

缺点蛮多,具体padding还不懂

Snap

最后没到位,动画会突然到位,是突兀

targetContentOffset

更平滑,之前计算好,动画自然


视觉差


通过 scrollViewDidScroll 实时改变另一个scrollView的contentOffSet,和新手教程里的原理类似。


重用


维护一个重用队列

当元素离开可见范围时,removeFromSuperview 并加入重用队列(enqueue)

当需要加入新的元素时,先尝试从重用队列获取可重用元素(dequeue)并且从重用队列移除

如果队列为空,新建元素

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


note that:


当重用对象为 view controller 时,记得 addChildViewController

当 view 或 view controller 被重用但其对应 model 发生变化的时候,需要及时清理重用前留下的内容

数据可以适当做缓存,在重用的时候尝试从缓存中读取数据甚至之前的状态(如 table view 的 contentOffset),以得到更好的用户体验

当 on screen 的元素数量可确定的时候,有时候可以提前 init 这些元素,不会在 scroll 过程中遇到因为 init 开销带来的卡顿(尤其是以 view controller 为重用对象的时候)




引文:

IOS之触摸事件和手势

触摸事件: 

touchesBegan:withEvent:

touchesMoved:withEvent:

touchesEnded:withEvent:

touchesCancelled:withEvent:  --  电话导入取消

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

(2) 连tap两下,touches -> touch -> tapCount == 2  -- 想到很多连击游戏,如果可以MS到方法里把tapCnt改个100。

通过实现一个TableView来理解iOS UI编程

(1)UIView中:

initWithFrame:--  初始化

layoutSubviews  --  布局 (setNeedsLayout)

CGGeometry.h -- CGRectGetMaxX


(2)首先将编译选项改为 OC++。

如果头文件中用 @class 声明一个类,那么这个类中的对象在被外界访问的时候就会出现 Member access into incomplete type "~~"`


(3)子类化UIScrollView实现对Cell的布局  --  高度和Y值进行布局

(4)Cell的重用  --  visibleCells(删除入队) 和 cacheCells(获取出队)

和VC重用思路类似。

在要使用一个Cell的时候我们先去看看tableView中有没有可以重用的cell,如果有就用这个可以重用的cell,只有在没有的时候才去创建一个Cell。这就是享元模式

享元模式可以理解成,当细粒度的对象数量特别多的时候运行的代价会相当大,此时运用共享的技术来大大降低运行成本。比较突出的表现就是内容有效的抑制内存抖动的情况发生,还有控制内存增长。它的英文名字是flyweight,让重量飞起来。

(5)响应和处理事件  --  addGesture (点击位置与cellFrame)

(6)接口和数据获取  --  protocol (数据源,点击事件)

(7)选中态  --  事件方法判断,backgroundView展示




UITableView 滚动流程性优化  --  O(1)

详细整理:UITableView优化技巧  --  用到很多不熟悉的方法

UITableView性能优化-一次面试后的反思总结

提升UITableView性能-复杂页面的优化  --  类似

UIScrollView 实践经验--  好棒几个例子,最佳技巧UITableView

如何加强 iOS 里的列表滚动时的顺畅感?--  很多

iOS 保持界面流畅的技巧  --  ibib

一次 TableView 性能优化经历

10个加速Table Views开发的Tips

阿峥教你实现UITableView循环利用

VVeboTableViewDemo


UITableView


1. 快速滑动

当用户手动拖动tableView时,加载cell图片。

当用户快速滑动的减速过程中, 有缓存就加载,没缓存不加载cell图片。

targetContentOffset位置直接设置加载,滚动到的时候已经开始加载。


2. LazyLoad

- fetchDataFromServer  - AFHTTPRequestOperationManager

- heightForRowAtIndexPath

- setupCell: withIndexPath:  --  SDWebImageDownloader

- loadImageForVisibleCells

- scrollViewDelegate - 3


3. Cell重用机制

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

(1) Storyboard: 定义Cell的Prototype,并设置其Reusable Identifier

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

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

重写自定义cell的 initWithStyle:withReuseableCellIdentifier: 方法进行布局。

自定义cell时,记得将其他内容加到self.contentView 上,而不是直接添加到 cell 本身上。


4. 设计统一规格的Cell

-  等高的Cell好设计

-  动态计算高度的Cell也应该统一设计


5. 提前计算并缓存cell的UI尺寸信息

创建ViewModel,计算并储存Cell的UI尺寸信息

tableView:heightForRowAtIndexPath: --> tableView:cellForRowAtIndexPath

使用了ViewModel来保存UI信息,Cell 高度的计算 和 使用的时机 需要特别留意。


6. 圆角,阴影,mask

(1) Cell中的view尽可能不要使用透明 -- opaque不透明的话,绘画的时候就不会去画下层的视图。尽量减少cell中子视图透明化以及做切圆操作,在layer层渲染图层时会涉及上下文切换以及离屏渲染之类的,系统开销会很大,特别是在cell视图很复杂的时候,由于渲染问题导致的内存开销会让你的tableview非常卡顿。比如cell中需要设置头像圆形直接设置圆角会很卡,一般用CG把拿到的图片处理一遍在给cell使用就好了。圆角、阴影之类的全部 bitmap 化,或者放到后台 draw 好了再拿来用

(2) 用代码自定义的cell,使用时要做 layer 栅格化处理

(3) 减少子视图的层级关系

(4) 图片载入在后台进程进行,滚出可视范围的载入进程cancel掉

(5) 后台对图片先进行解码

UIImageView载入是惰性的说法,是对的。但是大部分开发者都没有正确理解这一点。下面就详细解释一下:

[UIImage imageWithContentOfFile:] 出来的 UIImage 其实并没有真正把文件解码到内存,而是要等到用的时候(例如去显示或者去 scale)才会去做这件事情。但问题就在于 UIImageView 试图去 draw 图片的时候,它读文件、渲染也是在主线程里做的,所以你要读入的图片如果很大(比如 iPad3 上的 @2x 图),这一步就很容易会卡一下。这也就是为什么我说图片要放到后台进程去解码完之后,在主线程显示。




iOS中线宽与像素的关系  --  补补补

UITableViewCell


1. UITableViewCell的估算机制与高度计算

iOS7 

estimatedRowHeight, SectionHeaderHeight, SectionFooterHeight                     iOS7中每个cell的高度会被系统自动缓存起来,不会再重复计算了

[cell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize]; 可以通过约束计算到cell的高度。要求使用者对约束设置的比较熟练,要保证 contentView 内部上下左右所有方向都有约束支撑,设置不合理的话计算的高度就成了0。

另外需要注意的是, 在iOS7下, 如果布局中有UILabel, 并且行数大于0时, 需要指定preferredMaxLayoutWidth, 这样Label才能知道自己什么时候该换行, 然后-systemLayoutSizeFittingSize才能得到正确的高度.

另一种方案就是,给cell的contentView添加一个和tableView宽度相同的宽度约束, 这样就能在UILabel约束完备的情况下算出UILabel的宽度.(因为contentView宽度的计算需要知道子控件宽度的累加,而UILabel的换行却依赖着contentView的宽度,不换行就不知道UILabel的高度!)

iOS8

self-sizing cell


2. 制作一个可以滑动操作的 Table View Cell

创建一个自定义 Cell

-- button1  button2 点击传递给delegate去处理  -- 添加一个 Delegate

-- contentView 用于覆盖,Label用于显示

为按钮添加 Action

showDetailWithText: -- presentDetailNav

添加顶层视图并添加滑动 Action

添加数据 -- labelText由cell.label托管

手势识别

-- panRecognizer - panThisCell:

-- panStartPoint 开始点,判断左右滑动

-- startingRightLayoutConstraintConstant 右约束

-- contentViewLeftConstraint & contentViewRightConstraint

移动这些约束

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出现打开 -- prepareForReuse

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

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

- (void)openCell;

set & reset + delegate方法

cellsCurrentlyEditing -- 打开添加关闭移除没问题,打开cell状态滑动tableView,会进行重用会调用cellForRowAtIndexPath,所以检测是否包含在currentlyEditing数组中,如果在就


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

看到简单函数内部又调用复杂函数,现在终于想明白了,是为了对外提供简介的API,然后复杂的逻辑在自己内部做。我以后也要这么干。

如果多次跳转调用函数,传递参数名尽量不要变,不然会给阅读增加负担。

_ivar 比 self.ivar 的一个好处在于调试时候可以更容易看到值


4. 利用长按手势移动 Table View Cells  --  小而美




UICollectionView


UICollectionView Tutorial Part 1: Getting Started

UICollectionView Tutorial Part 2: Reusable Views and Cell Selection

现在,UICollectionViews有了简单的重排功能  -- 太棒

叶孤城:UICollectionView自定义布局教程——Pinterest

UICollectionView 高级进阶篇  -- 各种酷炫

自定义 Collection View 布局  --  中规中矩

UICollectionView Custom Layout Tutorial: A Spinning Wheel  --  旋转视图, 改变锚点, 自定义 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  --  这个整个布局过程中最重要的方法之一。因为这里可以创建和存储layout attributes。

- (CGSize)collectionViewContentSize

- (NSArray*)layoutAttributesForElementsInRect:(CGRect)rect

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

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

- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds


动画:

插入和删除

initialLayoutAttributesForAppearingItemAtIndexPath:

initialLayoutAttributesForAppearingSupplementaryElementOfKind:atIndexPath:

initialLayoutAttributesForAppearingDecorationElementOfKind:atIndexPath:

finalLayoutAttributesForDisappearingItemAtIndexPath:

finalLayoutAttributesForDisappearingSupplementaryElementOfKind:atIndexPath:

finalLayoutAttributesForDisappearingDecorationElementOfKind:atIndexPath:


布局间切换

将一个 collection view 布局动态的切换到另外一个布局。setCollectionViewLayout:animated:(突然想到那种四角碰撞,同时形变的小例子)


发现快捷键: option + command = 在IB中动态标尺

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 156,265评论 4 359
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,274评论 1 288
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,087评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,479评论 0 203
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,782评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,218评论 1 207
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,594评论 2 309
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,316评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 33,955评论 1 237
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,274评论 2 240
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,803评论 1 255
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,177评论 2 250
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,732评论 3 229
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,953评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,687评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,263评论 2 267
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,189评论 2 258

推荐阅读更多精彩内容