17

What type of value is [.layerMaxXMinYCorner, .layerMinXMinYCorner]? Is it possible to set this parameter on a View in Interface Builder? I know how to set layer.borderWidth, layer.borderUIColor and layer.cornerRadius in the Identity Inspector, but I can't figure out the right Type and Value to use for masked corners.

Thanks!

Jake


Update: here are the integer values for each combination (in terms of which are rounded):

  • 0: no rounded corners
  • 1: top left
  • 2: top right
  • 3: top left & right (both top corners)
  • 4: bottom left
  • 5: top & bottom left (both left corners)
  • 6: top right & bottom left
  • 7: top left & right, bottom left (all corners except bottom right)
  • 8: bottom right
  • 9: top left, bottom right
  • 10: top & bottom right (both right corners)
  • 11: both top corners, bottom right (all corners except bottom left)
  • 12: bottom left & right (both bottom corners)
  • 13: bottom left & right, top left (all corners except top right)
  • 14: bottom left & right, top right (all corners except top left)
  • 15: all corners rounded

  • Top left is in 1, 3, 5, 7, 9, 11, 13, 15
  • Top right is in 2-3, 6-7, 10-11, 14-15
  • Bottom left is in 4-7, 12-15
  • Bottom right is in 8-15

  • Both top corners are in 3, 7, 11, 15

  • Both right corners are in 10-11, 14,15
  • Both bottom corners are in 12-15
  • Both left corners are in 5, 7, 13, 15
Jacob Smolowe
  • 369
  • 2
  • 11

4 Answers4

6

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.

Mike Mertsock
  • 11,825
  • 7
  • 42
  • 75
  • Thank you! So if I understand correctly, I can use a different integer value for whichever corner or set of corners I want to mask? So like 1 might equal top left, 2 is top right, and 5 might be top right and bottom right or something, and I just have to work out which is which? – Jacob Smolowe Dec 07 '17 at 16:39
  • 1
    Exactly. Each corner is a power of 2, so any combination of corners you need will be a unique integer. Easiest way to find out the exact value you need is to write a bit of code in a view controller that sets up the corner mask you need for some random view, and then print out the `someView.layer.maskedCorners.rawValue` value to see what integer that corresponds to. – Mike Mertsock Dec 07 '17 at 16:41
  • Awesome, I'll do that. Thanks again! – Jacob Smolowe Dec 07 '17 at 16:41
  • 1
    Or actually, if you look at the CACornerMask documentation in Objective-C (*not* Swift), you can see the raw values for each mask: https://developer.apple.com/documentation/quartzcore/cacornermask?language=objc – Mike Mertsock Dec 07 '17 at 16:42
4

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)
            }
        }
    }
}
DarkByte
  • 1,162
  • 8
  • 17
1

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 
  • Interesting! I'm not too familiar with `@IBInspectable`, `@IBDesignable` and preprocessor macros, but I definitely like the neatness of the math in the extension! I will check this out, thanks! – Jacob Smolowe May 24 '19 at 05:23
1

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

Kurt L.
  • 623
  • 10
  • 23