Swift: Mask Alignment + Auto-Layout Constraints

2.9k views Asked by At

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

enter image description here

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

There are 2 answers

3
John Difool On BEST ANSWER

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

https://i.stack.imgur.com/k1e3N.jpg

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

}
1
John Difool On

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?