2012-02-29 3 views
2

UIView contentStretch 속성은 내용을 확장하여 뷰를 채울 영역을 정의합니다. 그것은 위대하고 모든 것이지만 나는 정확한 반대가 필요하다고 생각합니다. 나는 스트레치를하지 않고 뷰를 채울 때까지 바깥 쪽 가장자리가 늘어나는 rect를 정의 할 수있는 뷰를 원합니다.contentStretch의 역함수를 수행하는 UIView를 구현하는 방법은 무엇입니까?

+-------------------------------------------+ 
    |           | 
    |   +----------+      | 
    |   |   |      | 
    |   |   |      | 
    |   | Content |  Stretch to fill | 
    |   |   |      | 
    |   |   |      | 
    |   +----------+      | 
    |           | 
    +-------------------------------------------+ 

위의보기에서 바깥 쪽 rect는 내 제안 된보기입니다. 안쪽 rect는 non-stretchable 내용입니다. 이상적으로는 외부 rect 내 어디에서나 비 신축성 컨텐트의 중심점을 배치 할 수 있고 가장자리에 외부 비트가 채워지도록하고 싶습니다.

사용 예제 시나리오 : 나는 이렇게 생각 등 손전등 장면을 탐험 마우스/터치를 다음과 투명한 센터 "구멍"

방법 중 하나와 검은 색 오버레이 내용을 그릴 것입니다 UIView, 다음 네 가지 다른보기, 크기를 적절하게 그려서 바깥 쪽 영역을 감싸고 있지만 더 부드러운 애니메이션을위한 단일보기 솔루션이 필요했습니다. 하나의 UIView가 있어야하고 Core Animation을 사용하여 레이어를 망가 뜨려야 할 필요가 있습니다.

+1

와우, 탁월한 질문 ​​... 내 생각 과정은 가짜 일 것입니다. 검정색 오버레이를 추가하지만 UIView는 검은 오버레이 아래에 imageView라고 말하는 부분을 실제로 투영하는 방식을 따릅니다. – CodaFi

+0

하지만 아래 부분에 검은 색 오버레이가 어떻게 보이나요? 내용 rect에 투명 비트가 있다고 가정합니다. –

+0

다음은 확대 할 내용으로 UIViews를 허용하는 돋보기 뷰 링크입니다. 구현을 연구하지 않아 구체적인 정보를 제공 할 수는 없지만 약속대로 작동합니다. https://github.com/acoomans/iOS-MagnifyingGlass – CodaFi

답변

2

그래서 drawRect:에서 모든 일을 (이 질문에 대한 내 다른 대답) 내 첫 번째 시도는, 내 아이 패드 2에 약간 랙이 있었다 나는 각 슬라이스에 대해 별도의 CALayer를 작성하여 다시 실행 및 코어 애니메이션 걱정을하게하기로 결정 조각을 크기 조정하는 방법.

이 버전에서는 내 맞춤형 UIView 하위 클래스의 layoutSubviews 방법으로 작업이 수행됩니다. 보기의 크기가 변경 될 때마다 (보기가 처음 나타날 때 포함) 시스템에서 layoutSubviews을 호출합니다. setNeedsLayout 메시지를 사용하여 layoutSubviews으로 시스템에 문의 할 수도 있습니다. 시스템은 자동으로 그리기 요청을 통합하는 것처럼 레이아웃 요청을 통합합니다.

나는이 세 개인 인스턴스 변수가 필요합니다 :

@implementation OutstretchView { 
    CALayer *_slices[3][3]; 
    BOOL _imageDidChange : 1; 
    BOOL _hasSlices : 1; 
} 

layoutSubviews 방법은 어떤 이미지의 쉬운 사례를 처리하여 시작 또는 전혀 fixedRect :

- (void)layoutSubviews { 
    [super layoutSubviews]; 

    if (!self.image) { 
     [self hideSlices]; 
     self.layer.contents = nil; 
     return; 
    } 

    if (CGRectIsNull(self.fixedRect)) { 
     [self hideSlices]; 
     self.layer.contents = (__bridge id)self.image.CGImage; 
     return; 
    } 

는 그런 다음 게으르게 아홉 개 조각을 생성 아직 만들지 않은 경우 레이어 :

if (!_hasSlices) 
     [self makeSlices]; 

이미지가 변경된 경우 슬라이스 레이어의 이미지를 다시 계산합니다. _imageDidChange 플래그는 setImage:에 설정됩니다.

if (_imageDidChange) { 
     [self setSliceImages]; 
     _imageDidChange = NO; 
    } 

마지막으로 9 개의 슬라이스 레이어 각각의 프레임을 설정합니다.

[self setSliceFrames]; 
} 

