如何实现UIView的执行contentStretch的逆? [英] How to implement UIView that performs inverse of contentStretch?

查看:138
本文介绍了如何实现UIView的执行contentStretch的逆?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

UIView的<一个href=\"https://developer.apple.com/library/ios/#DOCUMENTATION/UIKit/Reference/UIView_Class/UIView/UIView.html\"相对=nofollow> contentStretch 属性定义哪些内容的内部区域被拉伸到填充视图。这是伟大的,但是我发现自己需要完全相反的:我希望有一个观点,我可以定义不拉伸,但有外边缘伸展,直到填满视图一个矩形。

The UIView contentStretch property defines an area inside of which content is stretched to fill the view. That's great and all, but I find myself in need of the exact opposite: I want a view where I can define a rect that does not stretch but have the outer edges stretched until they fill the view.

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

所以在上面的观点,外矩形是我提出的观点。内部矩形是在非伸缩性的内容。理想的情况下,我想能够内的外矩形的任何地方非伸缩性内容的中心点的位置,仍然有外位填充至边缘。

So in the above view, the outer rect is my proposed view. The inner rect is the non-stretchable content. Ideally I would like to be able to position the center point of the non-stretchable content anywhere within the outer rect and still have the outer bits fill to the edge.

用法示例场景:一个黑色的覆盖与后面探索用手电筒等场景中的鼠标/触摸透明中心洞

Example usage scenario: a black overlay with a transparent center "hole" that follows the mouse / touch for exploring a scene with a flashlight, etc.

我想做到这一点的方法之一是适当地绘制内容的UIView,然后绘制其他四个视图,尺寸覆盖外部区域,但我希望为平滑的动画的单一视图的解决方案。我猜我需要有一个单一的UIView,并使用核心动画踏踏实实惹的图层?

One way I thought to do this is to draw the content UIView, then draw four other views, size appropriately, to cover the outer area, but I was hoping for a single-view solution for smoother animating. I'm guessing I'll need to have a single UIView and use Core Animation to get down and mess with its Layers?

推荐答案

所以,我的第一次尝试(在我的其他回答这个问题),在做一切的drawRect:,在我的iPad 2有点laggy我决定通过创建一个单独的的CALayer 为每个切片重做,并让核心动画担心缩放切片。

So my first attempt (in my other answer to this question), doing everything in drawRect:, was a little laggy on my iPad 2. I decided to redo it by creating a separate CALayer for each slice and let Core Animation worry about scaling the slices.

在这个版本中,工作是我的自定义的UIView 子类的 layoutSubviews 方法完成。系统调用 layoutSubviews 每当视图的尺寸变化(包括当视图第一次出现)。我们也可以要求系统中使用 setNeedsLayout 消息来调用 layoutSubviews 。喜欢它会合并得出的请求,系统将自动聚结布局的要求。

In this version, the work is done in the layoutSubviews method of my custom UIView subclass. The system calls layoutSubviews whenever the view's size changes (including when the view first appears). We can also ask the system to call layoutSubviews using the setNeedsLayout message. The system automatically coalesces layout requests like it coalesces draw requests.

我需要三个私有实例变量:

I'll need three private instance variables:

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

我的 layoutSubviews 方法开始处理无图像或无 fixedRect 的易情况:

My layoutSubviews method starts by handling the easy cases of no image or no 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;
    }

然后懒洋洋地创建九片层,如果我没有创建他们尚未:

Then it lazily creates the nine slice layers if I haven't created them yet:

    if (!_hasSlices)
        [self makeSlices];

据重新计算的片层图像如果图像已经改变。在 _imageDidChange 标志设置在 setImage:

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

最后,它设置每个九个片层的框架。

Finally, it sets the frame of each of the nine slice layers.

    [self setSliceFrames];
}

当然,所有的实际工作中发生的辅助方法,但他们pretty简单。隐藏切片(当没有图像或没有 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;
        }
    }
}

创建片层也是微不足道的:

Creating the slice layers is also trivial:

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

为了使切片图像,并设置片层框架,我需要从我的其他答案 RECT 辅助函数:

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

创建切片图像需要一点的工作,因为我必须来计算片之间的界限的坐标。我使用 CGImageCreateWithImageInRect 从用户提供的图像创建切片图像。

Creating the slice images requires a little work, because I have to compute the coordinates of the boundaries between the slices. I use CGImageCreateWithImageInRect to create the slice images from the user-supplied image.

- (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[4] = { 0, fixedRect.origin.x, CGRectGetMaxX(fixedRect), CGImageGetWidth(cgImage) };
    CGFloat ys[4] = { 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[y][x].contents = (__bridge id)imageSlice;
            CGImageRelease(imageSlice);
        }
    }
}

设置片层框架是相似的,虽然我没有在这里处理图像比例。 (也许我应该使用 UIScreen 扩展?嗯,这是令人困惑。我还没有尝试过视网膜设备上。)

Setting the slice layer frames is similar, although I don't have to handle the image scale here. (Maybe I should be using the UIScreen scale? Hmm. It's confusing. I haven't tried it on a Retina device.)

- (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更加顺畅它可能旧设备上工作得很好了。

This version seems much smoother on my iPad 2. It might work very well on older devices too.

这我的测试项目中的版本是在github太

This version of my test project is on github too.

这篇关于如何实现UIView的执行contentStretch的逆?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