2012-02-29 3 views
2

UIView contentStretchプロパティは、内容を拡大してビューを満たす領域を定義します。それは素晴らしく、すべてですが、私は自分自身が正反対であることを求めています。私は、ストレッチを行わず、ビューを埋めるまで外側の辺を引き伸ばした矩形を定義できるビューが必要です。contentStretchの逆を実行するUIViewを実装する方法は?

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

上記の図では、外部レットは私の提案するビューです。インナーレットは非伸縮性のコンテンツです。理想的には、非伸縮性のコンテンツの中心点を外側の矩形内の任意の場所に配置し、外側のビットをエッジに塗りつぶしたいと思っています。

例の使用シナリオ:懐中電灯でシーンを探索するためのマウス/タッチを以下の透明センター「穴」と黒オーバーレイなど私はこれを行うには考え

一つの方法は、コンテンツを描画することですUIViewを使用して、他の4つのビュー、適切なサイズを描画して外側の領域をカバーしますが、よりスムーズなアニメーションを作成するための単一ビューのソリューションが必要でした。私は、単一のUIViewを持つ必要があり、コアアニメーションを使用してレイヤーをダウンして混乱させる必要があると思いますか?

+1

うわー、優秀な質問...私の思考プロセスは、それを偽造することです。黒いオーバーレイを追加しますが、UIViewには、実際には黒いオーバーレイの下にあるimageViewの部分だけを投影しています。 – CodaFi

+0

しかし、下の部分にどのように黒いオーバーレイが見えますか?コンテンツrectに透明なビットがあるとします。 –

+0

拡大表示するコンテンツとしてUIViewを受け入れる虫眼鏡のビューへのリンクです。私は実装を研究していないので、具体的な記述はできませんが、約束どおりに動作します:https://github.com/acoomans/iOS-MagnifyingGlass – CodaFi

答えて

2

ので(この質問への私の他の答えで)私の最初の試みは、drawRect:のすべてをやって、私はスライスごとに別々のCALayerを作成することによって、それを再実行し、Core Animationの心配を聞かせすることを決めた私のiPad 2にはほとんどラグでしたスライスのスケーリングについて

このバージョンでは、私のカスタムUIViewサブクラスのlayoutSubviewsメソッドで作業が行われます。ビューのサイズが変更されると(ビューが最初に表示されるときを含む)、システムはlayoutSubviewsを呼び出します。 setNeedsLayoutメッセージを使用してlayoutSubviewsに電話するようにシステムに依頼することもできます。システムは自動的に描画要求を結合するようにレイアウト要求を結合します。

私は3つのプライベートインスタンス変数必要があります

@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; 
    } 

は、それから、いい加減に9つのスライスを作成しますまだ作成していない場合はレイヤー:

if (!_hasSlices) 
     [self makeSlices]; 

イメージが変更された場合、スライスレイヤのイメージが再計算されます。 フラグは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; 
} 

スライス画像を作成し、スライス層のフレームを設定するには、I 「私の他の回答からrectヘルパー関数を必要とするでしょう:私はOの座標を計算する必要があるためスライス画像を作成する

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

は、少し作業が必要ですスライス間の境界。私は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); 
     } 
    } 
} 

このバージョンでは、それは上の非常によく働くかもしれない私のiPad 2上でよりスムーズなようです古いデバイスも。

このテストプロジェクトのバージョンはon github tooです。

1

おそらく4つの他のビューは必要ありません.8つが必要です。 9つのスライスのそれぞれは、水平方向および垂直方向のスケーリングの異なる組み合わせを必要とし得ること

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

注意:インナー「固定」矩形は、9つのスライスに視野を分割します。それはこのように見える終わる:とにかく

outside stretching demo

、私はそれだけでdrawRect:をオーバーライドするUIViewサブクラスを作成することにより、これを行うにはあまりにも難しいことではないだろうと思いました。

私は私のサブクラスOutstretchViewを命名し、それを3つのプロパティを与えた:

@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); 
} 

私はまた、2つのX座標の配列と2つのY座標の配列からCGRectを作成する小さなヘルパーメソッドを使用します:

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

これらのヘルパーでは、drawRect:を実装できます。

CGPoint imageFixedCenter = self.fixedCenter; 
    CGRect viewFixedRect = CGRectOffset(imageFixedRect, imageFixedCenter.x - imageFixedRect.size.width/2, imageFixedCenter.y - imageFixedRect.size.height/2); 
を:私は、画像の固定部分が描画される場所ビューの矩形座標計算

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

:最初私は描画する画像、または全く定義fixedRectないかが存在しない簡単なケースを扱います

次に、ビュー座標空間内のスライスの辺を定義する4つの(4つの)X座標の配列と、Y座標の同様の配列と、イメージ座標空間の座標の2つの配列:

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

それは私のiPad 2

私のテストプロジェクトのこのバージョンはon githubあるにはほとんどラグです。

関連する問題