How to optimize drawing of a UIView that contains subviews with CATiledLayer layers
I have a UIView within a UIScrollView that contains many UIView subviews. Each of these subviews has a CATiledLayer layer. In addition, I have a magnifying loupe functionality that draws the container UIView within its context (along with all the subviews). The relevant code:
This is drawRect method of the loupe:
- (void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextClipToMask( context , loupeRect, self.maskImage);
CGContextSetFillColorWithColor(context, [[UIColor whiteColor] CGColor]);
CGContextFillRect(context, loupeRect);
CGContextSaveGState( context );
CGContextScaleCTM(context, gridScale, gridScale);
CGContextTranslateCTM(context, offset.x, offset.y);
CGRect rectToDraw = CGRectMake(-offset.x, -offset.y, 512, 512);
[appDelegate.gridViewController.objectContainerView drawInContext:context forRect:rectToDraw];
CGContextRestoreGState( context );
[overlayImage drawAtPoint:CGPointZero];
}
And this is the drawInContext:forRect method of the container UIView where the subviews are drawn:
- (void)drawInContext:(CGContextRef)ctx forRect:(CGRect)rect {
CGRect newrect = CGRectMake(rect.origin.x-1024, rect.origin.y-1024, 2048, 2048);
for (UIView* v in [self subviews]) {
float viewscale = v.transform.a;
if (CGRectIntersectsRect(newrect,v.frame)) {
CGContextSaveGState(ctx);
CGContextScaleCTM(ctx, viewscale, viewscale);
CGContextTranslateCTM(ctx, v.frame.origin.x/viewscale, v.frame.origin.y/viewscale);
[v drawLayer:v.layer inContext:ctx];
CGContextRestoreGState(ctx);
}
}
[super drawLayer:self.layer inContext:ctx];
}
And finally this is the drawRect method of the subviews with CATiledLayer:
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
CGFloat scale = CGContextGetCTM(context).a;
scale = (scale <= .125) ? .125 : (scale <= .250 ? .250 : (scale <= .5 ? .5 : 1));
CATiledLayer *tiledLayer = (CATiledLayer *)[self layer];
CGSize tileSize = tiledLayer.tileSize;
tileSize.width /= scale;
tileSize.height /= scale;
int firstCol = floorf(CGRectGetMinX(rect) / tileSize.width);
int lastCol = floorf((CGRectGetMaxX(rect)-1) / tileSize.width);
int firstRow = floorf(CGRectGetMinY(rect) / tileSize.height);
int lastRow = floorf((CGRectGetMaxY(rect)-1) / tileSize.height);
for (int row = firstRow; row <= lastRow; row++) {
for (int col = firstCol; col <= lastCol; col++) {
UIImage *tile = [self tileForScale:scale row:row col:col];
CGRect tileRect = CGRectMake(tileSize.width * col, tileSize.height * row,
tileSize.width, tileSize.height);
tileRect = CGRectIntersection(self.bounds, tileRect);
[tile drawInRect:tileRect];
}
}
}
Now, everything works that way I intended, however the app significantly slows down when the magnifying loupe is on and is being moved. The issue is that everytime the loupe view is moved, its drawRect method is called (so it can update the magnified contents), which subsequently calls the drawInContext method of the container UIView and so forth... resulting in all the CATiledLayers updating their image tiles everytime the loupe is moved.
As you can see I've attempted to draw a larger portion of the container view within the context of the loupe but this is where I'm stuck. I can't see how I can "buffer" a larger portion of this container view so when the loupe is moved, the subviews are redrawn only if the rect to be redrawn goes beyond the "buffered" rect.
Sorry if the code is sloppy/newby - I'm in middle of it and looking for some help.
Thanks!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
Can you do a one time pre-render of anything the loupe/magnifier view will display? I'm not sure if the display you are trying to magnify is relatively static or constantly changing.
In my app the user taps the screen to place other views. As they touch & move their finger a magnifier view shows exactly where the item will be placed. Also, while they are actively using the magnifier the background image does not change (they may pan/zoom/move the views at other times just not while the magnifier happens to be on).
Since the display I'm magnifying is static during magnification, I was able to implement a loupe that does not override the 'draw' methods. Below is the entire implementation; it is instantiated and persisted by the ViewController/touch handler.
The trick below is to take a screenshot of the entire window and feed that to the loupe's
layer.contents
. Then use thelayer.contentsRect
to crop, zoom & position the screenshot at the touch point.touchesBegin
call[_magnifier magnify...
touchesMoved
call[_magnifier touchPointMovedTo...
touchesEnded
calls[_magnifier removeFromSuperview];
MagnifierView.h
MagnifierView.m