Horizontal auto-layout constraint - ios

In a subclass of UITableViewCell, I am trying to use auto layout.
Here is what I want to do :
Here is my code which doesn't work : only one view is shown
import Foundation
class CustomCell: UITableViewCell {
func configureCell() {
backgroundColor = UIColor.yellowColor()
let redColorView = UIView()
redColorView.backgroundColor = UIColor.redColor()
redColorView.translatesAutoresizingMaskIntoConstraints = false
let blueColorView = UIView()
blueColorView.backgroundColor = UIColor.blueColor()
blueColorView.translatesAutoresizingMaskIntoConstraints = false
addSubview(blueColorView)
addSubview(redColorView)
let viewsDictionary = ["blue":blueColorView,"red":redColorView]
let layout = NSLayoutFormatOptions(rawValue: 0)
let horizontalContraint:[NSLayoutConstraint] = NSLayoutConstraint.constraintsWithVisualFormat("|-10-[blue]-10-[red]-10-|", options: layout, metrics: nil, views: viewsDictionary)
let verticalContraint_1:[NSLayoutConstraint] = NSLayoutConstraint.constraintsWithVisualFormat("V:|-10-[blue]-10-|", options: layout, metrics: nil, views: viewsDictionary)
let verticalContraint_2:[NSLayoutConstraint] = NSLayoutConstraint.constraintsWithVisualFormat("V:|-10-[red]-10-|", options: layout, metrics: nil, views: viewsDictionary)
self.addConstraints(verticalContraint_1)
self.addConstraints(verticalContraint_2)
self.addConstraints(horizontalContraint)
}
}

The problem is that your horizontal constraints are ambiguous. They are insufficient to resolve to exactly one layout. So, the system has to pick arbitrarily from among the possibilities.
In particular, |-10-[blue]-10-[red]-10-| doesn't specify how wide blue and red should be. Assuming the superview is wider than 30 points, any number of solutions are possible. blue could be 0 width and red could be the superview's width minus 30. Or vice versa. Or anything in between. The only effective constraint on the two subviews' widths is that they add up to the superview's width minus 30. Since you say that only one is visible, presumably the other has been assigned 0 width.
As you've noticed, you can explicitly constrain the width one of the subview or you can specify that they have equal width to each other (or some other ratio).
If the views had intrinsic size, or if they had subviews with intrinsic size and their widths were constrained based on their subviews, then things like content-hugging and compression-resistance priorities would come into play. But plain UIViews like you're using have no intrinsic size, so they don't.

Related

Swift VFL to pin an item a fixed distance from superview's bottom margin

so I am doing something super simple, I would like to have an object be a fixed distance from the bottom of its superview's margin. say 20 ios points. I try the following:
addConstraintsWithFormat("V:|[v0]-20-|", views: nameLabel)
where addConstraintsWithFormat() is defined as following (from Brian Voong's facebook app):
extension UIView {
func addConstraintsWithFormat(format: String, views: UIView...) {
var viewsDictionary = [String: UIView]()
for (index, view) in views.enumerate() {
let key = "v\(index)"
viewsDictionary[key] = view
view.translatesAutoresizingMaskIntoConstraints = false
}
addConstraints(NSLayoutConstraint.constraintsWithVisualFormat(format, options: NSLayoutFormatOptions(), metrics: nil, views: viewsDictionary))
}
}
what this does, though, is place the object right next to the top margin of the superview, as opposed to 20 points next to the bottom margin of its superview.
Not really sure how to accomplish what i am aiming for.
The | represents the superview, so you need to delete the first one so that your view is not pinned to the top of its superview:
"V:[v0]-20-|"

How to set constraints for a reusable UIView in Swift with auto layout?

I'm trying to build a reusable UIView whose width should equal to its superview in Swift.
Since the size of its superview varies, I think I have to set constraints for it with auto layout.
But I can't figure out how to do it programmatically in Swift.
Here is the code for the reusable subview:
import UIKit
class bottomMenu: UIView {
#IBOutlet var bottomMenu: UIView!
required init(coder aDecoder: NSCoder) {
super.init(coder:aDecoder)
NSBundle.mainBundle().loadNibNamed("bottomMenu", owner: self, options: nil)
bottomMenu.backgroundColor = UIColor.redColor()
//How to make the width of the bottom Menu equal to its superview?
self.addSubview(self.bottomMenu)
}
}
Can anyone show me how to make it?
Thanks
You can override didMoveToSuperview() method in your UIView subclass and add the constraints there:
override func didMoveToSuperview() {
super.didMoveToSuperview()
backgroundColor = UIColor.redColor()
let views = ["view" : self];
self.setTranslatesAutoresizingMaskIntoConstraints(false)
self.superview?.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("|[view]|", options: NSLayoutFormatOptions(0), metrics: nil, views: views))
self.superview?.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|[view]|", options: NSLayoutFormatOptions(0), metrics: nil, views: views))
}
Add constraints for element inside. For each element add constraint for top, bottom, left and right. If you have any images that needs to be same size add width and height as well. If you can post the screenshot of the UIView I will add more information and will be able to be more helpful.
Also take a look at http://www.raywenderlich.com/50317/beginning-auto-layout-tutorial-in-ios-7-part-1 if you are new to autolayout.
The following code gives the loaded view the same height and width as the super view. (not sure what you wanted for height)
bottomMenu.setTranslatesAutoresizingMaskIntoConstraints(false)
//Views to add constraints to
let views = Dictionary(dictionaryLiteral: ("bottomMenu",bottomMenu))
// Horizontal constraints
let horizontalConstraints = NSLayoutConstraint.constraintsWithVisualFormat("H:|[bottomMenu]|", options: nil, metrics: nil, views: views)
self.addConstraints(horizontalConstraints)
// Vertical constraints
let verticalConstraints = NSLayoutConstraint.constraintsWithVisualFormat("V:|[bottomMenu]|", options: nil, metrics: nil, views: views)
self.addConstraints(verticalConstraints)

