2011-02-07 6 views
0

많은 UIView 하위 뷰가 포함 된 UIScrollView 내에 UIView가 있습니다. 각 하위 뷰에는 CATiledLayer 레이어가 있습니다. 또한 컨텍스트 내에서 컨테이너 UIView (모든 하위 뷰 포함)를 그리는 돋보기 확대 기능이 있습니다. 관련 코드 :CATiledLayer 레이어를 사용하여 하위 뷰가 포함 된 UIView 드로잉을 최적화하는 방법

이는 확대경의 drawRect 방법입니다 :

- (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]; 
} 

그리고 이것은 drawInContext입니다 : 파단이 그려 컨테이너에 UIView의 forRect 방법 :

- (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]; 
} 

그리고 마지막으로 이것은 CATiledLayer가있는 서브 뷰의 drawRect 메소드입니다.

- (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]; 
     } 
    } 
} 

N 아, 모든 것이 내가 의도 한대로 작동하지만 앱이 확대 루페가 켜져 움직일 때 상당히 느려집니다. 문제는 loupe보기가 이동 될 때마다 drawRect 메서드가 호출되므로 (확대 된 내용을 업데이트 할 수 있음) 컨테이너 UIView의 drawInContext 메서드 등을 호출하여 모든 CATiledLayers가 이미지를 업데이트하도록하는 것입니다. 루페가 움직일 때마다 타일.

위에서 볼 수 있듯이, 나는 루페의 컨텍스트 내에서 컨테이너 뷰의 더 큰 부분을 그렸지만, 이것이 내가 붙어있는 부분이다. 이 컨테이너보기의 더 큰 부분을 어떻게 "버퍼링"할 수 있는지를 알 수 없으므로 루페가 이동하면 다시 그려지는 rect가 "버퍼 된"rect를 넘어선 경우에만 서브 뷰가 다시 그려집니다.

죄송합니다. 코드가 엉성한/newby 인 경우 - 나는 중간에 있는데 도움이 필요합니다.

감사합니다.

+0

나는 @canfan과 같은 문제에 직면 해있다. 어떻게이 일을 끝내셨습니까? –

답변

1

확대경/확대경보기에 표시되는 항목을 한 번 미리 렌더링 할 수 있습니까? 확대하려는 디스플레이가 상대적으로 정적이거나 끊임없이 변화하는지 확실하지 않습니다.

내 앱에서 사용자는 화면을 탭하여 다른보기를 배치합니다. 그들이 손가락을 움직이면 &을 터치하면 돋보기보기가 항목이 배치 될 위치를 정확히 보여줍니다. 또한 돋보기를 적극적으로 사용하는 동안 배경 이미지가 변경되지 않습니다 (돋보기가 켜져있는 동안이 아닌 다른 시점에보기를 이동/확대/이동시킬 수 있음).

내가 확대 한 디스플레이가 확대 중에 고정되어 있기 때문에 'draw'메서드를 무시하지 않는 부분 확대를 구현할 수있었습니다. 아래는 전체 구현입니다. ViewController/touch 핸들러에 의해 인스턴스화되고 지속됩니다.

