I followed Hacking With Swift’s Project 2 and I had very obscure (i.e. I have no clue what’s going on) issues with Interface Builder.
I’ve built the layout as described in the tutorial:
- 3 buttons, constrained to be separated by 30px from each other
- each button contains an image of a flag
- each button is 200×100 in size
When I run Resolve AutoLayout Issues, all buttons get resized to 224×114 and get a padding which can’t be removed by using Interface Builder (all insets are set to 0 and button’s content mode is set to scale to fit). This can only be resolved by applying padding by code:
button1.configuration?.contentInsets = NSDirectionalEdgeInsets.zero
button2.configuration?.contentInsets = NSDirectionalEdgeInsets.zero
button3.configuration?.contentInsets = NSDirectionalEdgeInsets.zero
I’ve compared my Storyboard with the code in HWS repository, but didn’t notice anything different.
Here is the full code:
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var button1: UIButton!
@IBOutlet weak var button2: UIButton!
@IBOutlet weak var button3: UIButton!
var countries = [String]()
var correctAnswer = 0
var answeredQuestions = 0
var score = 0
override func viewDidLoad() {
super.viewDidLoad()
countries += [
"estonia", "france", "germany", "ireland",
"italy", "monaco", "nigeria", "poland",
"russia", "spain", "uk", "us"
]
button1.configuration?.contentInsets = NSDirectionalEdgeInsets.zero
button2.configuration?.contentInsets = NSDirectionalEdgeInsets.zero
button3.configuration?.contentInsets = NSDirectionalEdgeInsets.zero
button1.layer.borderWidth = 1
button2.layer.borderWidth = 1
button3.layer.borderWidth = 1
button1.layer.borderColor = UIColor.lightGray.cgColor
button2.layer.borderColor = UIColor.lightGray.cgColor
button3.layer.borderColor = UIColor.lightGray.cgColor
askQuestion()
}
func askQuestion(action: UIAlertAction! = nil) {
countries.shuffle()
correctAnswer = Int.random(in: 0...2)
let answer = countries[correctAnswer].uppercased()
title = "(answer) – score: (score)"
button1.setImage(UIImage(named: countries[0]), for: .normal)
button2.setImage(UIImage(named: countries[1]), for: .normal)
button3.setImage(UIImage(named: countries[2]), for: .normal)
}
@IBAction func buttonTapped(_ sender: UIButton) {
var title: String
var message: String
var buttonText: String
answeredQuestions += 1
if (sender.tag == correctAnswer) {
title = "Correct"
score += 1
} else {
let answer = countries[correctAnswer].uppercased()
title = "Wrong! That's the flag of (answer)"
score -= 1
}
if (answeredQuestions >= 10) {
message = "Your final score is (score)."
buttonText = "Restart"
answeredQuestions = 0
score = 0
} else {
message = "Your score is (score)."
buttonText = "Continue"
}
let ac = UIAlertController(title: title, message: message, preferredStyle: .alert)
ac.addAction(UIAlertAction(title: buttonText, style: .default, handler: askQuestion))
present(ac, animated: true)
}
}
2
Answers
Two things….
First comment: this is likely being caused by the new Button style. If you change it from
Plain
toDefault
:IB should keep the size at
200 x 100
.Second comment: it’s a much better idea to assign constraints exactly as you want them rather than relying on IB’s "automatically fix the layout."
In iOS 15, the
UIButton.Configuration
API was added, and this added a few more options to IB, as well as changes to how buttons look. The tutorial was likely written before this, and did not take this into account.Currently, every button you add in IB will by default use a
UIButton.Configuration
. This is what gives it the insets, which is part of the default configuration. In addition to setting this in code, it can also be set in IB under "Background Configuration":If you don’t want to use a configuration, and instead want the pre-iOS 15 behaviour, change "Style" to "Default":