问题
On Android I have item views with potentially large widths on a horizontal scrolling list. The view loads and draws chunks "images" as parts of the view become visible on the list. This is an optimization to avoid drawing all images at once as it would be wasteful and slow. The content drawn is essentially an audio waveform. Do to the ways things need to work I can't split the chunks as individual view items on a list. This strategy works perfect because of how android drawing architecture works.
Now I'm trying to use a similar pattern in iOS but I'm having some trouble and I'm not too sure how to come up with a solution.
In iOS I'm are using a UICollectionView
which draws large width Cells and we need that same optimization of loading and drawing only the visible chunks.
Solution 1:
Check what parts of the UIView
is visible and draw only those visible chunks. The problem with this solution is that as the UICollectionView
scrolls the UIView
doesn't draw the next chucks that are becoming visible. Here are examples of what I'm talking about.
UIView
loads initial chunks
These can be seen by their different chunk colors:
Scroll a bit
Black is shown and no content is loaded because there is no hint that the view needs to draw again so we can't load the next visible chunk.
Solution 2:
Use custom UIView
backed by a CATiledLayer
. This works perfect because it draws the tiles as they become visible while scrolling the UICollectionView
.
The problem is that drawing happens on the background thread or on the next drawing cycle if shouldDrawOnMainThread
is defined. This brings issues when the UIView
is resized or my internal zoom logic kicks. Things shift because the drawing cycle is not synchronized to the view resizing.
So how could I get notified like CATiledLayer
that a part is becoming visible and I could draw normally like a CALayer
backed UIView
?
UPDATE 1
I'm looking into using preferredLayoutAttributesFittingAttributes
as a way to check if a new chunk need to be drawn. This is called every time the cell is moved into a new position because of scrolling. Hopefully, this isn't a bad idea. :)
- (UICollectionViewLayoutAttributes *)preferredLayoutAttributesFittingAttributes:(UICollectionViewLayoutAttributes *)layoutAttributes
UPDATE 2
After much testing and playing around. Using solution 1 is not an option. When a UIView
reaches a huge width memory usage goes off the roof while using CATiledLayer
memory usage is at minimal as I guess one would expect. :/
回答1:
I don't know if you can completely turn off the effect. You can soften it by setting the fadeDuration to 0. To do this, you must create a subclass of CATiledLayer
and overwrite the class method fadeDuration
.
You can also try to implement shouldDrawOnMainThread
class method, but this is a private method, and you're risking a rejection on the App Store:
@implementation MyTiledLayer
+(CFTimeInterval)fadeDuration {
return 0.0;
}
+ (BOOL)shouldDrawOnMainThread {
return YES;
}
@end
or in Swift:
class MyTiledLayer: CATiledLayer {
override class func fadeDuration() -> CFTimeInterval {
return 0.0
}
class func shouldDrawOnMainThread() -> Bool {
return true;
}
}
If you want to implement your own tiled layer instead, you should waive of the tiles and try to compute the visible part (section and zoom level) of the view. Than you can draw only this part as layer whole content.
Apparently, there is no easy way to re-draw the view when the scrollview is scrolling. But you may implement scrollViewDidScroll:
, and trigger the redrawing manually. The visibleRect
method always returns the complete area of the layer, but you can use
CGRect theVisibleRect = CGRectIntersection(self.frame, self.superlayer.bounds);
or in Swift
let theVisibleRect = frame.intersection(superlayer.bounds)
to determine the visible area of the layer.
来源:https://stackoverflow.com/questions/48707815/how-to-draw-parts-of-a-uiview-as-it-becomes-visible-inside-uicollectionview