Any way to know the custom keyboard frame in viewDidLoad or viewWillAppear?

I'm developing a custom keyboard extension and noticed when I call self.inputView.frame.size.width in either viewDidLoad or viewWillAppear, it returns 0 or an incorrect number. Only in viewDidAppear is it correct. This is a problem because I need to update the size of elements in the keyboard based on the available width and height. Right now I am handling this by detecting the frame size in viewWillLayoutSubviews, which is called several times before it is correct. This works, but the user can see the elements changing size in the keyboard - the frame is not correct until after it has been presented to the user. This is also a problem because I need to update the scroll position once the elements are their correct size, so I'm forced to delay that until viewDidAppear. This isn't a great user experience with things jumping around after it's been presented.
Is there any way I could know the height and width of the keyboard earlier in the life cycle so I can set the size of the elements before they become visible to the user?
I am using a XIB to create the keyboard interface which utilizes Auto Layout. This is how I add it to the keyboard:
let keyboardNib = UINib(nibName: "KeyboardView", bundle: nil)
let keyboardInterface = keyboardNib.instantiateWithOwner(self, options: nil)[0] as! UIView
keyboardInterface.setTranslatesAutoresizingMaskIntoConstraints(false)
self.inputView.addSubview(keyboardInterface)
let verticalConstraints: [NSLayoutConstraint] = NSLayoutConstraint.constraintsWithVisualFormat("V:|-0-[keyboardInterface]-0-|", options: NSLayoutFormatOptions(0), metrics: nil, views: ["keyboardInterface": keyboardInterface, "view": view]) as! [NSLayoutConstraint]
let horizontalConstraints: [NSLayoutConstraint] = NSLayoutConstraint.constraintsWithVisualFormat("H:|-0-[keyboardInterface]-0-|", options: NSLayoutFormatOptions(0), metrics: nil, views: ["keyboardInterface": keyboardInterface]) as! [NSLayoutConstraint]
self.inputView.addConstraints(verticalConstraints)
self.inputView.addConstraints(horizontalConstraints)

Swift - constraint issues with UIScrollView (programmatically)

