Simply mask a UIView with a rectangle

前端 未结 7 1465
别跟我提以往
别跟我提以往 2020-11-30 20:10

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

相关标签:
7条回答
  • 2020-11-30 20:25

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

    wrapper.clipsToBounds = true
    
    0 讨论(0)
  • 2020-11-30 20:26

    Setting MaskToBounds is not enough, for example, in scenario where you have UIImageView that is positioned inside RelativeLayout. In case that you put your UIImageView to 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 UIImageView is on the center of layout, and that is not what you want.

    So, determinating maskRect is the right approach.

    0 讨论(0)
  • 2020-11-30 20:30

    An optional layer whose alpha channel is 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

    The correct way to do what you want is to create the maskView of the same frame (0, 0, 100, 100) as the viewToMask which 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).

    0 讨论(0)
  • 2020-11-30 20:33

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

    // 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;
    
    0 讨论(0)
  • 2020-11-30 20:33

    Swift 5 , thanks @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
        }
    }
    
    0 讨论(0)
  • 2020-11-30 20:35

    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/clipping UIView with 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
        }
    }
    

    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 :)

    0 讨论(0)
提交回复
热议问题