아래의 트릭은 전체 창에 대한 스크린 샷을 찍은 다음이를 루퍼의 layer.contents에 제공하는 것입니다. 그런 다음 자르기 위해 layer.contentsRect을 사용하고, 확대 &은 터치 포인트에서 스크린 샷을 위치시킵니다.발신자의 touchesBegin 통화에서

  • [_magnifier magnify...
  • 그리고 마지막으로 touchesMoved 전화에서 [_magnifier touchPointMovedTo...
  • , touchesEnded 전화 [_magnifier removeFromSuperview];

MagnifierView.h

#import <UIKit/UIKit.h> 
@interface MagnifierView : UIView { 
    float _xscale; 
    float _yscale; 
    float _loupewidth; 
    float _loupeheight; 
    float _xzoom; 
    float _yzoom; 
} 

- (void) magnify:(CGPoint) touchPoint; 
- (void) touchPointMovedTo:(CGPoint) touchPoint; 
@end 

MagnifierView.m

#import "MagnifierView.h" 
#import <QuartzCore/QuartzCore.h> 

#define kMagnifierDiameter 120 

@implementation MagnifierView 

- (id)initWithFrame:(CGRect)frame { 
    self = [super initWithFrame:CGRectMake(0, 0, kMagnifierDiameter, kMagnifierDiameter)]; 

    if (self) { 
     self.layer.borderColor = [[UIColor darkGrayColor] CGColor]; 
     self.layer.borderWidth = 3; 
     self.layer.cornerRadius = kMagnifierDiameter/2; 
     self.layer.masksToBounds = YES; 


     UIImageView *crosshair = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"crosshairs.png"]]; 
     [crosshair setCenter:self.center]; 
     [self addSubview:crosshair]; 
     [crosshair release]; 
    } 
    return self; 
} 

- (void) magnify:(CGPoint)touchPoint 
{ 
    UIView *windowview = [[UIApplication sharedApplication] keyWindow]; 

    /// grab a screenshot of the window (sloppy; goal is to get a CGImageRef to put into this view's layer) 
    UIGraphicsBeginImageContext(windowview.bounds.size); 
    CGContextRef context = UIGraphicsGetCurrentContext(); 
    [windowview.layer renderInContext:context]; 
    UIImage *screenshot = UIGraphicsGetImageFromCurrentImageContext(); /// autoreleased 
    UIGraphicsEndImageContext(); 


    /// while we have the image size update our positioning and zooming numbers 
    _xscale = 1/screenshot.size.width; /// layer units are 0.0 - 1.0 so scale the numbers to that range 
    _yscale = 1/screenshot.size.height; 
    _loupewidth = _xscale * CGRectGetWidth(self.frame);  /// scaled size of this view 
    _loupeheight = _yscale * CGRectGetHeight(self.frame); 
    _xzoom = (_xscale * 18); /// arbitrary 16; make sure it's in scaled units (negative to zoom out) 
    _yzoom = (_yscale * 18); 


    /// set our layer contents to the imageRef of the screenshot above (again, sloppy; can we skip UIImage altogether?) 
    CGImageRef imageRef = CGImageCreateWithImageInRect([screenshot CGImage], CGRectMake(0, 0, screenshot.size.width, screenshot.size.height)); 
    self.layer.contents = (id)imageRef; 
    CGImageRelease(imageRef); 

    /// add us to the window view and move us 
    [windowview addSubview:self]; 
    [self touchPointMovedTo:touchPoint]; 
} 

- (void) touchPointMovedTo:(CGPoint)touchPoint 
{ 
    self.center = CGPointMake(touchPoint.x, touchPoint.y - 80); /// arbitrary 80 so we float 'above' your fat finger 

    /// touchPoint is the center; figure out the x,y (top,left) 
    float xcenter = (_xscale * touchPoint.x); 
    float x = xcenter - (_loupewidth/2); 

    float ycenter = (_yscale * touchPoint.y); 
    float y = ycenter - (_loupeheight/2); 

    /// with no additional adjustments this rect will just 'see through' to the screenshot 
    CGRect seethrough = CGRectMake(x , y, _loupewidth, _loupeheight); 

    /// RectInset with the zoom factor to scale up the contents 
    self.layer.contentsRect = CGRectInset(seethrough, _xzoom, _yzoom); 

} 


- (void)dealloc { 
    [super dealloc]; 
} 


@end 
+0

코드는 더글러스 (Douglas)를 잘 사용합니다. 단, 십자형 사각형은 사용자가 손가락을 가리키고있는 영역과 정확히 일치하지 않습니다. 나는 x와 y가 계산 된다고 생각하지 않는다. 나는 정말로 약간의 도움에 감사 할 것이다. –