56

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

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

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

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

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

7 回答 7

130

感谢 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;
于 2012-07-09T08:30:04.987 回答
28

谢谢你们的回答。

万一有人在几个小时内找不到关于这个问题的合适答案,就像我刚刚做的那样,我已经在 Swift 2.2 中为masking/ clipping UIViewwith CGRect/组装了一个工作要点UIBezierPath

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

用法:

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)

希望它会在未来节省一些时间:)

于 2016-10-07T12:28:57.760 回答
22

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

wrapper.clipsToBounds = true
于 2013-10-11T10:44:27.460 回答
3

Swift 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
    }
}

输出

在此处输入图像描述

于 2015-10-20T18:16:20.890 回答
1

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

@property(retain) CALayer *mask

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

于 2012-07-09T08:21:37.357 回答
0

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
    }
}
于 2019-09-04T08:12:34.540 回答
-1

设置MaskToBounds是不够的,例如,在您将UIImageView其定位在内部的情况下RelativeLayout。如果您将您放在UIImageView布局的顶部边缘附近然后旋转您的UIImageView,它将超过布局并且不会被裁剪。如果您将该标志设置为 true,它将被裁剪,但是如果UIImageView位于布局的中心,它也会被裁剪,这不是您想要的。

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

于 2015-09-01T10:40:00.527 回答