I have a UIViewController
with four UIButtons
(2 x 2) on it that I laid out in interface builder that worked perfectly. I'm going to have a free and ad-supported version of my app, so I need to redo that scene to load based on whether the app is a paid or ad-supported version. Based on that, I'm attempting to use Visual Formatting Language to lay out the view. I'm getting incorrect values for my UIButton
heights despite accounting for them when I calculate them. I can't figure out my mistake (or omission?).
Here's a screenshot of my interface builder.
I do not have constraints on my buttons in interface builder, but I do have IBOutlets
wired to MyViewController
. MyViewController
is in a navigation controller and has a tab bar at the bottom.
I created a method called layoutButtons
that I call in viewDidLoad
just after super.viewDidLoad()
. Here it is:
func layoutButtons() {
// Configure layout constraints
// Remove interface builder constraints from storyboard
view.removeConstraints(view.constraints)
// create array to dump constraints into
var allConstraints = [NSLayoutConstraint]()
// determine screen size
let screenSize: CGRect = UIScreen.mainScreen().bounds
let navBarRect = navigationController!.navigationBar.frame
let navBarHeight = navBarRect.height
let tabBarRect = tabBarController!.tabBar.frame
let tabBarHeight: CGFloat = tabBarRect.height
// calculate button width based on screen size
// padding for left + middle + right = 8.0 + 8.0 + 8.0 = 24.0
let buttonWidth = (screenSize.width - 24.0) / 2
/*
My buttons are extending under the top & bottom layout guides despite accounting
for them when I set the buttonHeight.
*/
// padding for top + middle + bottom = 8.0 + 8.0 + 8.0 = 24.0
let buttonHeight = (screenSize.height - topLayoutGuide.length - bottomLayoutGuide.length - 24.0) / 2
// create dictionary of metrics
let metrics = ["buttonWidth": buttonWidth,
"buttonHeight": buttonHeight,
"navBarHeight": navBarHeight,
"tabBarHeight": tabBarHeight,
"bannerAdWidth": bannerAdWidth,
"bannerAdHeight": bannerAdHeight]
// create dictionary of views
var views: [String : AnyObject] = ["firstButton": firstButton,
"secondButton": secondButton,
"thirdButton": thirdButton,
"fourthButton": fourthButton,
"topLayoutGuide": topLayoutGuide,
"bottomLayoutGuide": bottomLayoutGuide]
let topRowHorizontalConstraints = NSLayoutConstraint.constraintsWithVisualFormat(
"H:|-[firstButton(buttonWidth)]-[secondButton(buttonWidth)]-|",
options: [.AlignAllCenterY],
metrics: metrics,
views: views)
allConstraints += topRowHorizontalConstraints
let bottomRowHorizontalConstraints = NSLayoutConstraint.constraintsWithVisualFormat(
"H:|-[thirdButton(buttonWidth)]-[fourthButton(buttonWidth)]-|",
options: [.AlignAllCenterY],
metrics: metrics,
views: views)
allConstraints += bottomRowHorizontalConstraints
let leftColumnVerticalConstraints = NSLayoutConstraint.constraintsWithVisualFormat(
"V:|[topLayoutGuide]-[firstButton(buttonHeight)]-[thirdButton(buttonHeight)]-[bottomLayoutGuide]|",
options: [],
metrics: metrics,
views: views)
allConstraints += leftColumnVerticalConstraints
let rightColumnVerticalConstraints = NSLayoutConstraint.constraintsWithVisualFormat(
"V:|[topLayoutGuide]-[secondButton(buttonHeight)]-[fourthButton(buttonHeight)]-[bottomLayoutGuide]|",
options: [],
metrics: metrics,
views: views)
allConstraints += rightColumnVerticalConstraints
NSLayoutConstraint.activateConstraints(allConstraints)
}
I've fiddled with my buttonHeight
variable, but every iteration I've tried results in the buttons extending under the topLayoutGuide
and bottomLayoutGuide
. Here's what it looks like at runtime:
I welcome any suggestions where to look for my mistake. Thank you for reading.
I originally followed a Ray Wenderlich's Visual Format Language tutorial and the tutorial's setup was similar to mine in that the
subViews
were on a storyboard and wired toMyViewController
withIBOutlets
.Out of desperation, I nuked the storyboard and created the
UIButtons
in code. Along the way, I discovered my problem was I was setting the image on the button before it was laid out. The moral of the story is don't set images onUIButtons
until they're laid out!Below is the code called from a method in my
viewDidLoad
that lays out a 2 x 2 grid of buttons: