ios - 如何实现执行 contentStretch 反转的 UIView?
<p><p>UIView <a href="https://developer.apple.com/library/ios/#DOCUMENTATION/UIKit/Reference/UIView_Class/UIView/UIView.html" rel="noreferrer noopener nofollow">contentStretch</a>属性定义了一个区域,在该区域内内容被拉伸(stretch)以填充 View 。这很好,但我发现自己需要完全相反的东西:我想要一个 View ,我可以定义一个不拉伸(stretch)但外边缘拉伸(stretch)直到填满 View 的矩形。 </p>
<pre><code>+-------------------------------------------+
| |
| +----------+ |
| | | |
| | | |
| |Content | Stretch to fill |
| | | |
| | | |
| +----------+ |
| |
+-------------------------------------------+
</code></pre>
<p>所以在上面的 View 中,外部矩形是我提出的 View 。内部矩形是不可拉伸(stretch)的内容。理想情况下,我希望能够将不可拉伸(stretch)内容的中心点定位在外部矩形内的任何位置,并且仍然让外部位填充到边缘。</p>
<p>示例使用场景:带有透明中心“孔”的黑色覆盖层,跟随鼠标/触摸,用于使用手电筒等探索场景。</p>
<p>我想这样做的一种方法是绘制内容 UIView,然后绘制其他四个 View ,大小适当,以覆盖外部区域,但我希望有一个单 View 解决方案来实现更流畅的动画效果。我猜我需要一个 UIView 并使用 Core Animation 来弄乱它的图层? </p></p>
<br><hr><h1><strong>Best Answer-推荐答案</ strong></h1><br>
<p><p>所以我的第一次尝试(在我对这个问题的另一个回答中),在 <code>drawRect:</code> 中做所有事情,在我的 iPad 2 上有点滞后。我决定通过创建一个单独的 <code>CALayer</code> 每个切片,让 Core Animation 担心缩放切片。</p>
<p>在这个版本中,工作是在我的自定义 <code>UIView</code> 子类的 <code>layoutSubviews</code> 方法中完成的。只要 View 的大小发生变化(包括 View 首次出现时),系统就会调用 <code>layoutSubviews</code>。我们还可以使用 <code>setNeedsLayout</code> 消息要求系统调用 <code>layoutSubviews</code>。系统会自动合并布局请求,就像合并绘图请求一样。</p>
<p>我需要三个私有(private)实例变量:</p>
<pre><code>@implementation OutstretchView {
CALayer *_slices;
BOOL _imageDidChange : 1;
BOOL _hasSlices : 1;
}
</code></pre>
<p>我的 <code>layoutSubviews</code> 方法从处理没有图像或没有 <code>fixedRect</code> 的简单情况开始:</p>
<pre><code>- (void)layoutSubviews {
;
if (!self.image) {
;
self.layer.contents = nil;
return;
}
if (CGRectIsNull(self.fixedRect)) {
;
self.layer.contents = (__bridge id)self.image.CGImage;
return;
}
</code></pre>
<p>如果我还没有创建九个切片层,它会懒惰地创建它们:</p>
<pre><code> if (!_hasSlices)
;
</code></pre>
<p>如果图像发生变化,它会重新计算切片层的图像。 <code>_imageDidChange</code> 标志在 <code>setImage:</code> 中设置。</p>
<pre><code> if (_imageDidChange) {
;
_imageDidChange = NO;
}
</code></pre>
<p>最后,它设置九个切片层中每一层的框架。</p>
<pre><code> ;
}
</code></pre>
<p>当然,所有实际工作都发生在辅助方法中,但它们非常简单。隐藏切片(当没有图像或没有 <code>fixedRect</code> 时)很简单:</p>
<pre><code>- (void)hideSlices {
if (!_hasSlices)
return;
for (int y = 0; y < 3; ++y) {
for (int x = 0; x < 3; ++x) {
_slices.hidden = YES;
}
}
}
</code></pre>
<p>创建切片层也很简单:</p>
<pre><code>- (void)makeSlices {
if (_hasSlices)
return;
CALayer *myLayer = self.layer;
for (int y = 0; y < 3; ++y) {
for (int x = 0; x < 3; ++x) {
_slices = ;
];
}
}
_hasSlices = YES;
}
</code></pre>
<p>要制作切片图像并设置切片图层帧,我需要其他答案中的 <code>rect</code> 辅助函数:</p>
<pre><code>static CGRect rect(CGFloat *xs, CGFloat *ys) {
return CGRectMake(xs, ys, xs - xs, ys - ys);
}
</code></pre>
<p>创建切片图像需要一些工作,因为我必须计算切片之间边界的坐标。我使用 <code>CGImageCreateWithImageInRect</code> 从用户提供的图像创建切片图像。</p>
<pre><code>- (void)setSliceImages {
UIImage *image = self.image;
CGImageRef cgImage = image.CGImage;
CGFloat scale = image.scale;
CGRect fixedRect = self.fixedRect;
fixedRect.origin.x *= scale;
fixedRect.origin.y *= scale;
fixedRect.size.width *= scale;
fixedRect.size.height *= scale;
CGFloat xs = { 0, fixedRect.origin.x, CGRectGetMaxX(fixedRect), CGImageGetWidth(cgImage) };
CGFloat ys = { 0, fixedRect.origin.y, CGRectGetMaxY(fixedRect), CGImageGetHeight(cgImage) };
for (int y = 0; y < 3; ++y) {
for (int x = 0; x < 3; ++x) {
CGImageRef imageSlice = CGImageCreateWithImageInRect(cgImage, rect(xs + x, ys + y));
_slices.contents = (__bridge id)imageSlice;
CGImageRelease(imageSlice);
}
}
}
</code></pre>
<p>设置切片层帧是类似的,虽然我不必在这里处理图像比例。 (也许我应该使用 <code>UIScreen</code> 比例尺?嗯。这令人困惑。我没有在 Retina 设备上尝试过。)</p>
<pre><code>- (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 = { bounds.origin.x, fixedRect.origin.x, CGRectGetMaxX(fixedRect), CGRectGetMaxX(bounds) };
CGFloat ys = { 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.frame = rect(xs + x, ys + y);
}
}
}
</code></pre>
<p>这个版本在我的 iPad 2 上似乎更流畅。它可能在旧设备上也能很好地工作。</p>
<p>我的测试项目的这个版本是<a href="https://github.com/mayoff/stackoverflow-OutstretchView" rel="noreferrer noopener nofollow">on github too</a> .</p></p>
<p style="font-size: 20px;">关于ios - 如何实现执行 contentStretch 反转的 UIView?,我们在Stack Overflow上找到一个类似的问题:
<a href="https://stackoverflow.com/questions/9493337/" rel="noreferrer noopener nofollow" style="color: red;">
https://stackoverflow.com/questions/9493337/
</a>
</p>
页:
[1]