import UIKit
import TinyConstraints
final class CountDownStackView: UIStackView {
func configure(withData data: [[String]]) {
data.forEach { rowData in
let verticalStackView = UIStackView()
verticalStackView.axis = .vertical
verticalStackView.spacing = 8
rowData.forEach { text in
let label = UILabel()
label.text = text
label.backgroundColor = .lightGray
label.textAlignment = .center
verticalStackView.addArrangedSubview(label)
}
let horizontalStackView = UIStackView()
horizontalStackView.axis = .horizontal
horizontalStackView.spacing = 5
horizontalStackView.addArrangedSubview(verticalStackView)
let parser = UILabel()
parser.text = ":"
parser.customizeLabel(font: UIFont.systemFont(ofSize: 18),textColor: .white)
horizontalStackView.addArrangedSubview(parser)
addArrangedSubview(horizontalStackView)
}
}
}
I'm using this code blog and I can't find where I went wrong and where I need to fix it.
the first image is the result of my codes, but I am trying to capture the situation in the second image
let data: [[String]] = [
["02", " Hour"],
["43","minute"],
["23","second"]]
sample data
You only need one horizontal stack view but your code will create 3. Since self
is already a stack view, self
should be the one and only horizontal stack view. Here's a rework of your class:
final class CountDownStackView: UIStackView {
func configure(withData data: [[String]]) {
axis = .horizontal
distribution = .equalSpacing
alignment = .center
spacing = 5
for (column, rowData) in data.enumerated() {
var labels = [UILabel]()
for (row, text) in rowData.enumerated() {
let label = UILabel(frame: .zero)
label.font = row == 0 ? .preferredFont(forTextStyle: .title1) : .preferredFont(forTextStyle: .body)
label.textColor = column + 1 == data.count ? .systemGray : .label
label.text = text
label.textAlignment = .left
labels.append(label)
}
let verticalStackView = UIStackView(arrangedSubviews: labels)
verticalStackView.axis = .vertical
verticalStackView.distribution = .equalSpacing
verticalStackView.alignment = .leading
verticalStackView.spacing = 4
addArrangedSubview(verticalStackView)
if column + 1 < data.count {
let parser = UILabel()
parser.text = ":"
parser.customizeLabel(font: UIFont.systemFont(ofSize: 18), textColor: .label)
parser.sizeToFit()
addArrangedSubview(parser)
}
}
}
}
Then call it with something like:
let data: [[String]] = [
["02", "Hour"],
["43", "Minute"],
["23", "Second"],
]
let stack = CountDownStackView()
stack.configure(withData: data)
This gives you a layout that is much closer to the 2nd picture. But the two colons don't look right. I would add the colons as strings in your data
array. Update the class as follows:
final class CountDownStackView: UIStackView {
func configure(withData data: [[String]]) {
axis = .horizontal
distribution = .equalSpacing
alignment = .center
spacing = 5
for (column, rowData) in data.enumerated() {
var labels = [UILabel]()
for (row, text) in rowData.enumerated() {
let label = UILabel(frame: .zero)
label.font = row == 0 ? .preferredFont(forTextStyle: .title1) : .preferredFont(forTextStyle: .body)
label.textColor = column + 1 == data.count ? .systemGray : .label
label.text = text
label.textAlignment = .left
labels.append(label)
}
let verticalStackView = UIStackView(arrangedSubviews: labels)
verticalStackView.axis = .vertical
verticalStackView.distribution = .equalSpacing
verticalStackView.alignment = .leading
verticalStackView.spacing = 4
addArrangedSubview(verticalStackView)
}
}
}
Then create it as follows:
let data: [[String]] = [
["02", "Hour"],
[":", " "],
["43", "Minute"],
[":", " "],
["23", "Second"],
]
let stack = CountDownStackView()
stack.configure(withData: data)
This gives a better result with the layout.
While this answer gives you a solution to the layout issues with your custom stack view subclass, I would suggest that this whole approach is far from ideal for implementing some sort of countdown view. Your current CountDownStackView
makes it very difficult to update any of the labels as the timer counts down.
While I'll leave the details for another question (since this is beyond the scope of your original question), you really should create a subclass of UIView
that makes use of a stack view of labels. It should also use a Timer
that can be used to update the labels in the stack view.