Crafting an Elegant Key-Value Display: A Custom UIView in Swift
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:
- If the
remainingWidth
is greater than or equal to thevalueLabelSizeInLine.width
, and isKeyLabelMoreThanOneLine
is false, which meanskeyLabel
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 👏 .