Swift: Mask Alignment + Auto-Layout Constraints

I have this PNG file, which I’d like to use as a mask for a UIView.

enter image description here

  • add UIImage in CALayer
  • Masking a UIView
  • UIImage masking problems iOS 7
  • Crash in actionForLayer:forKey:
  • How to apply CAGradientLayer as mask of another CALayer?
  • How to align a mask onto an image
  • The view must be:

    • 20 pixels/points in from each side
    • A perfect square
    • Centered vertically

    I set the following constraints to accomplish this:

    enter image description here

    However, it seems these constraints don’t play well with masks. When these constraints and the mask property are set, I get the following:

    enter image description here

    but I’d like the view to look just like the mask above, except orange (The backgroundColor here is just for simplicity—I later add subviews that need to be masked.)

    However, when no constraints are set, the mask seems to work properly and I get something like this (borderColor added for visual purposes only):

    enter image description here

    Here’s my code (viewForLayer is a UIView I made in the storyboard):

        viewForLayer.layer.borderColor = UIColor.redColor().CGColor
        viewForLayer.layer.borderWidth = 10
    
        var mask = CALayer()
        mask.contents = UIImage(named: "TopBump")!.CGImage
        mask.frame = CGRect(x: 0, y: 0, width: viewForLayer.bounds.width, height: viewForLayer.bounds.height)
        mask.position = CGPoint(x: viewForLayer.bounds.width/2, y: viewForLayer.bounds.height/2)
        viewForLayer.layer.mask = mask
        viewForLayer.backgroundColor = UIColor.orangeColor()
    

    The problem is though, that now the view isn’t the right size or in the right position—it doesn’t follow the rules above—”The view must be: “. How can I have the mask work properly, and the auto-layout constraints set at the same time?

    2 Solutions Collect From Internet About “Swift: Mask Alignment + Auto-Layout Constraints”

    I found a way around it. Not sure if this is the best way but here we go…

    http://imgur.com/pUIZbNA

    Just make sure you change the name of the UIView class in the storyboard inspector too. Apparently, the trick is to set the mask frame for each layoutSubviews call.

    class MaskView : UIView {
    
      override func layoutSubviews() {
        super.layoutSubviews()
        if let mask = self.layer.mask {
          mask.frame = self.bounds
        }        
      }
    
    }
    
    class ViewController: UIViewController {
    
        @IBOutlet weak var viewForLayer: MaskView!
    
        override func viewDidLoad() {
            super.viewDidLoad()
            let image = UIImage(named: "TopBump")!.CGImage!
            let maskLayer = CALayer()
            maskLayer.contents = image
            maskLayer.frame = viewForLayer.bounds
            viewForLayer.layer.mask = maskLayer
            viewForLayer.backgroundColor = UIColor.orangeColor()
    
            // Do any additional setup after loading the view, typically from a nib.
            viewForLayer.layer.borderColor = UIColor.redColor().CGColor
            viewForLayer.layer.borderWidth = 10
    
    
        }
    
        override func didReceiveMemoryWarning() {
            super.didReceiveMemoryWarning()
            // Dispose of any resources that can be recreated.
        }
    
    }
    

    I tried it for myself. Minus the nitpicking on ‘let mask = CALayer()’ (it’s immutable reference to an updatable object), changing the autolayout constraints of the embedded view shows the mask is aligned correctly.

        NSLog("\(viewForLayer.bounds.width), \(viewForLayer.bounds.height)")
    

    returns 375.0, 667.0 on an iPhone 6 screen. What are you getting?