问题
I'm creating a stack view as part of a UIControl subclass that successfully lays out n
buttons. The problem is, if I inspect the frame of any button, even though I can see their text, it says their width and height are 0.
final class MySegmentedControl: UIControl {
private var stackView: UIStackView?
private var selection: UIView? = nil
var selectedSegmentIndex: Int = 0
var items: [String] = []
private var segmentCount: CGFloat {
CGFloat(items.count)
}
private var segmentWidth: CGFloat {
guard segmentCount > 0 else { return 0 }
return floor(frame.width / segmentCount)
}
init(frame: CGRect, items: [String]) {
self.items = items.map { $0.uppercased() }
super.init(frame: frame)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private struct Style {
static let slateGray40 = UIColor.from(rgb: 0x8A949E)
static let teal = UIColor.from(rgb: 0x54919F)
}
private struct Metrics {
static let stackViewHeight: CGFloat = 32
static let barHeight: CGFloat = 4
static let cornerRadius: CGFloat = 2
}
private func createButtons() -> [UIButton] {
return items.map { item in
Init(UIButton()) {
$0.setTitle(item, for: .normal)
$0.setTitleColor(Style.slateGray40, for: .normal)
$0.titleLabel?.textAlignment = .center
$0.titleLabel?.font = UIFont.systemFont(ofSize: 14)
$0.addTarget(self, action: #selector(buttonTapped(_:)), for: .primaryActionTriggered)
$0.layer.borderWidth = 1
$0.layer.borderColor = UIColor.red.cgColor
}
}
}
func layout() {
let stackView = UIStackView()
stackView.axis = .horizontal
stackView.alignment = .fill
stackView.distribution = .fillEqually
stackView.spacing = 0
for (index, button) in createButtons().enumerated() {
button.frame = CGRect(
x: self.xPosition(forSegmentIndex: index),
y: 0,
width: Metrics.segmentWidth,
height: Metrics.stackViewHeight
)
stackView.addArrangedSubview(button)
}
addSubview(stackView)
stackView.frame = CGRect(
x: 0,
y: 0,
width: frame.width,
height: Metrics.stackViewHeight
)
self.stackView = stackView
let colorBar = UIView(frame: CGRect(
x: 0,
y: 32,
width: frame.width,
height: Metrics.barHeight
))
colorBar.backgroundColor = UIColor.from(rgb: 0xffffff).withAlphaComponent(0.1)
colorBar.clipsToBounds = true
colorBar.layer.cornerRadius = Metrics.cornerRadius
addSubview(colorBar)
let selection = UIView(frame: CGRect(
x: colorBar.bounds.minX,
y: colorBar.bounds.minY,
width: segmentWidth,
height: Metrics.barHeight
))
selection.clipsToBounds = true
selection.backgroundColor = Style.teal
selection.layer.cornerRadius = Metrics.cornerRadius
colorBar.addSubview(selection)
self.selection = selection
stackView.arrangedSubviews.forEach {
print("Frame", $0.frame)
}
didSelectIndex(0)
}
@objc func buttonTapped(_ sender: UIButton) {
print("tapped a thing")
guard
let stackView = stackView,
let senderButtonText = sender.titleLabel?.text,
let buttons = stackView.arrangedSubviews as? [UIButton]
else { return }
if let index = buttons
.compactMap({ $0.titleLabel?.text })
.firstIndex(of: senderButtonText) {
didSelectIndex(index)
}
}
private func didSelectIndex(_ index: Int) {
setSegmentAsActive(index) // below?
animateBar(to: index)
}
private func setSegmentAsActive(_ index: Int) {
guard
index > 0,
index < Int(segmentCount),
let stackView = stackView,
let buttons = stackView.arrangedSubviews as? [UIButton]
else { return }
buttons[index].setTitleColor(Style.teal, for: .normal)
}
private func animateBar(to index: Int) {
guard index > 0 && index < items.count else { return }
UIView.animate(withDuration: 1) {
self.selection?.frame.origin = CGPoint(
x: self.xPosition(forSegmentIndex: index),
y: 0
)
}
}
private func xPosition(forSegmentIndex index: Int) -> CGFloat {
guard index > 0 && index < items.count else { return 0 }
return segmentWidth * CGFloat(index)
}
}
And then the implementation in a view controller:
// In `viewDidLoad`:
let seg = MySegmentedControl(frame: CGRect(x: 0, y: 100, width: 200, height: 0), items: ["Executive", "Polling"])
view.addSubview(seg)
seg.center = CGPoint(x: view.frame.midX, y: seg.frame.minY)
seg.layout()
How can I make the button have a width and a height so they're tappable?
来源:https://stackoverflow.com/questions/59672144/how-do-i-get-a-uibutton-to-size-itself-in-a-uistackview