如何绘制UIView的部分,因为它在UICollectionView中变得可见?

Jon*_*ona 5 calayer uiview catiledlayer ios uicollectionview

在Android上,我在水平滚动列表上有可能有大宽度的项目视图.当视图的某些部分在列表中可见时,视图会加载并绘制块"图像".这是一种优化,以避免一次性绘制所有图像,因为它会浪费和缓慢.绘制的内容基本上是音频波形.做事情需要工作我不能将块拆分为列表中的单个视图项.由于android绘图架构的工作原理,这种策略非常有效.

现在我试图在iOS中使用类似的模式,但是我遇到了一些麻烦,我不太确定如何提出解决方案.

在iOS中,我正在使用UICollectionView绘制大宽度单元格的数据,我们需要同样优化加载和绘制可见的块.


解决方案1:
检查UIView可见的部分,并仅绘制那些可见的块.这个解决方案的问题在于,作为UICollectionView卷轴,UIView不会绘制下一个可见的卡盘.以下是我所谈论的例子.

UIView加载初始块
这些可以通过它们的不同块颜色看出: 在此输入图像描述

滚动
显示黑色显示并且没有加载任何内容,因为没有提示视图需要再次绘制,因此我们无法加载下一个可见的块. 在此输入图像描述


解决方案2:
使用UIView由a支持的自定义CATiledLayer.这非常有效,因为它会在滚动时绘制出可见的图块UICollectionView.

问题是如果shouldDrawOnMainThread定义了在后台线程或下一个绘图周期中进行绘制.这会在UIView调整大小或内部缩放逻辑启动时出现问题.由于绘图周期未与视图大小调整同步,因此事情发生了变化.


那么我怎么能得到通知,就像CATiledLayer一个部分变得可见,我可以正常画像一个CALayer支持UIView


更新1
我正在考虑使用preferredLayoutAttributesFittingAttributes一种方法来检查是否需要绘制新的块.每次由于滚动而将单元格移动到新位置时都会调用此方法.希望这不是一个坏主意.:)

- (UICollectionViewLayoutAttributes *)preferredLayoutAttributesFittingAttributes:(UICollectionViewLayoutAttributes *)layoutAttributes
Run Code Online (Sandbox Code Playgroud)

更新2
经过多次测试和玩耍.使用解决方案1不是一种选择.当UIView达到一个巨大的宽度时内存使用率下降,而使用CATiledLayer内存使用量是最小的,因为我猜想人们会期望.:/

cle*_*ens 2

不知道能不能完全关闭这个效果。您可以通过将 设为 0 来软化它fadeDuration。为此,您必须创建 的子类CATiledLayer并覆盖类方法fadeDuration

你也可以尝试实现shouldDrawOnMainThread类方法,但这是一个私有方法,你有被 App Store 拒绝的风险:

@implementation MyTiledLayer

+(CFTimeInterval)fadeDuration {
    return 0.0;
}

+ (BOOL)shouldDrawOnMainThread {
    return YES;
}

@end
Run Code Online (Sandbox Code Playgroud)

或在斯威夫特:

class MyTiledLayer: CATiledLayer {
    override class func fadeDuration() -> CFTimeInterval {
        return 0.0
    }

    class func shouldDrawOnMainThread() -> Bool {
        return true;
    }
}
Run Code Online (Sandbox Code Playgroud)

如果您想实现自己的平铺图层,则应该放弃平铺并尝试计算视图的可见部分(部分和缩放级别)。您可以仅将这部分绘制为图层整体内容。

显然,当滚动视图滚动时,没有简单的方法来重新绘制视图。但您可以实现scrollViewDidScroll:,并手动触发重绘。该visibleRect方法始终返回图层的完整区域,但您可以使用

CGRect theVisibleRect = CGRectIntersection(self.frame, self.superlayer.bounds);
Run Code Online (Sandbox Code Playgroud)

或在斯威夫特

let theVisibleRect = frame.intersection(superlayer.bounds)
Run Code Online (Sandbox Code Playgroud)

确定图层的可见区域。