I am building a custom longitude/latitude selector. The view looks like:
textfield ° textfield ′ textfield ″ N|S
The components are all in a stack view. I would like the width of the textfields to auto-fit the content. Here is the code that I am using (the actual layout code is not a lot. Most of it is boilerplate):
class DMSLongLatInputView : UIView {
private var degreeTextField: DMSLongLatTextField!
private var minuteTextField: DMSLongLatTextField!
private var secondTextField: DMSLongLatTextField!
var signSelector: UISegmentedControl!
let fontSize: CGFloat = 22
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
private func commonInit() {
degreeTextField = DMSLongLatTextField()
minuteTextField = DMSLongLatTextField()
secondTextField = DMSLongLatTextField()
signSelector = UISegmentedControl(items: ["N", "S"])
signSelector.selectedSegmentIndex = 0
signSelector.setTitleTextAttributes([.font: UIFont.systemFont(ofSize: fontSize)], for: .normal)
[degreeTextField, minuteTextField, secondTextField].forEach { (tf) in
tf?.font = UIFont.monospacedDigitSystemFont(ofSize: fontSize, weight: .regular)
}
let degreeLabel = UILabel()
degreeLabel.text = "°"
let minuteLabel = UILabel()
minuteLabel.text = "′"
let secondLabel = UILabel()
secondLabel.text = "″"
[degreeLabel, minuteLabel, secondLabel].forEach {
l in l.font = UIFont.systemFont(ofSize: fontSize)
}
let stackView = UIStackView(arrangedSubviews:
[degreeTextField,
degreeLabel,
minuteTextField,
minuteLabel,
secondTextField,
secondLabel,
signSelector
])
stackView.arrangedSubviews.forEach { (v) in
// I was hoping that this would make the widths of the stack view's subviews automatically
// fit the content
v.setContentCompressionResistancePriority(.required, for: .horizontal)
v.setContentHuggingPriority(.required, for: .horizontal)
}
stackView.axis = .horizontal
stackView.alignment = .center
stackView.distribution = .fill
addSubview(stackView)
stackView.topAnchor.constraint(equalTo: self.topAnchor).isActive = true
stackView.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true
stackView.leadingAnchor.constraint(equalTo: self.leadingAnchor).isActive = true
stackView.trailingAnchor.constraint(equalTo: self.trailingAnchor).isActive = true
stackView.translatesAutoresizingMaskIntoConstraints = false
backgroundColor = .clear
}
}
fileprivate class DMSLongLatTextField: UITextField, UITextFieldDelegate {
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
private func commonInit() {
backgroundColor = .tertiarySystemFill
placeholder = "00"
borderStyle = .none
textAlignment = .right
}
}
This produces:
The first textfield got stretched by a lot, even though I set the content hugging priority to .required
. I would like the first textfield to be only as wide as two digits, as that is how wide its placeholder is.
I suspected that this is because I used a wrong distribution
, but I tried all the other 4 distributions, but none of them got the result I wanted. For example, .equalSpacing
with spacing = 0
gave this result (as seen from the UI hierarchy inspector in the debugger):
Clearly the spacing is not 0! I would like all the subviews to be as close together as they can be, and stay centre-aligned. How can I do that?