只需用矩形遮罩 UIView

Posted

技术标签:

【中文标题】只需用矩形遮罩 UIView【英文标题】:Simply mask a UIView with a rectangle 【发布时间】:2012-07-08 14:46:44 【问题描述】:

我想知道如何简单地掩盖任何类型的 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;

【问题讨论】:

我不确定我是否理解,但你为什么不把你的视图框架改成蒙版呢?例如 viewToMask.frame = 掩码; (或者如果你想展示的话,反之亦然。) 这是行不通的,因为视图可能有应该被切断的可拉伸图像,或者即使被屏蔽也应该仍然可用的控件。更改框架会更改图像拉伸或使控件无法使用。 检查这是否有帮助 ***.com/questions/7241966/… 它有帮助:) 添加我作为答案的方式。 或者这个,逐个查看屏蔽***.com/questions/6805444/masking-a-uiview 【参考方案1】:

一个可选层,其 alpha 通道 用作掩码以进行选择 图层背景与合成结果之间 图层的内容及其过滤背景。

@property(retain) CALayer *mask

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

【讨论】:

感谢您的回答!我想我找到了一种不使用 alpha 的更简单的方法,所以我添加了自己的答案。 @Accatyyc 不错的解决方案,我通过将maskLayermaskRect 重命名为homeTeamMaskLayerhomeTeamMaskRect 对其进行了编辑,以便它编译:) 需要等待它被批准虽然并且感谢更新。 啊,感谢您的编辑建议。我以相反的方式更新了它,所以它对我以外的其他项目有意义:)【参考方案2】:

感谢 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;

【讨论】:

感谢您花时间写下您自己的答案!它使学习过程变得更加简单。现在我们也是声望好友了! :) 非常有用,我不知道你可以用路径制作图层,这正是我想要的。谢谢! 非常感谢。达维拉说的。同样在这里-非常感谢您编写此示例。工作出色。 (我用它在集合视图上创建了一个动画蒙版,这样我就可以拉起集合视图并让它在另一个视图之上展开,这样它就可以接管全屏——最终结果非常漂亮:-) ) 非常感谢!我被过滤器问题困扰了好几天,我试图屏蔽 UIImage,但屏蔽 UIView 是一个更好的解决方案!我希望你能通过堆栈溢出发送啤酒! 我整天都在找这个。爱你。【参考方案3】:

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

wrapper.clipsToBounds = true

【讨论】:

如果你想剪切一个控件,它实际上不起作用,因为它不会响应其父框架之外的触摸。此外,这基本上与设置蒙版的作用相同,只是现在您需要考虑一个相对框架。 我认为它的性能更高,因为这可能只在引擎盖下调整纹理坐标,但应用蒙版可能会进行 alpha 混合。 您始终可以覆盖 -pointInside: 以获取框架外的响应。不过,这可能真的是一笔开销。 更简单的方法!谢谢。【参考方案4】:

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

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

【讨论】:

【参考方案5】:

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
    

输出

【讨论】:

【参考方案6】:

谢谢大家的回答。

如果有人在这个问题上找不到合适的答案,就像我刚刚做的那样,我已经在 Swift 2.2 中为masking/clipping UIViewCGRect 组装了一个工作要点/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)

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

【讨论】:

经过几个小时的搜索,我找到了你,我的救星。非常感谢! :)【参考方案7】:

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
    

【讨论】:

以上是关于只需用矩形遮罩 UIView的主要内容,如果未能解决你的问题,请参考以下文章

[Egret][文档]遮罩

如何实现动态UIView遮罩?

UIView 遮罩透明。

UIView 圆形遮罩动画

遮罩层的创建

通过保留透明区域和移除彩色区域来使用 UIImage 遮罩 UIView