Setting masked corners in Interface Builder

I've just randomly bumped into this same "issue" and wrote another solution, using an extension (no subclassing) and visual toggles in IB for each corner (without setting / adding the bit flags manually).

Keep in mind that the default settings are On / true (so that "cornerRadius" affects all corners of the view). If you want to round all corners except one, just set that particular corner to false.

enter image description here

import UIKit

@IBDesignable
public extension UIView {
    @IBInspectable var cornerRadius: CGFloat {
        get { return layer.cornerRadius }
        set { layer.cornerRadius = newValue }
    }

    @IBInspectable var topLeft: Bool {
        get { return layer.maskedCorners.contains(.layerMinXMinYCorner) }
        set {
            if newValue {
                layer.maskedCorners.insert(.layerMinXMinYCorner)
            } else {
                layer.maskedCorners.remove(.layerMinXMinYCorner)
            }
        }
    }

    @IBInspectable var topRight: Bool {
        get { return layer.maskedCorners.contains(.layerMaxXMinYCorner) }
        set {
            if newValue {
                layer.maskedCorners.insert(.layerMaxXMinYCorner)
            } else {
                layer.maskedCorners.remove(.layerMaxXMinYCorner)
            }
        }
    }

    @IBInspectable var bottomLeft: Bool {
        get { return layer.maskedCorners.contains(.layerMinXMaxYCorner) }
        set {
            if newValue {
                layer.maskedCorners.insert(.layerMinXMaxYCorner)
            } else {
                layer.maskedCorners.remove(.layerMinXMaxYCorner)
            }
        }
    }

    @IBInspectable var bottomRight: Bool {
        get { return layer.maskedCorners.contains(.layerMaxXMaxYCorner) }
        set {
            if newValue {
                layer.maskedCorners.insert(.layerMaxXMaxYCorner)
            } else {
                layer.maskedCorners.remove(.layerMaxXMaxYCorner)
            }
        }
    }
}

In interface builder I set these by selecting the object you want to transform at run time and in user defined runtime attribute the KeyPath is layer.cornerRadius or layer.maskedCorners and the Type is Number and add the raw value of the corner you want to change.

enter image description here


maskedCorners is a CACornerMask, which is an OptionSet, or bit mask. The raw value is an integer: in code, you can try printing the value of someView.layer.maskedCorners.rawValue, or setting it via someView.layer.maskedCorners.setValue(3, forKey: "maskedCorners").

So you should be able to set the value of layer.maskedCorners to the integer 3 (or whatever you need) in Interface Builder, and I don't see why it would be unsafe to do this. Though it will be a pain to figure out what set of corners that integer value actually maps to if you forget.


Did not check if it works, but you may try c:

(Don't forget to apply the mask to your view 🌚)

@IBDesignable
class ViewController: UIViewController {
    // Preprocessor macro, you won't be able 
    // to use code inside this "if" statement from your... code
    // Just use cornerMask property directly
    #if TARGET_INTERFACE_BUILDER
    @IBInspectable
    var topLeft: Bool = false {
        didSet {
            updateCornerMask()
        }
    }

    @IBInspectable
    var topRight: Bool = false {
        didSet {
            updateCornerMask()
        }
    }

    @IBInspectable
    var bottomLeft: Bool = false {
        didSet {
            updateCornerMask()
        }
    }

    @IBInspectable
    var bottomRight: Bool = false {
        didSet {
            updateCornerMask()
        }
    }

    func updateCornerMask() {
        cornerMask = CACornerMask(
            TL: topLeft,
            TR: topRight,
            BL: bottomLeft,
            BR: bottomRight)
    }
    #endif

    var cornerMask: CACornerMask?
}

extension CACornerMask {
    init(TL: Bool = false, TR: Bool = false, BL: Bool = false, BR: Bool = false) {
        var value: UInt = 0
        if TL { value += 1 }
        if TR { value += 2 }
        if BL { value += 4 }
        if BR { value += 8 }

        self.init(rawValue: value)
    }
}

// And yeah maybe using UIView makes more sense 🙃