AutoLayout深入浅出五[UITableView动态高度]
1. - (CGRect)boundingRectWithSize:(CGSize)size options:(NSStringDrawingOptions)options context:(nullableNSStringDrawingContext*)contextNS_AVAILABLE(10_11,6_0);
2. This intrinsic size must be independent of the content frame
3. 与contentView的四边间距约束很重要,有了4个与contentView的边缘约束,才能保证contentView的大小跟随其subviews变化。
4.
(1)iOS 8的估高
_iTableView.rowHeight=UITableViewAutomaticDimension; // iOS8是默认,可忽略
_iTableView.estimatedRowHeight=70.0;//设置为一个接近平均行高的值
estimatedHeightForRowAtIndexPath:&& heightForRowAtIndexPath:
(2)iOS 7:
<1> 创建用于辅助计算高度的cell(有看到用单例的)
<2> 配置辅助cell + 强制更新布局,用 Cell.contentView systemLayoutSizeFittingSize 返回高度
使用Autolayout实现UITableView的Cell动态布局和高度动态改变 -- 正解!好文!代码!
1. 设置好布局 -- compression && hugging = 1000 + 四周约束
2. cell重用 -- 每个重用标示符实例化一个与之对应的cell实例
3. iOS8 - 估高同上 -- 滚动条跳跃 => tableView:estimatedHeightForRowAtIndexPath:
4. iOS7 -- 同上
但如果你对性能的要求并不那么高,可以尝试用 TableView 的预估高度的功能,并把每个 Cell 高度缓存下来。这里有个来自百度知道团队的开源项目可以很方便的帮你实现这一点:FDTemplateLayoutCell -- YY
提供了-systemLayoutSizeFittingSize:的 API,在 contentView 中设置约束后,就能计算出准确的值;缺点是计算速度肯定没有手算快,而且这是个实例方法,需要维护专门为计算高度而生的template layout cell,它还要求使用者对约束设置的比较熟练,要保证 contentView 内部上下左右所有方向都有约束支撑,设置不合理的话计算的高度就成了0。
提到一个 UILabel 的问题,当 UILabel 行数大于0时,需要指定preferredMaxLayoutWidth后它才知道自己什么时候该折行。因为 UILabel 需要知道 superview 的宽度才能折行,而 superview 的宽度还依仗着子 view 宽度的累加才能确定。
iOS8 self-sizing cell -- 同上
-preferredMaxLayoutWidth的问题,在计算高度前向 contentView 加了一条和 table view 宽度相同的宽度约束,强行让 contentView 内部的控件知道了自己父 view 的宽度,再反算自己被外界约束的宽度。
UITableView-FDTemplateLayoutCell
<1> UITableView+FDTemplateLayoutCell
.h --
i. UITableView (FDTemplateLayoutCell)
fd_templateCellForReuseIdentifier: - 懒加载templateCellsByIdentifiers + 懒加载templateCell = 维护一个key:cellIdentifier value:singleCell的字典
fd_heightForCellWithIdentifier:configuration: - ↑↑↑ templateLayoutCell,回调配置,contentViewWidth,fittingSize - fd_enforceFrameLayout(if - 继承覆盖&&没继承 --------->sizeThatFits else - contentView加宽度约束 -> systemLayoutSizeFittingSize),1px分割线
fd_heightForCellWithIdentifier:cacheByIndexPath:configuration: - cache存在返回,不存在↑↑↑创建,缓存返回
fd_heightForCellWithIdentifier:cacheByKey:configuration: - 同上
ii. UITableViewCell (FDTemplateLayoutCell)
fd_isTemplateLayoutCell - 用于计算的cell
fd_enforceFrameLayout - 使用frame布局,调用-sizeThatFits:获取cell的高度
<2> UITableView+FDKeyedHeightCache
.h --
i. FDKeyedHeightCache - 横竖屏height cache
existsHeightForKey:,cacheHeight: byKey:,heightForKey:
invalidateHeightForKey:,invalidateAllHeightCache - 失效Portrait+Landscape remove~~
ii. UITableView (FDKeyedHeightCache)
fd_keyedHeightCache - getter方法懒加载
<3> UITableView+FDIndexPathHeightCache
.h --
i. FDIndexPathHeightCache
existsHeightAtIndexPath:,cacheHeight:byIndexPath:,heightForIndexPath:
invalidateHeightAtIndexPath: + invalidateAllHeightCache - 枚举block回调 = 同上
ii. UITableView (FDIndexPathHeightCache)
fd_indexPathHeightCache - getter方法懒加载
iii. UITableView (FDIndexPathHeightCacheInvalidation)
fd_reloadDataWithoutInvalidateIndexPathHeightCache
.m --
i. buildCachesAtIndexPathsIfNeeded: - 原功能用于创建对应缓存位置,也可增加函数健壮性
-> buildSectionsIfNeeded: - 创建<=当前section的各种,不重复
-> buildRowsIfNeeded: - 创建<=当前row各种,不重复
enumerateAllOrientationsUsingBlock: - PortraitArray && LandscapeArray
iii.
FDPrimaryCall - 用于执行方法实现交换后的前向回调
fd_reloadDataWithoutInvalidateIndexPathHeightCache - 会调用原始的实现,所以缓存不会失效 - 当想要reloadData但是不想失效缓存,比如: 在tableView底部加载更多数据
+load - 9个方法的method swizzling
允许缓存自动失效 = YES
fd_reloadData - removeAllObjects
fd_insertSections:: - buildSectionsIfNeeded(确保缓存建立) + insertObject:atIndex:
fd_deleteSections:: - buildSectionsIfNeeded(增加健壮性) +removeObjectAtIndex:
fd_reloadSections:: - buildSectionsIfNeeded(增加健壮性) + [section] removeAllObjects
fd_moveSection:: - buildSectionsIfNeeded(增加健壮性) + exchangeObjectAtIndex:withObjectAtIndex:
fd_insertRowsAtIndexPaths:: - buildCachesAtIndexPathsIfNeeded(确保缓存建立) + [section] insertObj:@-1 atIndex:row
fd_deleteRowsAtIndexPaths:: - buildCachesAtIndexPathsIfNeeded(增加健壮性) + mutableIndexSetsToRemove = key:@(section) value:indexSet{row, row, row} + [key.integerValue] removeObjectsAtIndexes:indexSet
fd_reloadRowsAtIndexPaths:: - buildCachesAtIndexPathsIfNeeded(增加健壮性) + [section][row] = @-1;
fd_moveRowAtIndexPath:: - buildCachesAtIndexPathsIfNeeded(增加健壮性) + 取出最底层value,手工交换
<4> UITableView+FDTemplateLayoutCellDebug
.h -- fd_debugLogEnabled,fd_debugLog:
.m --
fd_debugLogEnabled - debug打印开关,如"creating", "calculating", "precaching" or "hitting cache"
魔性表达式,定出自孙源老师之手核心代码:
templateLayoutCell -> 设置内容 -> 计算contentViewWidth -> frame(sizeThatFits:) OR autoLayout(systemLayoutSizeFittingSize) -> 1px分割线
轻松学习之二 —— iOS利用Runtime自定义控制器POP手势动画
FDFullscreenPopGesture
.h --
UINavigationController (FDFullscreenPopGesture): <1> fd_fullscreenPopGR <2>fd_viewControllerBasedNavigationBarAppearanceEnabled(VC自己控制导航栏的外观,默认YES)
UIViewController (FDFullscreenPopGesture): <1> fd_interactivePopDisabled (禁止GR)<2> fd_prefersNavigationBarHidden(默认NO,当fd_~~↑↑↑属性YES,才检查) <3> fd_interactivePopMaxAllowedInitialDistanceToLeftEdge(默认0,不限制)
.m --
<1> _FDFullscreenPopGestureRecognizerDelegate
gestureRecognizerShouldBegin:(出栈>1,fd_interactivePopDisabled,beginningLocation.x > maxAllowedInitialDistance,_isTransitioning,translationInView:反方向)
<2> UIViewController (FDFullscreenPopGesturePrivate)
_FDViewControllerWillAppearInjectBlock(关联对象get && set)
+load: (标准写法) - viewWillAppear:
fd_viewWillAppear:(fd_willAppearInjectBlock(self, animated))
<3> UINavigationController (FDFullscreenPopGesture)
+load: (标准写法) - pushViewController:animated:
fd_pushViewController:animated: (原生禁止,添加自定义pan(target+action+delegate),设置隐藏bar)
fd_setupViewControllerBasedNavigationBarAppearanceIfNeeded: (_FDViewControllerWillAppearInjectBlock(NavBarHidden: ),为appearing/disappearingViewController设置willAppearInjectBlock)
fd_popGestureRecognizerDelegate(懒加载关联对象)
fd_fullscreenPopGestureRecognizer(懒加载关联对象)
fd_viewControllerBasedNavigationBarAppearanceEnabled(默认YES,关联对象set && get)
<4> UIViewController (FDFullscreenPopGesture)
fd_interactivePopDisabled (关联对象get && set)
fd_prefersNavigationBarHidden (关联对象get && set)
fd_interactivePopMaxAllowedInitialDistanceToLeftEdge (关联对象get && set)
核心代码:
网友评论