물론 실제 작업은 모두 도우미 메서드에서 발생하지만 매우 간단합니다.(어떤 이미지 또는 전혀 fixedRect가 없을 때) 조각을 숨기는 것은 간단하다 : 슬라이스 레이어를 생성

- (void)hideSlices { 
    if (!_hasSlices) 
     return; 
    for (int y = 0; y < 3; ++y) { 
     for (int x = 0; x < 3; ++x) { 
      _slices[y][x].hidden = YES; 
     } 
    } 
} 

되고 또한 사소한 :

- (void)makeSlices { 
    if (_hasSlices) 
     return; 
    CALayer *myLayer = self.layer; 
    for (int y = 0; y < 3; ++y) { 
     for (int x = 0; x < 3; ++x) { 
      _slices[y][x] = [CALayer layer]; 
      [myLayer addSublayer:_slices[y][x]]; 
     } 
    } 
    _hasSlices = YES; 
} 

슬라이스 이미지를 만들고 슬라이스 층 프레임을 설정하려면, 나는 내가 가지고 있기 때문에 오 좌표를 계산하기 위해,

static CGRect rect(CGFloat *xs, CGFloat *ys) { 
    return CGRectMake(xs[0], ys[0], xs[1] - xs[0], ys[1] - ys[0]); 
} 

슬라이스 이미지를 만들기가 약간의 작업이 필요합니다 '는 내 다른 대답에서 rect 도우미 기능을 필요 것이다 슬라이스 간의 경계 나는 CGImageCreateWithImageInRect을 사용하여 사용자 제공 이미지에서 슬라이스 이미지를 만듭니다.

슬라이스 레이어 프레임을 설정하는 것은 비슷하지만 이미지 스케일은 여기에서 처리 할 필요가 없습니다. (어쩌면 내가 UIScreen 규모를 사용해야 하는가? 흠.. 나는 망막 장치에 그것을 시도하지 않은 혼란.)

- (void)setSliceFrames { 
    CGRect bounds = self.bounds; 
    CGRect fixedRect = self.fixedRect; 
    CGPoint fixedCenter = self.fixedCenter; 
    fixedRect = CGRectOffset(fixedRect, fixedCenter.x - fixedRect.size.width/2, fixedCenter.y - fixedRect.size.height/2); 
    CGFloat xs[4] = { bounds.origin.x, fixedRect.origin.x, CGRectGetMaxX(fixedRect), CGRectGetMaxX(bounds) }; 
    CGFloat ys[4] = { bounds.origin.y, fixedRect.origin.y, CGRectGetMaxY(fixedRect), CGRectGetMaxY(bounds) }; 

    for (int y = 0; y < 3; ++y) { 
     for (int x = 0; x < 3; ++x) { 
      _slices[y][x].frame = rect(xs + x, ys + y); 
     } 
    } 
} 

이 버전은 그것은 매우 잘 작동 할 수 있습니다 내 아이 패드 2에 훨씬 부드러워 보인다 오래된 장치들도.

이 테스트 프로젝트의 버전은 on github too입니다.

1

아마도 네 개의 다른보기가 필요하지 않을 것입니다. 여덟 개가 필요합니다. 아홉 개 슬라이스 각각 수평 및 수직 스케일링의 상이한 조합을 요구할 수있다

+----+-----+---------+ 
| |  |   | 
+----+-----+---------| 
| |fixed|   | 
| |  |   | 
+----+-----+---------+ 
| |  |   | 
| |  |   | 
| |  |   | 
+----+-----+---------+ 

주의 : 내측 "고정"사각형 조각으로 구 뷰를 분할한다. 그것은 다음과 같이 찾고 끝 :

outside stretching demo

을 어쨌든, 그냥 drawRect:을 재정의하는 UIView 하위 클래스를 만들어이 일을 너무 열심히하지 않을 거라 생각 했어요.

나는 나의 서브 클래스 OutstretchView 이름과 세 속성 주었다

@interface OutstretchView : UIView 

@property (nonatomic) UIImage *image; 
@property (nonatomic) CGRect fixedRect; // in image coordinates 
@property (nonatomic) CGPoint fixedCenter; // in view coordinates 

