How to arrange 3 UIButtons side-by-side in swift3 programmatically - ios

I want to arrange 3 UIButtons side-by-side in swift3 programmatically,
they should be equal width regardless of device. please be details code with constraints .
here I have tried with constraint , all buttons are in centre together. this is my code,
let addToWishListBtn: UIButton = {
let btn = UIButton()
btn.setTitle("Add to Wish List", for: UIControlState())
btn.setImage(UIImage(named: "service_icon"), for: UIControlState())
btn.setTitleColor(.black, for: UIControlState())
btn.titleLabel?.font = UIFont.boldSystemFont(ofSize: 10)
btn.addTarget(self, action: #selector(addToWishListBtnTarget), for: .touchUpInside)
btn.imageEdgeInsets = UIEdgeInsetsMake(20, 50, 40, 0)
btn.titleEdgeInsets = UIEdgeInsetsMake(20, 0, 0, 0)
btn.backgroundColor = .yellow
btn.translatesAutoresizingMaskIntoConstraints = false
return btn
}()
func addToWishListBtnTarget() {
print("add to wish btn target")
}
let emailToFriendBtn: UIButton = {
let btn = UIButton()
btn.setTitle("Email to Friend", for: .normal)
btn.setImage(UIImage(named:"service_icon"), for: .normal)
btn.setTitleColor(.black, for: .normal)
btn.titleLabel?.textAlignment = .center
btn.titleLabel?.font = UIFont.boldSystemFont(ofSize: 10)
btn.addTarget(self, action: #selector(emailToFriendBtnTarget), for: .touchUpInside)
btn.imageEdgeInsets = UIEdgeInsetsMake(20, 50, 40, 0)
btn.titleEdgeInsets = UIEdgeInsetsMake(20, 0, 0, 0)
btn.translatesAutoresizingMaskIntoConstraints = false
return btn
}()
func emailToFriendBtnTarget() {
print(" email to friend target")
}
let shareBtn: UIButton = {
let btn = UIButton()
btn.setTitle("Share", for: UIControlState())
btn.setImage(UIImage(named: "service_icon"), for: .normal)
btn.setTitleColor(.black, for: UIControlState())
btn.titleLabel?.font = UIFont.boldSystemFont(ofSize: 10)
btn.addTarget(self, action: #selector(shareBtnTarget), for: .touchUpInside)
btn.imageEdgeInsets = UIEdgeInsetsMake(20, 50, 40, 0)
btn.titleEdgeInsets = UIEdgeInsetsMake(20, 0, 0, 0)
btn.translatesAutoresizingMaskIntoConstraints = false
btn.backgroundColor = .purple
// button.imageEdgeInsets = UIEdgeInsetsMake(0, 0, 0, 0)
btn.backgroundColor = .green
return btn
}()
func shareBtnTarget() {
print("share btn target")
}
view.addSubview(addToWishListBtn)``
view.addSubview(emailToFriendBtn)
view.addSubview(shareBtn)
addToWishListBtn.topAnchor.constraint(equalTo: view.topAnchor, constant: 380).isActive = true
addToWishListBtn.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 0).isActive = true
addToWishListBtn.widthAnchor.constraint(equalToConstant: 125).isActive = true
addToWishListBtn.heightAnchor.constraint(equalToConstant: 50).isActive = true
emailToFriendBtn.topAnchor.constraint(equalTo: view.topAnchor, constant: 380).isActive = true
// addToWishListBtn.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 0).isActive = true
emailToFriendBtn.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
emailToFriendBtn.widthAnchor.constraint(equalToConstant: 125).isActive = true
emailToFriendBtn.heightAnchor.constraint(equalToConstant: 50).isActive = true
addToWishListBtn.topAnchor.constraint(equalTo: view.topAnchor, constant: 380).isActive = true
addToWishListBtn.rightAnchor.constraint(equalTo: view.rightAnchor, constant: 0).isActive = true
addToWishListBtn.widthAnchor.constraint(equalToConstant: 125).isActive = true
addToWishListBtn.heightAnchor.constraint(equalToConstant: 50).isActive = true

You need to give following constraints..
btn1 leading to its superview
btn2 leading to btn1 trailing
btn3 leading to btn2 trailing
btn3 trailing to its superview
btn1 equal width to btn2
btn2 equal width to btn3
btn1 top to its superview
btn2 top to its superview
btn3 top to its superview
for all 3 buttons you also need to give height constraints.
You can give constraints by constraintsWithVisualFormat or constraintWithItem.
EDIT:
Take a look...
//all 3 buttons will be in views dict.
let views = ["btn1" : btn1, "btn2" : btn2, "btn3": btn3];
// align btn1 from the top
self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-8-[btn1]", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: views));
// align btn2 from the top
self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-8-[btn2]", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: views));
// align btn3 from the top
self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-8-[btn3]", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: views));
//horizontal constraints
self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-8-[btn1]-8-[btn2]-8-[btn3]-8-|", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: views));
// height constraint if you want to give
self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:[btn1(==30)]", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: views));
self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:[btn2(==30)]", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: views));
self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:[btn3(==30)]", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: views));
//equal width for all 3 btns
self.view.addConstraint(NSLayoutConstraint.init(item: btn1, attribute: .width, relatedBy: .equal, toItem: btn2, attribute: .width, multiplier: 1.0, constant: 0))
self.view.addConstraint(NSLayoutConstraint.init(item: btn2, attribute: .width, relatedBy: .equal, toItem: btn3, attribute: .width, multiplier: 1.0, constant: 0))

You have two options
1) create constraints (Little long and harder)
2) Use Stack View
If your app runs ios9+ You can use UIStackView
so create UIStackView programatically
and add 3 buttons to it it will automatically adjust width accordingly
Here is an example
let labelOne = UILabel(frame: CGRect(x: 0, y: 0, width: 200, height: 21))
labelOne.text = "Hello"
labelOne.backgroundColor = UIColor.red
let labelTwo = UILabel(frame: CGRect(x: 0, y: 0, width: 200, height: 21))
labelTwo.text = "There"
labelTwo.backgroundColor = UIColor.blue
let myStack = UIStackView(arrangedSubviews: [labelOne, labelTwo])
myStack.distribution = .fillEqually
myStack.axis = .horizontal
Enjoy ...
Hope it helps to you

