Crafting an Elegant Key-Value Display: A Custom UIView in Swift

Wing CHAN
3 min readApr 16, 2023

--

Hey folks! One of my recent projects required me to create a custom UIView that can display key-value pair information in a clear and concise manner. It’s a perfect implementation for scenarios when your app needs to display small pieces of data side-by-side, such as labels and values.

The magic happens in the KeyValueView class which is a simple UIView subclass. It contains two UILabels: keyLabel for displaying the key and valueLabel for the value. The idea is that if both texts are short enough, they will be displayed side by side:

+--------------------------------------------------+
| keyLabel(short) | valueLabel(short) |
+--------------------------------------------------+

However, if the text in the value label is too long and cannot be displayed on the same line as the key label, the value will wrap to the next line:

+--------------------------------------------------+
| keyLabel(short) |
| valueLabel(long) |
|--------------------------------------------------|

To achieve this, I added keyLabel and valueLabel as subviews and set their numberOfLines property to zero. This allows the labels to wrap their content as needed.

keyLabel.numberOfLines = 0
valueLabel.numberOfLines = 0
addSubview(keyLabel)
addSubview(valueLabel)

The real key to success is in the layoutSubviews() method, which takes care of properly resizing and positioning the labels. The method calculates the optimal sizes and positions of both labels based on the width of the containing view, and then assigns their frames accordingly. It also invalidates the view's intrinsic content size to make sure the layout is updated correctly.

override public func layoutSubviews() {
...
}

Let’s dive into how the layoutSubviews() method calculates whether the valueLabel should appear on the same line as the keyLabel or on the next line.

The first step in the process is to set the preferredMaxLayoutWidth based on the frame width of the KeyValueView.

preferredMaxLayoutWidth = frame.width

Next, we calculate the size of both keyLabel and valueLabel if they were placed on a single line by calling the sizeThatFits(_:) function with .zero.

let keyLabelSizeInLine = keyLabel.sizeThatFits(.zero)
let valueLabelSizeInLine = valueLabel.sizeThatFits(.zero)

Then, we check if the keyLabel spans more than one line by comparing its width to the preferredMaxLayoutWidth.

let isKeyLabelMoreThanOneLine: Bool = keyLabelSizeInLine.width > preferredMaxLayoutWidth

The next step is to set the frame of the keyLabel. If the isKeyLabelMoreThanOneLine is true, we use the multiline size of the keyLabel. Otherwise, we keep the single-line size.

if isKeyLabelMoreThanOneLine {
...
} else {
keyLabel.frame = CGRect(x: 0, y: 0, width: keyLabelSizeInLine.width, height: keyLabelSizeInLine.height)
}

Once the frame of the keyLabel is set, we calculate the remaining width available for the valueLabel after placing the keyLabel.

let remainingWidth = preferredMaxLayoutWidth - keyLabelSizeInLine.width

Now, here’s the critical part: we decide whether to place the valueLabel on the same line or the next line. We check two conditions:

  1. If the remainingWidth is greater than or equal to the valueLabelSizeInLine.width, and
  2. isKeyLabelMoreThanOneLine is false, which means keyLabel is on a single line.

If both conditions are met, the valueLabel will be placed on the same line as the keyLabel. Otherwise, we place valueLabel on the next line.

if remainingWidth >= valueLabelSizeInLine.width, !isKeyLabelMoreThanOneLine {
...
} else {
valueLabel.frame = CGRect(x: 0, y: keyLabel.frame.height, width: valueLabelSizeInMultiline.width, height: valueLabelSizeInMultiline.height)
}

And that’s how the valueLabel is positioned either on the same line as the keyLabel or on the next line, based on their sizes and the available space. The relevant calculations and conditions ensure that the layout will always be clear and nicely formatted.

that’s all there is to it! The KeyValueView is now a reusable and adaptable component that can be easily integrated into your app. To use it, simply call the config(key:value:) function and pass in the strings for the key and value.

public func config(key: String, value: String) {
...
}

That’s all for this simple but effective solution for displaying key-value pairs in your app. I hope you find this useful and can integrate it into your projects! As always, feel free to ask questions, give feedback, and share your own ideas in the comments below. Happy coding!

I hope you enjoy reading this article and hitting clap 👏 .

--

--