[TOC]
UIView之在屏幕上展示的百分比
前言
众所周知,屏幕上的视图们以树状结构一层层的加载,并最终显示在屏幕上。而如何判断视图是否展示呢?常见方法,是判断视图是否展示在了当前keyWindow上,但这样是否是正确的呢?答案,不够的。下面请跟随本文一起来追溯原因。
对比 UIWindow 查看显示区域
如下图所示,currentView有部分内容超出了父视图,而被隐藏。如果仅对比currentView在UIWindow上展示的区域,结果将会是全部展示。因此,想要精确的判断视图的展示比例,需要考虑currentView的层层父视图。
<div align="center">
<img src="http://or5n398vd.bkt.clouddn.com/UIWindow_error.png" width = "200" height = "200" alt="UITableView-VisibleCells" />
</div>
根据父视图类型,具体分析
视图本身(currentView)
打铁须得自身硬。
- self.hidden 是否隐藏
- self.alpha 是否透明
- self.size 是否尺寸为0
- self.superview 父视图是否存在
父视图(currentView.superview)
如下图,所示。需要计算当前视图在各级父视图上的展示情况。(self->self.superView->...->keyWindow)
- (CGFloat)yu_displayedPrecentInView:(UIView *)view;
<div align="center">
<img src="http://or5n398vd.bkt.clouddn.com/UIView_Precent1.png" width = "200" height = "200" alt="UITableView-VisibleCells" />
</div>
为节省篇幅,具体实现方法可参考我的另一篇文章
// 获取当前视图在view上展示的比例
UITableView、UICollectionView
由于UITableView、UICollectionView中Cell的复用机制,因此在判断Cell展示区域时,需要首先判断Cell是否在visibleCells中。
也就是需要知道Cell对应的IndexPath:
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0];
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
if (cell == nil) {
// An object representing a cell of the table, or nil if the cell is not visible or indexPath is out of range.
// 代表当前indexPath对应的Cell不可见,或者indexPath超出数据源的范围
}
另外需要额外注意的是,当UITableView 的frame区域超出屏幕时。例如,表单高度超出屏幕,底部cell不能显示在屏幕上,但仍被包含在visibleCells中。
此时,判断cell是否展示在屏幕上就需要考虑UITableView在屏幕上的展示区域,进而转换计算。
<div align="center">
<img src="http://or5n398vd.bkt.clouddn.com/UITableView-VisibleCells.png" width = "200" height = "200" alt="UITableView-VisibleCells" />
</div>
UIViewController.view
需要确保UIViewController.view本身全部展示在屏幕中。其中遇到以下情况:
- UIContainerViewController中,UIViewController可以看待成普通视图展示。
- UITabBarController包含了多个UIViewController。
- UINavigationViewController内部自适应时,UIViewController的展示区域从导航栏底部开始。详情见外链
结论:需要组合以上各情况计算显示情况
场景一:任意View获取在屏幕上的展示百分比 (父视图不包含UITableView、UICollectionView)
- 视图本身(currentView)
- 当前视图在各级父视图上的展示情况(self->self.superView->...->keyWindow)
- UIView 使用- (CGFloat)yu_displayedPrecentInView:(UIView *)view;方法计算。
综合以上各情况,求取最小值,即视图在屏幕上的展示情况。
场景二:任意View获取在屏幕上的展示百分比 (父视图包含UITableView、UICollectionView)
- 视图本身(currentView)
- 当前视图在各级父视图上的展示情况(self->self.superView->...->keyWindow)
- UIView 使用- (CGFloat)yu_displayedPrecentInView:(UIView *)view;方法计算 。
- UITableView 需要预先知道indexPath,判断所在Cell是否可见
- UICollectionView 需要预先知道indexPath,判断所在Cell是否可见
必须说明的是,IndexPath想要预先知道,就必须做出特出处理,不能简单的使用- (CGFloat)yu_displayedPrecentInView:(UIView *)view;方法计算。
综合以上各情况,求取最小值,即视图在屏幕上的展示情况。
场景三:在UITableView、UICollectionView滑动过程中,获取Cell在屏幕上展示的百分比
- 获取visibleCells
- 遍历每个Cell,并在屏幕上的展示百分比
此时,相对场景二,省却了锁定indexPath的事务,应用也更灵活一些。
注意事项
- UIViewController.viewDidAppear 方法执行完成之前,控制器上的视图在获取superView/nextResponder时,会发现获取到UIViewController之后为nil.这是由于,UIViewController尚未加载到UIWindow/UINaviationController上。