I recently added a scrollview to my viewcontroller. However, this caused my layout to mess up completely.
Here's an image below.
(I gave the UIScrollView a temporary red background, to display, that it's clearly taking the full screen)
now. I have a bunch of things in this view. But to keep it simple I will focus on the top blue bar, which in my app is called "topBar"
First of, I define it in my class.
var topBar = UIView()
I remove the auto sizing, give it a color and add it to my scrollview.
//----------------- topBar ---------------//
topBar.setTranslatesAutoresizingMaskIntoConstraints(false)
topBar.backgroundColor = UIColor.formulaBlueColor()
self.scrollView.addSubview(topBar)
add it to my viewsDictionary:
var viewsDictionary = [ "topBar":topBar]
add the height to my metricsDictionary:
let metricsDictionary = ["topBarHeight":6]
set the height in a sizing constraint.
//sizing constraints
self.scrollView.addConstraints(
NSLayoutConstraint.constraintsWithVisualFormat(
"V:[topBar(topBarHeight)]", options: NSLayoutFormatOptions(0), metrics: metricsDictionary, views: viewsDictionary))
And finally the part that doesn't work. I /attempt/ to make it the full width of "scrollView"
// Horizontal Constraints
self.scrollView.addConstraints(
NSLayoutConstraint.constraintsWithVisualFormat(
"H:|[topBar]|", options: nil, metrics: nil, views: viewsDictionary))
and my vertical constraint to put it at the top.
// Vertical Constraints
self.scrollView.addConstraints(
NSLayoutConstraint.constraintsWithVisualFormat(
"V:|[topBar]", options: nil, metrics: nil, views: viewsDictionary))
Now as for my scrollview, (the one that's probably causing my layout headaches)
It's set up as follows:
as the very first thing in the class:
let scrollView = UIScrollView(frame: UIScreen.mainScreen().bounds)
first thing in my viewDidLoad()
self.view.addSubview(scrollView)
scrollView.setTranslatesAutoresizingMaskIntoConstraints(false)
scrollView.scrollEnabled = true
and lastly my viewDidLayoutSubviews.
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
scrollView.frame = view.bounds
scrollView.contentSize = CGSize(width:2000, height: 5678)
}
^ The width of the contentSize will be changed to the width of the screen (I only want vertical scrolling). But right now that's a minor issue compared to the layout problems I'm having
Any help as to why everything is squeezed together would be greatly appreciated!
I managed to fix it doing the following.
Defining my contentsize in viewDidLayoutSubviews
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
scrollView.frame = view.bounds
scrollView.contentSize = CGSize(width:self.view.bounds.width, height: 5678)
}
and instead of making the view equal to a scrollview, I had to make it a subview of it.
I also had to make a subview of the scrollview, for all my content to work with constraints properly.
self.view.addSubview(scrollView)
scrollView.translatesAutoresizingMaskIntoConstraints = false
contentView.translatesAutoresizingMaskIntoConstraints = false
scrollView.addSubview(contentView)
and all my other objects was made subviews of the "contentView" and not the scrollview.

Programmatically retrieve the width and height of a UIView using Auto Layout and NSLayoutConstraints?

How do you get the width and height of a UIView who's size and position are set using Auto Layout and Apple's Visual Format Language?
Here's the code (view is just the variable from UIViewController):
// Create and add the view
var stageView = UIView()
stageView.setTranslatesAutoresizingMaskIntoConstraints(false) // Since I'm using Auto Layout I turn this off
view.addSubview(stageView)
// Create and add constraints to the containing view
let viewsDictionary = ["stageView":stageView]
let horizontalConstraints: NSArray = NSLayoutConstraint.constraintsWithVisualFormat("H:|-150-[stageView]-150-|", options: NSLayoutFormatOptions(0), metrics: nil, views: viewsDictionary)
let verticalConstraints: NSArray = NSLayoutConstraint.constraintsWithVisualFormat("V:|-100-[stageView]-150-|", options: NSLayoutFormatOptions.AlignAllCenterX, metrics: nil, views: viewsDictionary)
view.addConstraints(horizontalConstraints)
view.addConstraints(verticalConstraints)
println("stageView.frame=\(stageView.frame)")
and got:
stageView.frame=(0.0,0.0,0.0,0.0)
so I tried:
let fittingSize = stageView.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize)
println("fittingSize=\(fittingSize)")
and got:
fittingSize=(0.0,0.0)
I can't seem to find a way to get the size. I'm able to add subviews to stageView that place just fine using Auto Layout and Visual Format Language, but I can't get width and height for stageView which I need to further position those subviews.
Any ideas?
You have a few options:
You can force the layout engine to size the views immediately by calling setNeedsLayout and then call layoutIfNeeded. This is not recommended because it's inefficient and any manual frames required for layout might not have been set yet. You can read more about this approach on my answer to this question.
You can also wait until the subviews have been updated in the view controller:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
println("stageView.frame = \(stageView.frame)")
}
If you want to know within a UIView subclass (or more often, a UITableViewCell subclass, you can check after layoutSubviews has run:
override func layoutSubviews() {
super.layoutSubviews()
println("self.frame = \(self.frame)")
}
You need to check the frame inside viewDidLayoutSubviews.
This function run after constraint calculation
Its suppose to look something like this
override func viewDidLayoutSubviews()
{
super.viewDidLayoutSubviews()
//Print frame here
}

Resources