Related

Why is button background changing to blue when button selected?

I have three buttons, when selected they change background color to red - yet a blue background is popping up as well and I can't figure out why (sorry if the reason is obvious, ive only been doing "programming" for 3 weeks. I can't find anything in the code that would explain itI have three buttons, when selected they change background color to red - yet a blue background is popping up as well and I can't figure out why (sorry if the reason is obvious, ive only been doing "programming" for 3 weeks. I can't find anything in the code that would explain it
[enter image description here][1]import UIKit
class ViewController: UIViewController {
var counterEN : Int = 0
var count = Array(1...150).filter { $0 % 1 != 0}.count
var pizzaCount = 0
#IBOutlet weak var pizzaCounter: UILabel!
#IBOutlet weak var textField: UITextField!
#IBAction func test(_ sender: UIButton) {
self.textField.text = sender.currentTitle
}
var lastY: CGFloat = 100
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func buttonClicked(_ sender: UIButton) {
pizzaCount = pizzaCount + 1
pizzaCounter.text = ("Antal pizza: ") + "\(pizzaCount)"
let contentView = UIView()
addViewsTo(contentView, title: sender.currentTitle)
contentView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(contentView)
// Add size constraints to the content view (260, 30)
NSLayoutConstraint(item: contentView, attribute: .width, relatedBy: .equal,
toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: 850.0).isActive = true
NSLayoutConstraint(item: contentView, attribute: .height, relatedBy: .equal,
toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: ` 30.0).isActive = true
// Add position constraints to the content view (horizontal center, 100 from the top)
NSLayoutConstraint(item: contentView, attribute: .top, relatedBy: .equal, toItem: view, attribute: .top, multiplier: 1.0, constant: lastY).isActive = true
NSLayoutConstraint(item: contentView, attribute: .centerX, relatedBy: .equal, toItem: view, attribute: .centerX, multiplier: 1.0, constant: 0.0).isActive = true
// Update last Y position to have the gaps between views to be 10
lastY += 50
}
// Add label and button to the content view
func addViewsTo(_ contentView: UIView, title: String?) {
// Add a label with size of (100, 30)
let label = UILabel()
label.text = title
//label.text = ("1 x 17 ")
label.frame = CGRect(x: 0.0, y: 0.0, width: 100.0, height: 30.0)
contentView.addSubview(label)
let DressingButton = UIButton(type: .system)
DressingButton.frame = CGRect(x: 210, y: 0, width: 40, height: 30)
DressingButton.setTitle("D", for: .normal)
DressingButton.setTitleColor(.black, for: .normal)
DressingButton.setTitleColor(.red, for: .selected)
DressingButton.sendActions(for: .touchUpInside)
contentView.addSubview(DressingButton)
DressingButton.addTarget(self, action: #selector(dressingAction(_:)), for .touchUpInside)
let Chili = UIButton(type: .system)
Chili.frame = CGRect(x: 160, y: 0, width: 40, height: 30)
Chili.setTitle("C", for: .normal)
Chili.setTitleColor(.black, for: .normal)
Chili.backgroundColor = UIColor.white
Chili.setTitleColor(.systemRed, for: .selected)
Chili.sendActions(for: .touchUpInside)
contentView.addSubview(Chili)
Chili.addTarget(self, action: #selector(dressingAction(_:)), for: .touchUpInside)
let Hvidløg = UIButton(type: .system)
Hvidløg.frame = CGRect(x: 110, y: 0, width: 40, height: 30)
Hvidløg.setTitle("H", for: .normal)
Hvidløg.setTitleColor(.black, for: .normal)
Hvidløg.setTitleColor(.red, for: .selected)
Hvidløg.backgroundColor = UIColor.white
Hvidløg.sendActions(for: .touchUpInside)
contentView.addSubview(Hvidløg)
Hvidløg.addTarget(self, action: #selector(dressingAction(_:)), for: .touchUpInside)
let button2 = UIButton(type: .system)
button2.frame = CGRect(x: -40, y: 20, width: 150, height: 30)
button2.setTitle("Alm", for: .normal)
button2.setTitle("Ful", for: .selected)
button2.setTitle("Glu", for: .selected)
// Set button action
button2.addTarget(self, action: #selector(buttonAction(_:)), for: .touchUpInside)
contentView.addSubview(button2)
self.view = view
}
#objc func dressingAction(_ sender:UIButton){
sender.isSelected.toggle()
print("knap")
sender.backgroundColor = UIColor.red
}
#objc func buttonAction(_ sender:UIButton!)
{
sender.isSelected = !sender.isSelected
sender.setTitle("Alm", for: .normal)
if sender.isSelected{
sender.setTitle("Fuld", for: .selected)}
else {
sender.setTitle("Glu", for: .selected)
sender.isSelected = !sender.isSelected
// sender.tintColor = .clear
//// if sender.isSelected{
// sender.setTitleColor(.green, for: .selected)
// }
// else{
// sender.setTitleColor(.blue, for: .selected)
//
func buttonAction(_ button3:UIButton!) {
print("Button tapped"); counterEN += 1; count += 1; print (counterEN); print (count)
}
}
}
}
Please change your button type to .custom and the issue will be solved.
let dressingButton = UIButton(type: .custom)
Try changing your button Type to custom

Swift - Dynamic number of buttons in tableviewcell

Actually i have a project in github. The problem is that i cannot get why when scrolling, the buttons constraints in the cells are going crazy..
I didn't saw any project like this, then i have a reason to share it but i want to give a good example for another people.
I'll be very thankful with any help that drives me to the solution of this problem.
Best regards.
There it is the code for the cell:
import UIKit
class BookTableViewCell: UITableViewCell {
let nameLabel = UILabel()
let detailLabel = UILabel()
var cellButton = UIButton()
var cellLabel = UILabel()
var book : Book!
// MARK: Initalizers
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
}
func configureCellCon(botones:Int, titulo:String, book:Book) {
self.book = book
let marginGuide = contentView.layoutMarginsGuide
// configure titleLabel
contentView.addSubview(nameLabel)
nameLabel.translatesAutoresizingMaskIntoConstraints = false
nameLabel.leadingAnchor.constraint(equalTo: marginGuide.leadingAnchor).isActive = true
nameLabel.topAnchor.constraint(equalTo: marginGuide.topAnchor).isActive = true
nameLabel.trailingAnchor.constraint(equalTo: marginGuide.trailingAnchor).isActive = true
nameLabel.numberOfLines = 0
nameLabel.font = UIFont(name: "AvenirNext-DemiBold", size: 16)
nameLabel.text = book.name
// configure authorLabel
contentView.addSubview(detailLabel)
detailLabel.translatesAutoresizingMaskIntoConstraints = false
detailLabel.leadingAnchor.constraint(equalTo: marginGuide.leadingAnchor).isActive = true
// detailLabel.bottomAnchor.constraint(equalTo: marginGuide.bottomAnchor).isActive = true
detailLabel.trailingAnchor.constraint(equalTo: marginGuide.trailingAnchor).isActive = true
detailLabel.topAnchor.constraint(equalTo: nameLabel.bottomAnchor).isActive = true
detailLabel.numberOfLines = 0
detailLabel.font = UIFont(name: "Avenir-Book", size: 12)
detailLabel.textColor = UIColor.lightGray
detailLabel.text = book.details
var lastButton = UIButton()
if(book.buttonsAttibutes.count == 1) {
let button = UIButton()
button.tag = 0
button.backgroundColor = UIColor.init(white: 0.9, alpha: 0.0)
// button.setBackgroundColor(color: UIColor.white, forState: .normal)
// button.setBackgroundColor(color: UIColor.blue, forState: .normal)
button.setTitle(book.buttonsAttibutes[0].title, for: .normal)
button.setTitleColor(UIColor.blue.withAlphaComponent(0.7), for: .normal)
button.setTitleColor(UIColor.init(white: 0.8, alpha: 1), for: .highlighted)
button.layer.borderWidth = 0
button.layer.borderColor = UIColor.blue.cgColor
contentView.addSubview(button)
button.translatesAutoresizingMaskIntoConstraints = false
contentView.addConstraints([
NSLayoutConstraint(item: button, attribute: .leftMargin, relatedBy: .equal, toItem: contentView, attribute: .leftMargin, multiplier: 1.0, constant: 20),
NSLayoutConstraint(item: button, attribute: .rightMargin, relatedBy: .equal, toItem: contentView, attribute: .rightMargin, multiplier: 1.0, constant: -20),
])
button.topAnchor.constraint(equalTo: detailLabel.bottomAnchor).isActive = true
button.bottomAnchor.constraint(equalTo: marginGuide.bottomAnchor).isActive = true
button.addTarget(self, action:#selector(mostrarMensaje), for:.touchUpInside)
return
}else if(book.buttonsAttibutes.count == 0) {
detailLabel.bottomAnchor.constraint(equalTo: marginGuide.bottomAnchor).isActive = true
return
}
if(book.buttonsAttibutes.count > 0) {
for x in 0...(book.buttonsAttibutes.count - 1) {
let button = UIButton()
button.tag = x
button.backgroundColor = UIColor.init(white: 0.9, alpha: 0.0)
// button.setBackgroundColor(color: UIColor.white, forState: .normal)
// button.setBackgroundColor(color: UIColor.blue, forState: .normal)
button.setTitle(book.buttonsAttibutes[x].title, for: .normal)
button.setTitleColor(UIColor.blue.withAlphaComponent(0.7), for: .normal)
button.setTitleColor(UIColor.init(white: 0.8, alpha: 1), for: .highlighted)
button.layer.borderWidth = 0
button.layer.borderColor = UIColor.blue.cgColor
contentView.addSubview(button)
button.translatesAutoresizingMaskIntoConstraints = false
// button.leadingAnchor.constraint(equalTo: marginGuide.leadingAnchor).isActive = true
// button.trailingAnchor.constraint(equalTo: marginGuide.trailingAnchor).isActive = true
contentView.addConstraints([
NSLayoutConstraint(item: button, attribute: .leftMargin, relatedBy: .equal, toItem: contentView, attribute: .leftMargin, multiplier: 1.0, constant: 20),
NSLayoutConstraint(item: button, attribute: .rightMargin, relatedBy: .equal, toItem: contentView, attribute: .rightMargin, multiplier: 1.0, constant: -20),
])
if(x == 0){
if #available(iOS 11.0, *) {
button.layer.cornerRadius = 8
button.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
} else {
button.layer.cornerRadius = 5
}
button.topAnchor.constraint(equalTo: detailLabel.bottomAnchor).isActive = true
}else if(x == (book.buttonsAttibutes.count - 1)){
if #available(iOS 11.0, *) {
button.layer.cornerRadius = 8
button.layer.maskedCorners = [.layerMinXMaxYCorner, .layerMaxXMaxYCorner]
} else {
button.layer.cornerRadius = 5
}
button.topAnchor.constraint(equalTo: lastButton.bottomAnchor).isActive = true
button.bottomAnchor.constraint(equalTo: marginGuide.bottomAnchor).isActive = true
}else{
button.topAnchor.constraint(equalTo: lastButton.bottomAnchor).isActive = true
}
button.addTarget(self, action:#selector(mostrarMensaje), for:.touchUpInside)
lastButton = button
}
}
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
#objc func mostrarMensaje(sender: UIButton){
let message = self.book.buttonsAttibutes[sender.tag].message
let alertController = UIAlertController(title: "\(self.book.name)", message: message, preferredStyle: UIAlertControllerStyle.alert)
let okAction = UIAlertAction(title: "Cerrar", style: UIAlertActionStyle.default) {
(result : UIAlertAction) -> Void in
}
alertController.addAction(okAction)
if let myViewController = parentViewController {
print(myViewController.title ?? "ViewController without title.")
myViewController.present(alertController, animated: true, completion: nil)
}
}
}
Ok, i went for the "InterfaceBuilder" and did it properly.
However, I'll be reading more about programmatic methods to do it.
thanks to all of you for the guide.
When creating the buttons and constraints programmatically, they are generating more constraints over the same cells making them crash.

How to programmatically add an InputAccessoryView with Autolayout?

I am trying to add a UIView with "Done" button as an input accessory view to the text field.
let view = UIView()
let doneButton = UIButton(type: .Custom)
doneButton.setTitle("Done", forState: .Normal)
doneButton.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(doneButton)
view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:[button]-|", options: NSLayoutFormatOptions.DirectionLeadingToTrailing, metrics: nil, views: ["button":doneButton]))
view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|[button]|", options: NSLayoutFormatOptions.DirectionLeadingToTrailing, metrics: nil, views: ["button":doneButton]))
view.addConstraint(NSLayoutConstraint(item: view, attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.Equal, toItem: doneButton, attribute: NSLayoutAttribute.Height, multiplier: 1, constant: 0)) // Even this does not work
self.emailTextField.inputAccessoryView = view
But however I cannot see the views height being set nor the buttons in the View Hierarchy debugger/inspector in Xcode.
But if I add a view by setting its frame I can see the view being added.
Also I tried setting height constraint forcibly to a constant 21 and it broke some other constraints which I had not added _UIKBAutolayoutHeightConstraint
"<NSLayoutConstraint:0x7fa3c962be50 UIView:0x7fa3c963bf60.height == UIButton:0x7fa3c963c0d0.height + 21>",
"<NSLayoutConstraint:0x7fa3c95e0a90 '_UIKBAutolayoutHeightConstraint' V:[UIView:0x7fa3c963bf60(0)]>"
Any one faced this issue before ?
Swift 3+
You need to specify size of toolbar view in first line.
Don't use 'view' as variable in viewcontroller class since it creates confusion vs self.view
override func viewDidLoad() {
super.viewDidLoad()
let toolBar = UIView(frame: CGRect(x: 0, y: 0, width: view.bounds.width, height: 50))
toolBar.backgroundColor = .gray
let doneButton = UIButton(type: .custom)
doneButton.setTitle("Done", for: .normal)
doneButton.translatesAutoresizingMaskIntoConstraints = false
toolBar.addSubview(doneButton)
toolBar.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:[button]-|",
options: .directionLeadingToTrailing,
metrics: nil,
views: ["button":doneButton]))
toolBar.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|[button]|",
options: .directionLeadingToTrailing,
metrics: nil,
views: ["button":doneButton]))
self.emailTextField.inputAccessoryView = toolBar
}

iOS 9 StackView inside ScrollView does not fill the width of the screen

I'm trying to make a UIScrollView containing a UIStackView with several layers of stack views nested inside. I would like to use AutoLayout, but there is something wrong with what I'm doing, and my brain is getting scrambled trying to figure it out:
import UIKit
class HomeView: UIView {
let looks = sampleLooks
let user = sampleUser
let streamView: UIStackView = UIStackView(arrangedSubviews: [])
let scrollView: UIScrollView = UIScrollView()
func makeButtonWithTitle(title: String, image: UIImage?, selector: String, tag: Int) -> UIButton {
let button = UIButton(type: .System)
button.setImage(image, forState: .Normal)
button.tintColor = UIColor.blackColor()
switch tag {
case 0...10:
button.backgroundColor = UIColor(white: 0.98, alpha: 0.8)
button.titleLabel?.font = UIFont(name: "HelveticaNeue-Thin", size: 30)
default:
button.backgroundColor = UIColor(white: 0.90, alpha: 1.0)
button.titleLabel?.font = UIFont(name: "HelveticaNeue-Thin", size: 40)
}
button.setTitle(title, forState: .Normal)
button.tag = tag
return button
}
func makeMessageView(senderImage: UIImage, senderHandle: String, text: String) -> UIView {
let textView = UILabel()
textView.text = text
textView.font = UIFont(name: "HelveticaNeue-Thin", size: 20)
let senderLabel = UILabel()
senderLabel.text = senderHandle
textView.font = UIFont(name: "HelveticaNeue-Thin", size: 20)
let textStackView = UIStackView(arrangedSubviews:[senderLabel, textView])
textStackView.axis = .Horizontal
textStackView.alignment = .Fill
textStackView.distribution = .Fill
let postView = UIView()
postView.addSubview(textStackView)
postView.backgroundColor = UIColor(white: 0.98, alpha: 0.8)
return postView
}
required init?(coder:NSCoder) {
super.init(coder:coder)
self.contentMode = .ScaleToFill
self.backgroundColor = UIColor(patternImage: UIImage(named: "background")!)
self.streamView.spacing = 20.0
self.streamView.translatesAutoresizingMaskIntoConstraints = false
self.streamView.axis = .Vertical
self.streamView.alignment = .Fill
self.streamView.distribution = .FillEqually
for look in self.looks {
let lookImageView = UIImageView(image: look.photo.photo)
lookImageView.contentMode = .ScaleAspectFit
lookImageView.clipsToBounds = true
let postView = self.makeMessageView(
look.user.photo.photo, senderHandle: look.user.handle, text: look.post)
let likeButton = self.makeButtonWithTitle(
" Like", image: UIImage(named: "like"), selector: "", tag: 0)
let commentButton = self.makeButtonWithTitle(
" Comment", image: UIImage(named: "SMS"), selector: "", tag: 1)
let shareButton = self.makeButtonWithTitle(
" Share", image: UIImage(named: "upload"), selector: "", tag: 2)
let buttonsView = UIStackView(arrangedSubviews: [likeButton, commentButton, shareButton])
buttonsView.distribution = .FillEqually
let lookView = UIStackView(arrangedSubviews:[lookImageView, postView, buttonsView])
lookView.axis = .Vertical
lookView.distribution = .Fill
lookView.alignment = .Fill
self.streamView.addArrangedSubview(lookView)
}
self.scrollView.addSubview(self.streamView)
self.scrollView.frame = UIScreen.mainScreen().bounds
self.scrollView.showsVerticalScrollIndicator = false
self.scrollView.showsHorizontalScrollIndicator = false
self.addSubview(self.scrollView)
}
}
So, what I would like this code to do is to give me a scrollable stack of nested stacks that covers the width of the screen (it's ok if the images are clipped, but I'd like them to cover the screen without distortion). What it actually gives me is a scrollable set of images where the width of the first image (seemingly) determines the width of the stack. I'm actually not sure if that's what's going on, but the top level UIStackView is not covering the width of the screen.
I think it has something to do with the UIScrollView has no intrinsic width, so the stack view inside it decides its own size. I say this because if I put the stack view directly in the parent view, it covers the display, but then as you might expect, there is no scrolling...
You need to set some constraints between the scrollview and the UIStackView it contains, at the moment you don't have any.
These three are enough to make the inner UIStackView the same size of the scrollview:
Horizontal constraint between the scrollview and the UIStackView, no space between them
Vertical constraint between the scrollview and the UIStackView, no space between them, this way you'll be able to scroll for the full height of the UIStackView
Same width for the scrollview and the UIStackView, this way the UIStackView will match the scrollview width
And this is the code:
//You already have these two lines:
//scrollView.addSubview(streamView)
//streamView.translatesAutoresizingMaskIntoConstraints = false;
//Add this one too:
scrollView.translatesAutoresizingMaskIntoConstraints = false;
scrollView.addConstraints(
NSLayoutConstraint.constraintsWithVisualFormat("V:|[innerView]|",
options: NSLayoutFormatOptions(rawValue:0),
metrics: nil,
views: ["innerView":streamView]))
scrollView.addConstraints(
NSLayoutConstraint.constraintsWithVisualFormat("H:|[innerView]|",
options: NSLayoutFormatOptions(rawValue:0),
metrics: nil,
views: ["innerView":streamView]))
scrollView.addConstraint(
NSLayoutConstraint(item: scrollView,
attribute: .Width,
relatedBy: .Equal,
toItem: streamView,
attribute: .Width,
multiplier: 1.0,
constant: 0))
Alternatively, you can extend UIView with a method that embed your view in a scrollview:
extension UIView {
func embedInScrollView()->UIView{
let cont=UIScrollView()
self.translatesAutoresizingMaskIntoConstraints = false;
cont.translatesAutoresizingMaskIntoConstraints = false;
cont.addSubview(self)
cont.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|[innerView]|", options: NSLayoutFormatOptions(rawValue:0),metrics: nil, views: ["innerView":self]))
cont.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|[innerView]|", options: NSLayoutFormatOptions(rawValue:0),metrics: nil, views: ["innerView":self]))
cont.addConstraint(NSLayoutConstraint(item: self, attribute: .Width, relatedBy: .Equal, toItem: cont, attribute: .Width, multiplier: 1.0, constant: 0))
return cont
}
}
And use it this way:
let scrollView = streamView.embedInScrollView()
Edit: Fixed last constraint in the first snippet.

Equal spacing between UIButtons using visual format language

I have a bunch of UIButtons that I want to space out evenly in a container view, right now I have this constraint for spacing:
someView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-(>=0)-[M]-(>=0)-[T]-(>=0)-[W]-(>=0)-[T]-(>=0)-[F]-(>=0)-[S]-(>=0)-[S]-(>=0)-|", options: NSLayoutFormatOptions.AlignAllCenterY, metrics: nil, views: buttonsArray))
However this makes the buttons look like this:
The problem is what I want for spacing is calculated in this way:
spacing = (someView.frame.width - (someView.frame.height * 0.6) * 7) / 8
someView.frame.height * 0.6 is the side length of the buttons. I am not sure what to do.
Here is a simple code which does exactly distribute the spaces between buttons in a view, I hope this will help you figure out for your use case,
let containerView = UIView(frame: CGRect.zero)
containerView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(containerView)
let M = UIButton(type: .System)
M.translatesAutoresizingMaskIntoConstraints = false
M.backgroundColor = UIColor.lightGrayColor()
M.setTitle("M", forState: .Normal)
containerView.addSubview(M)
let T = UIButton(type: .System)
T.translatesAutoresizingMaskIntoConstraints = false
T.setTitle("T", forState: .Normal)
T.backgroundColor = UIColor.lightGrayColor()
containerView.addSubview(T)
let W = UIButton(type: .System)
W.translatesAutoresizingMaskIntoConstraints = false
W.setTitle("W", forState: .Normal)
W.backgroundColor = UIColor.lightGrayColor()
containerView.addSubview(W)
let Th = UIButton(type: .System)
Th.translatesAutoresizingMaskIntoConstraints = false
Th.setTitle("T", forState: .Normal)
Th.backgroundColor = UIColor.lightGrayColor()
containerView.addSubview(Th)
let F = UIButton(type: .System)
F.translatesAutoresizingMaskIntoConstraints = false
F.setTitle("F", forState: .Normal)
F.backgroundColor = UIColor.lightGrayColor()
containerView.addSubview(F)
let S = UIButton(type: .System)
S.translatesAutoresizingMaskIntoConstraints = false
S.setTitle("S", forState: .Normal)
S.backgroundColor = UIColor.lightGrayColor()
containerView.addSubview(S)
let Su = UIButton(type: .System)
Su.translatesAutoresizingMaskIntoConstraints = false
Su.setTitle("Su", forState: .Normal)
Su.backgroundColor = UIColor.lightGrayColor()
containerView.addSubview(Su)
let views = [
"M": M,
"T": T,
"W": W,
"Th":Th,
"F": F,
"S": S,
"Su": Su
]
let horizontalSpacing = 20
let cornerMargin = 30
let metrics = [
"horizontalSpacing": horizontalSpacing,
"cornerMargin": cornerMargin
]
views.values.forEach { view in
view.clipsToBounds = true
view.layer.cornerRadius = 10
}
let verticalCenter = NSLayoutConstraint(item: containerView, attribute: .CenterY, relatedBy: .Equal, toItem: view, attribute: .CenterY, multiplier: 1.0, constant: 0)
let horizontalCenter = NSLayoutConstraint(item: containerView, attribute: .CenterX, relatedBy: .Equal, toItem: view, attribute: .CenterX, multiplier: 1.0, constant: 0)
view.addConstraint(verticalCenter)
view.addConstraint(horizontalCenter)
let horizontalFormat = "H:|-(==cornerMargin)-[M]-horizontalSpacing-[T]-horizontalSpacing-[W]-horizontalSpacing-[Th]-horizontalSpacing-[F]-horizontalSpacing-[S]-horizontalSpacing-[Su]-(==cornerMargin)-|"
let horizontalConstraints = NSLayoutConstraint.constraintsWithVisualFormat(horizontalFormat, options: .AlignAllCenterY, metrics: metrics, views: views)
view.addConstraints(horizontalConstraints)
let verticalFormat = "V:|-[M]-|"
let verticalConstraints = NSLayoutConstraint.constraintsWithVisualFormat(verticalFormat, options: .AlignAllCenterY, metrics: metrics, views: views)
view.addConstraints(verticalConstraints)
And, here is the result,

Resources