I have a very basic UICollectionView. Inside my CollectionViewCell, I have a "thumbnailImage" which is just an image. I would like to have a gradient layer that fades from black, to a clear color BUT I would also like to have a UILabel ON TOP of this CAGradient and not underneath. The label is the "MovieTitle". I am programmatically doing everything, including the constraints. How do I perform this? Here is my code
let myView: UIView = {
let view = UIView()
return view
}()
let gradientView: CAGradientLayer = {
let grad = CAGradientLayer()
grad.colors = [UIColor.white.cgColor, UIColor.red.cgColor]
grad.locations = [0.7, 1.2]
return grad
}()
func setupViews() {
thumbnailImageView.addSubview(movieTitle)
addSubview(thumbnailImageView)
thumbnailImageView.addSubview(dividerLine)
thumbnailImageView.addSubview(myView)
myView.layer.addSublayer(gradientView)
addConstrainsWithFormat(format: "H:|[v0]|", views: thumbnailImageView)
addConstrainsWithFormat(format: "V:|[v0]|", views: thumbnailImageView)
thumbnailImageView.addConstrainsWithFormat(format: "H:|[v0]|", views: myView)
thumbnailImageView.addConstrainsWithFormat(format: "V:|[v0]|", views: myView)
thumbnailImageView.addConstrainsWithFormat(format: "H:|[v0]|", views: dividerLine)
thumbnailImageView.addConstrainsWithFormat(format: "V:[v0(0.75)]|", views: dividerLine)
thumbnailImageView.addConstrainsWithFormat(format: "H:|-16-[v0]-16-|", views: movieTitle)
thumbnailImageView.addConstrainsWithFormat(format: "V:[v0(25)]-8-|", views: movieTitle)
}
Try to change the order of your views so that label appears above the view with gradient inside:
myView.layer.addSublayer(gradientView)
thumbnailImageView.addSubview(myView)
thumbnailImageView.addSubview(movieTitle)
thumbnailImageView.addSubview(dividerLine)
addSubview(thumbnailImageView)
You can also insert layers and views below or above already existing layers/views in your hierarchy:
view.insertSubview(subview, at: 0)
view.insertSubview(subview, belowSubview: existingView)
view.insertSubview(subview, aboveSubview: existingView)
layer.insertSublayer(gradientLayer, at: 0)
layer.insertSublayer(gradientLayer, below: anotherLayer)
layer.insertSublayer(gradientLayer, above: anotherLayer)
Related
I've a custom UINavigationItem title view. It has a label pushed to the top of it, which I've enabled to respond to .touchDown and send an action.
However, the taps don't register near the top of the label, presumably because the active region is clipped. So I configured another invisible view (not in navigation item), and set it up as a control, and positioned it above that navigation title view label.
However, it doesn't work unless I set the 'invisible' view's alpha to at least 0.02, because Apple seems to intentionally disable action for a view with an alpha less than that. Unfortunately, against a black screen in dark mode, the 'invisible' hitpad view shows up as a slightly grey rectangle, which is not a good aesthetic.
I guess I could go to some lengths to try to make the button background color match the screen background at that location, but it seems a bit tacky.
What alternatives might I have?
You can simply create a blank png image, and add it in top of your title view. make sure to set the imageView and the title view isUserInteractionEnabled properties to true:
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .green
let imageView = UIImageView()
imageView.isUserInteractionEnabled = true
let tap = UITapGestureRecognizer(
target: self,
action: #selector(tap)
)
imageView.addGestureRecognizer(tap)
imageView.autoresizingMask = [.flexibleHeight, .flexibleWidth]
imageView.image = UIImage(named: "blank")
let titleLabel = UILabel()
titleLabel.text = "Transparent Button"
titleLabel.autoresizingMask = [.flexibleHeight, .flexibleWidth]
titleLabel.addSubview(imageView)
navigationItem.titleView = titleLabel
navigationItem.titleView?.isUserInteractionEnabled = true
}
#objc func tap(_ gesture: UITapGestureRecognizer) {
print(#function)
}
}
Sample project
You can also just add your gesture recognizer directly to your titleView. No need for the transparent image at all unless you need to control the area of the gesture:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .green
let tap = UITapGestureRecognizer(
target: self,
action: #selector(tap)
)
let titleLabel = UILabel()
titleLabel.text = "Transparent Button"
titleLabel.autoresizingMask = [.flexibleHeight, .flexibleWidth]
titleLabel.addGestureRecognizer(tap)
navigationItem.titleView = titleLabel
navigationItem.titleView?.isUserInteractionEnabled = true
}
#objc func tap(_ gesture: UITapGestureRecognizer) {
print(#function)
}
}
This is an adaptation of the #LeoDabus' Accepted answer that works. However it was utterly informed by his explanation and example. The only meaningful change I made to Leo's example was to create a real empty image programmatically, and drop the label generation. Without a real empty UIImage(), the only way to make taps on the region work that I found is to set the image view's background color to non-clear.
func emptyImage(with size: CGSize) -> UIImage?
{
UIGraphicsBeginImageContext(size)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image
}
func configureButtons() {
let imageView = UIImageView(image: emptyImage(with: CGSize(width: view.frame.size.width - 250, height: 44)))
imageView.frame = CGRect(x: 140, y: self.view.safeAreaInsets.top + 50,
width: view.frame.size.width - 250, height: 44)
imageView.isUserInteractionEnabled = true
let tap = UITapGestureRecognizer(target: self, action: #selector(actionEnableTitleEditing))
imageView.addGestureRecognizer(tap)
imageView.autoresizingMask = [.flexibleHeight, .flexibleWidth]
view.addSubview(imageView)
}
I just started swift programming and i'm facing difficulties with visual format constraints. I'm trying to make multiple tables of 3 by 6 rows and columns with and header on top of the table. I have added the names of the row and columns but it's not aligned in the expected (by me) order. The problem in below code is: the line
> addConstraintsWithFormat(format: "H:|-40-[v0][v1][v2]-[v3]-[v4]-|",
> views: cashLabel, pinLabel, idealLabel, houseLabel,
> totalPerPayMethodLabel) is placed in between the rows of
> addConstraintsWithFormat(format: "V:|-[v0(30)]-[v1]-[v2]-[v3]-|",
> views: timePeriodLabel, highBtwLabel, lowBtwLabel, totalPerBtwLabel).
Also the cashLabel has a big gap with the pinLabel. When i remove the (30) from view v0 the line with the cashLabel, pinLabel etc. is placed above the other rows (V:) as expected. Also the cashLabel does not seem to be affected by the H:-40-[v0] etc.
class AccountingCell: UITableViewCell {
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
setupViews()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
let timePeriodLabel: UILabel = {
let label = UILabel()
label.backgroundColor = UIColor.red
label.text = "Header"
label.translatesAutoresizingMaskIntoConstraints = false
label.textAlignment = .center
return label
}()
let highBtwLabel: UILabel = {
let label = UILabel()
label.text = "high vat"
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
let lowBtwLabel: UILabel = {
let label = UILabel()
label.text = "low vat"
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
let cashLabel: UILabel = {
let label = UILabel()
label.text = "Cash"
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
let pinLabel: UILabel = {
let label = UILabel()
label.text = "Pin"
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
let idealLabel: UILabel = {
let label = UILabel()
label.text = "IDEAL"
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
let houseLabel: UILabel = {
let label = UILabel()
label.text = "House"
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
let totalPerBtwLabel: UILabel = {
let label = UILabel()
label.text = "Totaal"
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
let totalPerPayMethodLabel: UILabel = {
let label = UILabel()
label.backgroundColor = UIColor.red
label.text = "Totaal"
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
func setupViews() {
addSubview(timePeriodLabel)
addSubview(highBtwLabel)
addSubview(lowBtwLabel)
addSubview(cashLabel)
addSubview(pinLabel)
addSubview(idealLabel)
addSubview(houseLabel)
addSubview(totalPerBtwLabel)
addSubview(totalPerPayMethodLabel)
addConstraintsWithFormat(format: "H:|[v0]|", views: timePeriodLabel)
addConstraintsWithFormat(format: "H:|-40-[v0][v1][v2]-[v3]-[v4]-|", views: cashLabel, pinLabel, idealLabel, houseLabel, totalPerPayMethodLabel)
addConstraintsWithFormat(format: "H:|[v0]|", views: timePeriodLabel)
addConstraintsWithFormat(format: "H:|[v0]|", views: highBtwLabel)
addConstraintsWithFormat(format: "H:|[v0]|", views: lowBtwLabel)
addConstraintsWithFormat(format: "H:|[v0]|", views: totalPerBtwLabel)
addConstraintsWithFormat(format: "V:|-[v0(30)]-[v1]-[v2]-[v3]-|", views: timePeriodLabel, highBtwLabel, lowBtwLabel, totalPerBtwLabel)
addConstraintsWithFormat(format: "V:|-[v0]-|", views: cashLabel)
addConstraintsWithFormat(format: "V:|-[v0]-|", views: pinLabel)
addConstraintsWithFormat(format: "V:|-[v0]-|", views: idealLabel)
addConstraintsWithFormat(format: "V:|-[v0]-|", views: houseLabel)
addConstraintsWithFormat(format: "V:|-[v0]-|", views: totalPerPayMethodLabel)
}
Here the cell height is 300. And also you have to add more UILabels to fill those tables.
addConstraintsWithFormat(format: "H:|[v0]|", views: timePeriodLabel)
addConstraintsWithFormat(format: "H:|-60-[v0(==v1)][v1(==v2)][v2(==v3)][v3(==v4)][v4]-|", views: cashLabel, pinLabel, idealLabel, houseLabel, totalPerPayMethodLabel)
addConstraintsWithFormat(format: "H:|[v0]|", views: timePeriodLabel)
addConstraintsWithFormat(format: "H:|[v0]|", views: highBtwLabel)
addConstraintsWithFormat(format: "H:|[v0]|", views: lowBtwLabel)
addConstraintsWithFormat(format: "H:|[v0]|", views: totalPerBtwLabel)
addConstraintsWithFormat(format: "V:|[v0]-(30)-[v1(==v2)][v2(==v3)][v3]-|", views: timePeriodLabel, highBtwLabel, lowBtwLabel, totalPerBtwLabel)
addConstraintsWithFormat(format: "V:|[v0]-230-|", views: cashLabel)
addConstraintsWithFormat(format: "V:|[v0]-230-|", views: pinLabel)
addConstraintsWithFormat(format: "V:|[v0]-230-|", views: idealLabel)
addConstraintsWithFormat(format: "V:|[v0]-230-|", views: houseLabel)
addConstraintsWithFormat(format: "V:|[v0]-230-|", views: totalPerPayMethodLabel)
Auto Layout: quick prototyping and shared UI components with Visual Format Language and JSONLayout provides an extensive example of building a calculator layout with VFL.
I would like to arrange four buttons with Visual Format Language around the central X an Y of a view without hard coding any points, preferring to scale with constraints.
I can only achieve a cluster of buttons to align to the bottom margin, how do I centre them with the spacing you see (e.g. ~20 points) without resorting to NSLayoutConstraint?
I did not place them in a stack, they are all separate buttons.
I read that stacks were not a good idea, but it seems like the logical way, otherwise they stretch out vertically.
Ideally I would like to use VFL to make a calculator UI but am trying this first.
#IBDesignable class images_and_constraints: UIButton {
override init(frame: CGRect) {
super.init(frame: frame)
calcButtons()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
calcButtons()
}
private func calcButtons() {
let calcPlus = UIButton()
calcPlus.translatesAutoresizingMaskIntoConstraints = false
calcPlus.setTitle("+", for: .normal)
calcPlus.setTitleColor(UIColor.black, for: .normal)
calcPlus.setTitleColor(UIColor.white, for: .highlighted)
calcPlus.backgroundColor = UIColor.orange
addSubview(calcPlus)
let calcSubtract = UIButton()
calcSubtract.translatesAutoresizingMaskIntoConstraints = false
calcSubtract.setTitle("-", for: .normal)
calcSubtract.setTitleColor(UIColor.black, for: .normal)
calcSubtract.setTitleColor(UIColor.white, for: .highlighted)
calcSubtract.backgroundColor = UIColor.orange
addSubview(calcSubtract)
let calcMultiply = UIButton()
calcMultiply.translatesAutoresizingMaskIntoConstraints = false
calcMultiply.setTitle("x", for: .normal)
calcMultiply.setTitleColor(UIColor.black, for: .normal)
calcMultiply.setTitleColor(UIColor.white, for: .highlighted)
calcMultiply.backgroundColor = UIColor.orange
addSubview(calcMultiply)
let calcDivide = UIButton()
calcDivide.translatesAutoresizingMaskIntoConstraints = false
calcDivide.setTitle("/", for: .normal)
calcDivide.setTitleColor(UIColor.black, for: .normal)
calcDivide.setTitleColor(UIColor.white, for: .highlighted)
calcDivide.backgroundColor = UIColor.orange
addSubview(calcDivide)
let views = ["calcPlus": calcPlus,
"calcSubtract": calcSubtract,
"calcMultiply": calcMultiply,
"calcDivide": calcDivide]
NSLayoutConstraint.activate(NSLayoutConstraint.constraints(withVisualFormat: "H:|-[calcPlus]-[calcSubtract(==calcPlus)]-|",
options: .alignAllBottom,
metrics: nil,
views: views))
NSLayoutConstraint.activate(NSLayoutConstraint.constraints(withVisualFormat: "H:|-[calcMultiply]-[calcDivide(==calcMultiply)]-|",
options: .alignAllTop,
metrics: nil,
views: views))
NSLayoutConstraint.activate(NSLayoutConstraint.constraints(withVisualFormat: "V:[calcSubtract]-[calcDivide(==calcSubtract)]-|",
options: .alignAllCenterX,
metrics: nil,
views: views))
NSLayoutConstraint.activate(NSLayoutConstraint.constraints(withVisualFormat: "H:[calcSubtract]",
options: .alignAllCenterX,
metrics: nil,
views: views))
}
}
Using VFL to center views requires trickery.
Look at this question and particularly this answer for the trick.
For the kind of layout you want, VFL is just not a good fit.
Just one NSLayoutConstraint in addition to VFL would solve it but since you're only interested in VFL, I would suggest you use the trick to center a container view that holds your buttons.
Solution:
func calcButtons() {
//1. Create a container view that will contain your operator buttons
let buttonContainerView = UIView()
buttonContainerView.backgroundColor = UIColor.lightGray
buttonContainerView.translatesAutoresizingMaskIntoConstraints = false
self.addSubview(buttonContainerView)
//Place it vertically in the center of the superview
NSLayoutConstraint.activate(NSLayoutConstraint.constraints(withVisualFormat: "V:[superview]-(<=1)-[childView]",
options: .alignAllCenterX,
metrics: nil,
views: ["superview" : self,
"childView" : buttonContainerView]))
//Place it horizontally in the center of the superview + equal widths to superview
NSLayoutConstraint.activate(NSLayoutConstraint.constraints(withVisualFormat: "H:[superview]-(<=1)-[childView(==superview)]",
options: .alignAllCenterY,
metrics: nil,
views: ["superview" : self,
"childView" : buttonContainerView]))
//2. Create your buttons as you were:
//DRY Fix: Helper function to create button and add it to `buttonContainerView`
func addButton(title: String, selector: Selector? = nil) -> UIButton {
let button = UIButton()
button.backgroundColor = UIColor.orange
button.setTitle(title, for: .normal)
button.setTitleColor(UIColor.black, for: .normal)
button.setTitleColor(UIColor.white, for: .highlighted)
//You might need this later cuz a button gotta do wat a button gotta do
if let selector = selector {
button.addTarget(self, action: selector, for: UIControlEvents.touchUpInside)
}
button.translatesAutoresizingMaskIntoConstraints = false
buttonContainerView.addSubview(button)
return button
}
let calcPlus = addButton(title: "+", selector: #selector(CalculatorView.add))
let calcSubtract = addButton(title: "-")
let calcMultiply = addButton(title: "x")
let calcDivide = addButton(title: "/")
let views = ["calcPlus": calcPlus,
"calcSubtract": calcSubtract,
"calcMultiply": calcMultiply,
"calcDivide": calcDivide]
//Same as before
NSLayoutConstraint.activate(NSLayoutConstraint.constraints(withVisualFormat: "H:|-[calcPlus]-[calcSubtract(==calcPlus)]-|",
options: .alignAllBottom,
metrics: nil,
views: views))
//Same as before
NSLayoutConstraint.activate(NSLayoutConstraint.constraints(withVisualFormat: "H:|-[calcMultiply]-[calcDivide(==calcMultiply)]-|",
options: .alignAllTop,
metrics: nil,
views: views))
/*
Same as before but this time we give a top constraint too
i.e.
"V:|-[calcSubtract]..."
instead of
"V:[calcSubtract]..."
*/
//
NSLayoutConstraint.activate(NSLayoutConstraint.constraints(withVisualFormat: "V:|-[calcSubtract]-[calcDivide(==calcSubtract)]-|",
options: .alignAllCenterX,
metrics: nil,
views: views))
}
In the end I decided on NSLayoutConstraint.activate of which each button would be reliant on the one before it (rows), with the leading (far left for left-to-right readers) button constrained to the one above it.
calculatriceButtons["7"]!.leadingAnchor.constraint(equalTo: guide.leadingAnchor, constant: 1.0),
calculatriceButtons["7"]!.topAnchor.constraint(equalTo: calculatriceButtons["C"]!.bottomAnchor, constant: 1.0),
This was the best way to assure the buttons scaled on all devices.
There is a new alternative to using VFL which is what I use in code now.
Layout Anchors
Each view has different anchors. leading, trailing, top, bottom, etc...
You can use these to create constraints for you...
NSLayoutConstraint.activate([
viewB.leadingAnchor.constraint(equalTo: viewA.leadingAnchor, constant: 20),
viewA.widthAnchor.constraint(equalTo: viewB.widthAnchor)
])
for example.
Stack View
In addition to that there is an even more modern approach which is to use UIStackView. This is a really useful view that takes away the need to add constraints and does it for you.
let stackView = UIStackView(arrangedSubViews: [viewA, viewB])
stackView.spacing = 20
stackView.axis = .horizontal
stackView.alignment = .center
stackView.distribution = .fillEqually
You can also nest stack views to create more complex layouts.
Definitely worth looking in to...
https://developer.apple.com/documentation/uikit/uistackview?changes=_6
Creating your layout
let upperStackView = UIStackView(arrangedSubviews: [topLeft, topRight])
upperStackView.axis = .horizontal
upperStackView.distribution = .fillEqually
upperStackView.spacing = 20
let lowerStackView = UIStackView(arrangedSubviews: [bottomLeft, bottomRight])
lowerStackView.axis = .horizontal
lowerStackView.distribution = .fillEqually
lowerStackView.spacing = 20
let mainStackView = UIStackView(arrangedSubviews: [upperStackView, lowerStackView])
mainStackView.axis = .vertical
mainStackView.distribution = .fillEqually
mainStackView.spacing = 20
view.addSubview(mainStackView)
NSLayoutConstraint.activate([
mainStackView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
mainStackView.centerYAnchor.constraint(equalTo: view.centerYAnchor),
mainStackView.widthAnchor.constraint(equalToConstant: 200),
mainStackView.heightAnchor.constraint(equalToConstant: 200),
])
Why not VFL?
While VFL was a nice first attempt at AutoLayout, I feel that Apple has moved away from it now and are moving towards these more succinct methods of creating AutoLayout constraints.
It still allows you to think in constraints while writing code but provides a slightly more modern approach.
Of course... you can also create UIStackView in Interface Builder also :D
I have some code that I found online that fits my project perfectly. The issue is that I don't use storyboards at all. Instead of using a storyboard file to create the UIView (CustomCallOutView), I just create a class with a subclass of UIView. Here's the code that requires a nib file but I don't want to use them. How do I achieve this? The code is below. Thank you!
func mapView(_ mapView: MKMapView,
didSelect view: MKAnnotationView)
{
// 1
if view.annotation is MKUserLocation
{
// Don't proceed with custom callout
return
}
// 2
let starbucksAnnotation = view.annotation as! StarbucksAnnotation
let views = Bundle.main.loadNibNamed("CustomCalloutView", owner: nil, options: nil)
let calloutView = views?[0] as! CustomCalloutView
calloutView.starbucksName.text = starbucksAnnotation.name
calloutView.starbucksAddress.text = starbucksAnnotation.address
calloutView.starbucksPhone.text = starbucksAnnotation.phone
calloutView.starbucksImage.image = starbucksAnnotation.image
let button = UIButton(frame: calloutView.starbucksPhone.frame)
button.addTarget(self, action: #selector(ViewController.callPhoneNumber(sender:)), for: .touchUpInside)
calloutView.addSubview(button)
// 3
calloutView.center = CGPoint(x: view.bounds.size.width / 2, y: -calloutView.bounds.size.height*0.52)
view.addSubview(calloutView)
mapView.setCenter((view.annotation?.coordinate)!, animated: true)
}
Assuming that CustomCalloutView doesn't do anything sophisticated:
let calloutView = CustomCalloutView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
If you want to create an UIView subclass that is loads a nib you can use the following:
class YourViewWithNib: UIView {
required init?(coder: NSCoder) {
super.init(coder: coder)
loadNib()
}
override init(frame: CGRect) {
super.init(frame: frame)
bounds = frame
loadNib()
}
func loadNib() {
let nib = Bundle.main.loadNibNamed(
"YourNibFileName",
owner: self,
options: nil
)
let view = nib![0] as! UIView
view.translatesAutoresizingMaskIntoConstraints = false
addSubview(view)
// if you are using autolayout
let views = ["nibView": view]
let hconstraints = NSLayoutConstraint.constraints(
withVisualFormat: "H:|[nibView]|",
metrics: nil,
views: views
)
NSLayoutConstraint.activate(hconstraints)
let vconstraints = NSLayoutConstraint.constraints(
withVisualFormat: "V:|[nibView]|",
metrics: nil,
views: views
)
NSLayoutConstraint.activate(vconstraints)
}
}
To add your custom view, you just can either use YourViewWithNib(frame: aFrame) or add a YourViewWithNib view in any XIB or Storyboard.
I have created a UIViewController with a UIStackView (vertical axis) wrapped in a UIScrollView that's pinned to the edges of the root View with auto-layout constraints.
I have also generated a number of UIButtons and added to the arranged subviews of the UIStackView.
I have tried to no avail to centre align the UIButtons in the UIStackView.
I'm not certain what i'm doing wrong.
Here's the code:
import UIKit
class ViewController: UIViewController {
var scrollView: UIScrollView!
var stackView: UIStackView!
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor.whiteColor()
scrollView = UIScrollView()
scrollView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(scrollView)
view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|[scrollView]|", options: .AlignAllCenterX, metrics: nil, views: ["scrollView": scrollView]))
view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|[scrollView]|", options: .AlignAllCenterX, metrics: nil, views: ["scrollView": scrollView]))
stackView = UIStackView()
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.axis = .Vertical
stackView.alignment = .Center
scrollView.addSubview(stackView)
scrollView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|[stackView]|", options: NSLayoutFormatOptions.AlignAllCenterX, metrics: nil, views: ["stackView": stackView]))
scrollView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|[stackView]|", options: NSLayoutFormatOptions.AlignAllCenterX, metrics: nil, views: ["stackView": stackView]))
for _ in 1 ..< 100 {
let vw = UIButton(type: UIButtonType.System)
vw.setTitle("Button", forState: .Normal)
stackView.addArrangedSubview(vw)
}
}
}
An extra equal width constraint between the scrollView and the stackView was needed. Like this:
scrollView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:[stackView(==scrollView)]", options: .AlignAllCenterX, metrics: nil, views: ["stackView": stackView, "scrollView": scrollView]))
That did it for me.
How about:
stackView.Alignment = UIStackViewAlignment.Center
?