@end 

fixedRect 속성을 뻗어 안됩니다 이미지의 부분을 정의합니다. fixedCenter 속성은 뷰에서 이미지의 일부분을 그려야하는 위치를 정의합니다.

이미지의 9 개 슬라이스를 실제로 그리려면 이미지 좌표에서 CGRect 및 뷰 좌표에서 CGRect을 사용하고 뷰의 지정된 부분에 이미지의 지정된 부분을 그려주는 메서드를 정의하는 것이 유용합니다 . 나는 석영 2D의 클립을 사용하고 CTM은 "무거운"할 기능 :

- (void)drawRect:(CGRect)imageRect ofImage:(UIImage *)image inRect:(CGRect)viewRect { 
    CGContextRef gc = UIGraphicsGetCurrentContext(); 
    CGContextSaveGState(gc); { 
     CGContextClipToRect(gc, viewRect); 
     CGContextTranslateCTM(gc, viewRect.origin.x, viewRect.origin.y); 
     CGContextScaleCTM(gc, viewRect.size.width/imageRect.size.width, viewRect.size.height/imageRect.size.height); 
     CGContextTranslateCTM(gc, -imageRect.origin.x, -imageRect.origin.y); 
     [image drawAtPoint:CGPointZero]; 
    } CGContextRestoreGState(gc); 
} 

가 나는 또한 두 개의 X 좌표의 배열과 두 개의 Y 좌표의 배열에서 CGRect을 만드는 작은 도우미 메서드를 사용합니다을 :

static CGRect rect(CGFloat *xs, CGFloat *ys) { 
    return CGRectMake(xs[0], ys[0], xs[1] - xs[0], ys[1] - ys[0]); 
} 

drawRect:을 구현할 수 있습니다.그릴에 이미지, 또는 전혀 정의 fixedRect 중이없는 경우 우선 I은 쉽게 경우를 처리 : 다음

- (void)drawRect:(CGRect)dirtyRect { 
    UIImage *image = self.image; 
    if (!image) 
     return; 

    CGRect imageBounds = (CGRect){ CGPointZero, image.size }; 
    CGRect viewBounds = self.bounds; 

    CGRect imageFixedRect = self.fixedRect; 
    if (CGRectIsNull(imageFixedRect)) { 
     [image drawInRect:viewBounds]; 
     return; 
    } 

I는 뷰 사각형의 화상의 고정 된 부분이 인출되는 위치 좌표를 계산한다 :

CGPoint imageFixedCenter = self.fixedCenter; 
    CGRect viewFixedRect = CGRectOffset(imageFixedRect, imageFixedCenter.x - imageFixedRect.size.width/2, imageFixedCenter.y - imageFixedRect.size.height/2); 

다음에는 뷰 좌표 공간에서 슬라이스의 가장자리를 정의하는 4 개의 (네, 네 개의) X 좌표 배열과 Y 좌표에 대한 유사한 배열 및 이미지 좌표 공간에있는 좌표에 대한 두 개의 배열 :

CGFloat viewSlicesX[4] = { viewBounds.origin.x, viewFixedRect.origin.x, CGRectGetMaxX(viewFixedRect), CGRectGetMaxX(viewBounds) }; 
    CGFloat viewSlicesY[4] = { viewBounds.origin.y, viewFixedRect.origin.y, CGRectGetMaxY(viewFixedRect), CGRectGetMaxY(viewBounds) }; 
    CGFloat imageSlicesX[4] = { imageBounds.origin.x, imageFixedRect.origin.x, CGRectGetMaxX(imageFixedRect), CGRectGetMaxX(imageBounds) }; 
    CGFloat imageSlicesY[4] = { imageBounds.origin.y, imageFixedRect.origin.y, CGRectGetMaxY(imageFixedRect), CGRectGetMaxY(imageBounds) }; 

그리고 마지막으로 쉬운 부분, 드로잉 조각 :

for (int y = 0; y < 3; ++y) { 
     for (int x = 0; x < 3; ++x) { 
      [self drawRect:rect(imageSlicesX + x, imageSlicesY + y) ofImage:image inRect:rect(viewSlicesX + x, viewSlicesY + y)]; 
     } 
    } 
} 

그것은 내 아이 패드 2

내 테스트 프로젝트의이 버전은 on github 인에 약간 랙이 있습니다.

관련 문제