I am Building a simple hangman game. I have built a simple keyboard out of UIButtons. The keyboard is inside a subview and each row is a seperate subview.
The Buttons are not clickable, I can get the top row working but then the other rows get pushed apart.
I have tried setting the NSLayoutConstraint height anchors and it will push the UIButtons out of their corresponding Views.
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
}
You have a bug in the place where you calculate the frame of the buttons to be placed in each row.
// your code
let frame = CGRect(x: col * width, y: row * height, width: width, height: height)
You don't need to change the y
position of the button. It can just be 0 here since each row is within its own view.
// corrected code
let frame = CGRect(x: col * width, y: 0, width: width, height: height)
You should also set a height constraint for each row you have. All the buttons which were added were out of bounds of the parent view. This becomes visible when rowView.clipsToBounds = true
is set. That's why your buttons weren't working.
I believe there is an issue in the loop as well running more than it needs to, but I haven't checked it.
Solution to your issue: I tried fixing your sample code and it works. Check here
Also try using a collection or stack view to solve the problem when you find time.