![](/img/trans.png)
[英]iOS8 NSLayoutConstraint causing issues and UIButton not clickable
[英]Swift: UIButton not clickable, NSLayoutConstraint heightAnchor issues
我正在構建一個簡單的劊子手游戲。 我用 UIButtons 構建了一個簡單的鍵盤。 鍵盤在子視圖內,每一行都是一個單獨的子視圖。
按鈕不可點擊,我可以讓第一行工作,但其他行被推開。
我已經嘗試設置 NSLayoutConstraint 高度錨點,它會將 UIButtons 推出其相應的視圖。
class ViewController: UIViewController {
// letterGuess
// usedLetters
// score/lives
var scoreLabel: UILabel!
var answerLabel: UILabel!
var characterButtons = [UIButton]()
var score = 0 {
didSet {
scoreLabel.text = "Score: \(score)"
}
}
override func loadView() {
view = UIView()
view.backgroundColor = .white
scoreLabel = UILabel()
scoreLabel.translatesAutoresizingMaskIntoConstraints = false
scoreLabel.textAlignment = .right
scoreLabel.font = UIFont.systemFont(ofSize: 24)
scoreLabel.text = "Score: 0"
view.addSubview(scoreLabel)
answerLabel = UILabel()
answerLabel.translatesAutoresizingMaskIntoConstraints = false
answerLabel.font = UIFont.systemFont(ofSize: 24)
answerLabel.text = "ANSWER"
answerLabel.numberOfLines = 1
answerLabel.textAlignment = .center
answerLabel.setContentHuggingPriority(UILayoutPriority(1), for: .vertical)
view.addSubview(answerLabel)
let buttonsView = UIView()
buttonsView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(buttonsView)
let row1View = UIView()
row1View.translatesAutoresizingMaskIntoConstraints = false
buttonsView.addSubview(row1View)
let row2View = UIView()
row2View.translatesAutoresizingMaskIntoConstraints = false
row2View.setContentHuggingPriority(.defaultLow, for: .vertical)
buttonsView.addSubview(row2View)
let row3View = UIView()
row3View.translatesAutoresizingMaskIntoConstraints = false
buttonsView.addSubview(row3View)
NSLayoutConstraint.activate([
scoreLabel.topAnchor.constraint(equalTo: view.layoutMarginsGuide.topAnchor),
scoreLabel.trailingAnchor.constraint(equalTo: view.layoutMarginsGuide.trailingAnchor, constant: 0),
answerLabel.topAnchor.constraint(equalTo: scoreLabel.bottomAnchor, constant: 25),
answerLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor),
buttonsView.widthAnchor.constraint(equalToConstant: 1000),
buttonsView.heightAnchor.constraint(equalToConstant: 300),
buttonsView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
buttonsView.topAnchor.constraint(equalTo: answerLabel.bottomAnchor, constant: 20),
buttonsView.bottomAnchor.constraint(equalTo: view.layoutMarginsGuide.bottomAnchor, constant: -20),
row1View.leftAnchor.constraint(equalTo: buttonsView.leftAnchor),
row1View.topAnchor.constraint(equalTo: buttonsView.topAnchor),
row1View.widthAnchor.constraint(equalTo: buttonsView.widthAnchor),
//row1View.heightAnchor.constraint(equalTo: buttonsView.heightAnchor, multiplier: 0.333, constant: 0),
//row1View.heightAnchor.constraint(equalToConstant: 100),
row2View.leftAnchor.constraint(equalTo: buttonsView.leftAnchor),
row2View.topAnchor.constraint(equalTo: row1View.bottomAnchor),
row2View.widthAnchor.constraint(equalTo: buttonsView.widthAnchor),
//row2View.heightAnchor.constraint(equalTo: buttonsView.heightAnchor, multiplier: 0.333, constant: 0),
//row2View.heightAnchor.constraint(equalToConstant: 100),
row3View.leftAnchor.constraint(equalTo: buttonsView.leftAnchor),
row3View.topAnchor.constraint(equalTo: row2View.bottomAnchor),
row3View.widthAnchor.constraint(equalTo: buttonsView.widthAnchor),
//row3View.heightAnchor.constraint(equalTo: buttonsView.heightAnchor, multiplier: 0.333, constant: 0),
//row3View.heightAnchor.constraint(equalToConstant: 100),
])
let width = 100
let height = 100
var i = 10
for row in 0..<3 {
print(row)
switch row {
case 0:
i = 10
case 1:
i = 9
case 2:
i = 7
default:
return
}
for col in 0..<i {
let characterButton = UIButton(type: .system)
characterButton.titleLabel?.font = UIFont.systemFont(ofSize: 36)
characterButton.layer.borderWidth = 1
characterButton.layer.borderColor = UIColor.lightGray.cgColor
characterButton.layer.backgroundColor = UIColor.white.cgColor
characterButton.setTitle("#", for: .normal)
let frame = CGRect(x: col * width, y: row * height, width: width, height: height)
characterButton.frame = frame
switch row {
case 0:
print(row)
print("row 1")
row1View.addSubview(characterButton)
case 1:
print(row)
print("row 2")
row2View.addSubview(characterButton)
case 2:
print(row)
print("row 3")
row3View.addSubview(characterButton)
default:
print("defualt")
return
}
characterButtons.append(characterButton)
characterButton.addTarget(self, action: #selector(characterTapped), for: .touchUpInside)
}
}
buttonsView.backgroundColor = .purple
row1View.backgroundColor = .red
row2View.backgroundColor = .yellow
row3View.backgroundColor = .green
}
您在計算要放置在每行中的按鈕的框架的地方有一個錯誤。
// your code
let frame = CGRect(x: col * width, y: row * height, width: width, height: height)
您不需要更改按鈕的y
position。 它在這里只能是0 ,因為每一行都在它自己的視圖中。
// corrected code
let frame = CGRect(x: col * width, y: 0, width: width, height: height)
您還應該為您擁有的每一行設置一個高度約束。 添加的所有按鈕都超出了父視圖的范圍。 這在設置rowView.clipsToBounds = true
時變得可見。 這就是為什么您的按鈕不起作用的原因。
我相信循環中存在問題,並且運行超出了它的需要,但我沒有檢查它。
您的問題的解決方案:我嘗試修復您的示例代碼並且它有效。 在這里檢查
還可以在找到時間時嘗試使用集合或堆棧視圖來解決問題。
使用UIStackView
s 有很多好處……主要是因為它們可以用來自動排列和調整子視圖的大小,從而使您的布局很容易適應不同的設備和屏幕尺寸。
這是您的代碼示例,修改為使用堆棧視圖來保存按鈕(我還添加了一個自定義CharacterButton
class,它將自動在用戶定義的范圍/比例中設置按鈕標簽的字體大小):
class CharacterButton: UIButton {
// this will automatically set the font size for the button
// if the button width >= 100, font size will be maxSize
// if it's less than 100, font size will be proportional
// with a minimum font size of 20
// these are declared as "var" so they can be changed at run-time if desired
var maxSize: CGFloat = 36
var minSize: CGFloat = 20
var forWidth: CGFloat = 100
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() -> Void {
// set title colors
setTitleColor(.blue, for: .normal)
setTitleColor(.lightGray, for: .highlighted)
// maybe change title color when disabled?
//setTitleColor(.darkGray, for: .disabled)
// give it a border
layer.borderWidth = 1
layer.borderColor = UIColor.lightGray.cgColor
layer.backgroundColor = UIColor.white.cgColor
}
override func layoutSubviews() {
super.layoutSubviews()
let fSize = min(max(minSize, bounds.size.width / forWidth * maxSize), maxSize)
titleLabel?.font = UIFont.systemFont(ofSize: fSize)
}
}
class HangManViewController: UIViewController {
// letterGuess
// usedLetters
// score/lives
var scoreLabel: UILabel!
var answerLabel: UILabel!
var characterButtons = [UIButton]()
var score = 0 {
didSet {
scoreLabel.text = "Score: \(score)"
}
}
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
// create and add the Score label
scoreLabel = UILabel()
scoreLabel.translatesAutoresizingMaskIntoConstraints = false
scoreLabel.textAlignment = .right
scoreLabel.font = UIFont.systemFont(ofSize: 24)
scoreLabel.text = "Score: 0"
view.addSubview(scoreLabel)
// create and add the Answer label
answerLabel = UILabel()
answerLabel.translatesAutoresizingMaskIntoConstraints = false
answerLabel.font = UIFont.systemFont(ofSize: 24)
answerLabel.text = "ANSWER"
answerLabel.numberOfLines = 1
answerLabel.textAlignment = .center
answerLabel.setContentHuggingPriority(UILayoutPriority(1), for: .vertical)
view.addSubview(answerLabel)
// create a view to hold the buttons
let buttonsView = UIView()
buttonsView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(buttonsView)
// create a vertical "outer" stack view
let outerStack = UIStackView()
outerStack.axis = .vertical
outerStack.distribution = .fillEqually
// add it to the buttons holder view
outerStack.translatesAutoresizingMaskIntoConstraints = false
buttonsView.addSubview(outerStack)
// create three "row" stack views
let row1Stack = UIStackView()
row1Stack.axis = .horizontal
row1Stack.distribution = .fillEqually
let row2Stack = UIStackView()
row2Stack.axis = .horizontal
row2Stack.distribution = .fillEqually
let row3Stack = UIStackView()
row3Stack.axis = .horizontal
row3Stack.distribution = .fillEqually
// add the 3 "row" stack views to the "outer" stack view
[row1Stack, row2Stack, row3Stack].forEach {
outerStack.addArrangedSubview($0)
}
let g = view.layoutMarginsGuide
NSLayoutConstraint.activate([
// constrain Score label to top-right
scoreLabel.topAnchor.constraint(equalTo: g.topAnchor),
scoreLabel.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: 0),
// constrain Answer label centered horizontally
answerLabel.centerXAnchor.constraint(equalTo: g.centerXAnchor),
// and 4-pts above the grid of buttons
answerLabel.bottomAnchor.constraint(equalTo: buttonsView.topAnchor, constant: -4),
// constrain buttons holder view Leading / Trailing
buttonsView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 0.0),
buttonsView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: 0.0),
// constrain buttons holder view Bottom with 20-pts "padding"
buttonsView.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: -20),
// constrain all 4 sides of "outer" stack view to buttons holder view
outerStack.topAnchor.constraint(equalTo: buttonsView.topAnchor),
outerStack.leadingAnchor.constraint(equalTo: buttonsView.leadingAnchor),
outerStack.trailingAnchor.constraint(equalTo: buttonsView.trailingAnchor),
outerStack.bottomAnchor.constraint(equalTo: buttonsView.bottomAnchor),
])
let letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".map { String($0) }
var j = 0
// loop through the 3 "rows" adding 10, 9 and 7 buttons
for (thisRow, numButtonsInThisRow) in zip([row1Stack, row2Stack, row3Stack], [10, 9, 7]) {
for i in 0..<10 {
if i < numButtonsInThisRow {
// create a button
let characterButton = CharacterButton()
// set its title
characterButton.setTitle(letters[j], for: .normal)
// maybe set button title to "#" when disabled?
//characterButton.setTitle("#", for: .disabled)
// give button a touchUp target
characterButton.addTarget(self, action: #selector(self.characterTapped(_:)), for: .touchUpInside)
// add button to current row stack view
thisRow.addArrangedSubview(characterButton)
// add button to characterButtons Array
characterButtons.append(characterButton)
// increment j
j += 1
} else {
// we're past the number of character buttons that should be on this row
// so "fill it out" with bordered views
let v = UIView()
v.layer.borderWidth = 1
v.layer.borderColor = UIColor.lightGray.cgColor
v.layer.backgroundColor = UIColor.white.cgColor
thisRow.addArrangedSubview(v)
}
}
}
// we want square buttons, so
// we only need to set the first button to have a 1:1 height:width ratio
// the stack views' fillEqually distribution will handle the rest
if let v = characterButtons.first {
v.heightAnchor.constraint(equalTo: v.widthAnchor).isActive = true
}
// just so we can see the frame of the answer label
answerLabel.backgroundColor = .green
}
@objc func characterTapped(_ sender: UIButton) {
// character button tapped
// get its title
let s = sender.currentTitle ?? "no title"
// do we want to disable it?
//sender.isEnabled = false
// for now, print the Letter to the debug console
print("Button \(s) was tapped!")
}
}
結果:
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.