ios 只需用矩形遮罩 UIView

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/11391058/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-30 19:11:43  来源:igfitidea点击:

Simply mask a UIView with a rectangle

iosuiviewmask

提问by Accatyyc

I want to know how to simply mask the visible area of a UIView of any kind. All the answers/tutorials I've read so far describe masking with an image, gradient or creating round corners which is way more advanced than what I am after.

我想知道如何简单地屏蔽任何类型的 UIView 的可见区域。到目前为止,我读过的所有答案/教程都描述了使用图像、渐变或创建圆角进行遮罩,这比我所追求的要先进得多。

Example: I have a UIView with the bounds (0, 0, 100, 100) and I want to cut away the right half of the view using a mask. Therefore my mask frame would be (0, 0, 50, 100).

示例:我有一个边界为 (0, 0, 100, 100) 的 UIView,我想使用遮罩切掉视图的右半部分。因此,我的蒙版框将是 (0, 0, 50, 100)。

Any idea how to do this simply? I don't want to override the drawrect method since this should be applicable to any UIView.

知道如何简单地做到这一点吗?我不想覆盖 drawrect 方法,因为这应该适用于任何 UIView。

I've tried this but it just makes the whole view invisible.

我试过这个,但它只会使整个视图不可见。

CGRect mask = CGRectMake(0, 0, 50, 100);
UIView *maskView = [[UIView alloc] initWithFrame:mask];
viewToMask.layer.mask = maskView.layer;

回答by Accatyyc

Thanks to the link from MSK, this is the way I went with which works well:

感谢 MSK 的链接,这是我使用的方式,效果很好:

// Create a mask layer and the frame to determine what will be visible in the view.
CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];
CGRect maskRect = CGRectMake(0, 0, 50, 100);

// Create a path with the rectangle in it.
CGPathRef path = CGPathCreateWithRect(maskRect, NULL);

// Set the path to the mask layer.
maskLayer.path = path;

// Release the path since it's not covered by ARC.
CGPathRelease(path);

// Set the mask of the view.
viewToMask.layer.mask = maskLayer;

回答by Eugene Tartakovsky

Thanks for answers guys.

谢谢各位大佬解答。

In case someone can't find suitable answer on SO for this question for hours, like i just did, i've assembled a working gist in Swift 2.2 for masking/clippingUIViewwith CGRect/UIBezierPath:

如果有人在这个问题上几个小时都找不到合适的答案,就像我刚刚做的那样,我已经在 Swift 2.2 中为masking/clippingUIViewCGRect/组装了一个工作要点UIBezierPath

https://gist.github.com/Flar49/7e977e81f1d2827f5fcd5c6c6a3c3d94

https://gist.github.com/Flar49/7e977e81f1d2827f5fcd5c6c6a3c3d94

extension UIView {

    func mask(withRect rect: CGRect, inverse: Bool = false) {
        let path = UIBezierPath(rect: rect)
        let maskLayer = CAShapeLayer()

        if inverse {
            path.append(UIBezierPath(rect: self.bounds))
            maskLayer.fillRule = kCAFillRuleEvenOdd
        }

        maskLayer.path = path.cgPath

        self.layer.mask = maskLayer
    }

    func mask(withPath path: UIBezierPath, inverse: Bool = false) {
        let path = path
        let maskLayer = CAShapeLayer()

        if inverse {
            path.append(UIBezierPath(rect: self.bounds))
            maskLayer.fillRule = kCAFillRuleEvenOdd
        }

        maskLayer.path = path.cgPath

        self.layer.mask = maskLayer
    }
}

Usage:

用法:

let viewSize = targetView.bounds.size
let rect = CGRect(x: 20, y: 20, width: viewSize.width - 20*2, height: viewSize.height - 20*2)

// Cuts rectangle inside view, leaving 20pt borders around
targetView.mask(withRect: rect, inverse: true)

// Cuts 20pt borders around the view, keeping part inside rect intact
targetView.mask(withRect: rect)

Hope it will save someone some time in the future :)

希望它会在将来为某人节省一些时间:)

回答by Geri Borbás

No need any mask at all. Just put it into a wrapper viewwith the smaller frame, and set clipsToBounds.

根本不需要任何口罩。只需将其放入具有较小框架的包装视图中,并设置clipsToBounds

wrapper.clipsToBounds = YES;

回答by Dan Rosenstark

Very simple example in a Swift ViewController, based on the accepted answer:

很简单的例子在斯威夫特的ViewController,根据公认的答案:

import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        let redView = UIView(frame: view.bounds)
        view.addSubview(redView)

        view.backgroundColor = UIColor.green
        redView.backgroundColor = UIColor.red

        mask(redView, maskRect: CGRect(x: 50, y: 50, width: 50, height: 50))
    }

    func mask(_ viewToMask: UIView, maskRect: CGRect) {
        let maskLayer = CAShapeLayer()
        let path = CGPath(rect: maskRect, transform: nil)
        maskLayer.path = path

        // Set the mask of the view.
        viewToMask.layer.mask = maskLayer
    }
}

Output

输出

enter image description here

在此处输入图片说明

回答by A-Live

An optional layer whose alpha channelis used as a mask to select between the layer's background and the result of compositing the layer's contents with its filtered background.

@property(retain) CALayer *mask

一个可选图层,其Alpha 通道用作蒙版,以在图层背景和图层内容与其过滤背景的合成结果之间进行选择。

@property(retain) CALayer *mask

The correct way to do what you want is to create the maskViewof the same frame (0, 0, 100, 100) as the viewToMaskwhich layer you want to mask. Then you need to set the clearColor for the path you want to make invisible (this will block the view interaction over the path so be careful with the view hierarchy).

做您想做的正确方法是创建与 您要遮罩的图层maskView相同的帧(0、0、100、100)viewToMask。然后,您需要为要使其不可见的路径设置 clearColor(这将阻止路径上的视图交互,因此请注意视图层次结构)。

回答by dengST30

Swift 5, thanks @Dan Rosenstark

Swift 5,感谢@Dan Rosenstark

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        let red = UIView(frame: view.bounds)
        view.addSubview(red)
        view.backgroundColor = UIColor.cyan
        red.backgroundColor = UIColor.red
        red.mask(CGRect(x: 50, y: 50, width: 50, height: 50))
    }
}

extension UIView{
    func mask(_ rect: CGRect){
        let mask = CAShapeLayer()
        let path = CGPath(rect: rect, transform: nil)
        mask.path = path
        // Set the mask of the view.
        layer.mask = mask
    }
}

回答by jefimijana

Setting MaskToBoundsis not enough, for example, in scenario where you have UIImageViewthat is positioned inside RelativeLayout. In case that you put your UIImageViewto be near top edge of the layout and then you rotate your UIImageView, it will go over layout and it won't be cropped. If you set that flag to true, it will be cropped yes, but it will also be cropped if UIImageViewis on the center of layout, and that is not what you want.

设置MaskToBounds是不够的,例如,在场景中,你有UIImageView那位于内侧RelativeLayout。如果您将您的UIImageViewto 靠近布局的顶部边缘,然后旋转您的UIImageView,它将越过布局并且不会被裁剪。如果您将该标志设置为 true,它将被裁剪为 yes,但如果UIImageView位于布局的中心,它也会被裁剪,这不是您想要的。

So, determinating maskRectis the right approach.

因此,确定maskRect是正确的方法。