Episode #393

Padded Label and Separator View

Series: Making a Podcast App From Scratch

11 minutes
Published on May 29, 2019

This video is only available to subscribers. Get access to this video and 572 others.

In this episode we create some more custom @IBDesignable views, this time for a padded genre label where we use the intrinsicContentSize to make a label take up more space and give itself a little padding. We also create a separator view that draws a thin line to separate sections visually.

Padded Label and Separator View

In this episode, we create some more custom @IBDesignable views, this time for a padded genre label where we use the intrinsicContentSize to make a label take up more space and give itself a little padding. We also create a separator view that draws a thin line to separate sections visually.

Creating Custom Genre Label

We will start by creating a custom UILabel to define our settings. To view the custom label rendered in Interface Builder, we will mark the class as @IBDesignable and configure the UI components with @IBInspectable. Since the content size is dependent on these properties, we need to invalidate the intrinsic content size in didSet.

import UIKit

@IBDesignable
class GenreLabel : UILabel {

    @IBInspectable
    var horizontalInset: CGFloat = 0 {
        didSet {
            invalidateIntrinsicContentSize()
        }
    }

    @IBInspectable
    var verticalInset: CGFloat = 0 {
        didSet {
            invalidateIntrinsicContentSize()
        }
    }
}

Now we will set properties for the label. We will set cornerRadius to half of bounds height and to see the effect of this setting we will set masksToBounds. Now we will define intrinsicContentSize.

private func commonSetup() {
    backgroundColor = Theme.Colors.gray2
    textColor = Theme.Colors.gray0
    font = UIFont.systemFont(ofSize: 12, weight: .medium)
    textAlignment = .center
    layer.masksToBounds = true
}

override func layoutSubviews() {
    super.layoutSubviews()
    layer.cornerRadius = bounds.size.height / 2
}

override var intrinsicContentSize: CGSize {
    var superSize = super.intrinsicContentSize
    superSize.width += horizontalInset * 2
    superSize.height += verticalInset * 2
    return superSize
}

Creating a Separator View

We will create a UIView by marking it with @IBDesignable and its properties marked with @IBInspectable. To trigger the setting of separatorColor through Interface Builder we need to call setNeedsDisplay while setting this property.

@IBDesignable
class SeparatorView : UIView {

    @IBInspectable
    var separatorColor: UIColor = Theme.Colors.gray3 {
        didSet {
            setNeedsDisplay()
        }
    }

    override func awakeFromNib() {
        super.awakeFromNib()
        commonSetup()
    }

    override func prepareForInterfaceBuilder() {
        super.prepareForInterfaceBuilder()
        commonSetup()
    }
}

We will now draw the separator view and set the properties for the current context. To draw the separator at the mid-level of original, set y to half of the height. To ensure visibility of our custom settings, set the properties like isOpaque and backgroundColor.

private func commonSetup() {
    isOpaque = false
    backgroundColor = .clear
}

override func draw(_ rect: CGRect) {
    guard let context = UIGraphicsGetCurrentContext() else { return }
    separatorColor.setStroke()

    context.setLineWidth(1/UIScreen.main.scale)
    let midY = bounds.size.height / 2
    context.move(to: CGPoint(x: 0, y: midY))
    context.addLine(to: CGPoint(x: bounds.size.width, y: midY))
    context.strokePath()
}

This episode uses Xcode 10.2.1, Swift 5.0.