Equal spacing between UIButtons using visual format language - ios

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,

Related

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 arrange 3 UIButtons side-by-side in swift3 programmatically

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

swift Autolayout 3 button set horizontally width manage according to text

I have to set button in one child view max button is three and minimum is zero
I Try to set Horizontally button but its take always equal width , I am Try to set left aligned , with depend on button text width , and one more problem is if any one button is missing i mean to say if button array count is two and one then not show any one , please give me solution
Here is my code
let button1 = UIButton()
let button2 = UIButton()
let button3 = UIButton()
print(getChannelname)
for var i = 0; i < getChannelname.count; i++ {
if(i == 0)
{
button1.translatesAutoresizingMaskIntoConstraints = false
button1.backgroundColor = UIColor.greenColor();
button1.tintColor = UIColor.blackColor()
button1.titleLabel?.textColor = UIColor.blackColor()
button1.setTitle("#\(getChannelname[i])" , forState: UIControlState.Normal)
button1.titleLabel!.font = UIFont(name: "Arial", size: 12)
}else if (i == 1)
{
button2.translatesAutoresizingMaskIntoConstraints = false
button2.backgroundColor = UIColor.yellowColor();
button2.tintColor = UIColor.blackColor()
button2.titleLabel?.textColor = UIColor.blackColor()
button2.setTitle("#\(getChannelname[i])" , forState: UIControlState.Normal)
button2.titleLabel!.font = UIFont(name: "Arial", size: 12)
}else
{
button3.translatesAutoresizingMaskIntoConstraints = false
button3.backgroundColor = UIColor.greenColor();
button3.tintColor = UIColor.blackColor()
button3.titleLabel?.textColor = UIColor.blackColor()
button3.setTitle("#\(getChannelname[i])" , forState: UIControlState.Normal)
button3.titleLabel!.font = UIFont(name: "Arial", size: 12)
}
}
cell.viewChannel!.addSubview(button1)
cell.viewChannel!.addSubview(button2)
cell.viewChannel!.addSubview(button3)
cell.viewChannel!.layer.borderWidth = 2
let leftConstraint = NSLayoutConstraint(item: button1,
attribute: .Bottom,
relatedBy: .Equal,
toItem: cell.viewChannel!,
attribute: .Bottom,
multiplier: 1.0,
constant: 0.0);
cell.viewChannel!.addConstraint(leftConstraint);
cell.viewChannel!.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-0-[b1]-0-[b2(==b1)]-0-[b3(==b1)]-0-|", options: NSLayoutFormatOptions.AlignAllBottom, metrics: nil, views: ["b1": button1, "b2": button2, "b3": button3]))
array of name like this
**ONE**
(
channelsnamelonggergo,
General
)
**two**
(
trndnl,
jive,
Banter
)
**Three**
(
channelsnamelonggergo,
Trending,
Local
)
**Four**
(
scoobydoobyparampaa,
channelsnamesarebigmerge,
enternewchannelsjive
)
Please Give me soultion.
If I have well understood the question, you can manage to do this by using an UIStackView and creating the three UIButtons inside it.
Here is a link : https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIStackView_Class_Reference/

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.

3 views next to each other programmatically constraints

I'm trying to create a custom callOutView and for this i have a bubbleView, which is a subclass of a UIView in this view i want to create 3 views next to eachother. First an imageView, which has an static width and height on 60. Then an UIView which has a dynamically width depending on the total width of the bubbleView. Then in the end there is another imageView with an static height and width again at 60. How can i achieve this? i've tried below with snapKit, but does not seem to work.
Illustration of what i want
Code i've tried
bubbleView = BubbleView()
bubbleView?.clipsToBounds = true
bubbleView?.layer.masksToBounds = true
self.addSubview(bubbleView!)
let logoImageView = UIImageView()
logoImageView.contentMode = UIViewContentMode.ScaleAspectFill
logoImageView.image = UIImage(data: logoImage!)
bubbleView?.contentView.addSubview(logoImageView)
logoImageView.backgroundColor = UIColor.whiteColor()
let informationView = UIView()
bubbleView?.contentView.addSubview(informationView)
informationView.backgroundColor = UIColor.redColor()
let discView = UIImageView()
discView.contentMode = UIViewContentMode.ScaleAspectFill
discView.image = UIImage(data: logoImage!)
bubbleView?.contentView.addSubview(discView)
discView.backgroundColor = UIColor.whiteColor()
logoImageView.snp_makeConstraints { (make) -> Void in
make.top.equalTo(bubbleView!).offset(0)
make.left.equalTo(bubbleView!).offset(0)
make.height.equalTo(60)
make.right.equalTo(informationView)
}
informationView.snp_makeConstraints { (make) -> Void in
make.top.equalTo(bubbleView!).offset(0)
make.left.equalTo(logoImageView).offset(0)
make.height.equalTo(60)
make.right.equalTo(discView).offset(0)
}
discView.snp_makeConstraints { (make) -> Void in
make.top.equalTo(bubbleView!).offset(0)
make.left.equalTo(informationView).offset(0)
make.height.equalTo(60)
make.right.equalTo(bubbleView!)
}
You need to set
width constraint on leftView and rightView equal to 60.
middleView.leading equal leftView.trailing
middleView.trailing equal rightView.leading.
all.height equal to 60.
all.top equal parent.top
You can try this in Playground.
import UIKit
import XCPlayground
let parentView = UIView()
parentView.frame.size = CGSize(width: 450, height: 60)
parentView.backgroundColor = UIColor.whiteColor()
let leftView = UIView()
leftView.translatesAutoresizingMaskIntoConstraints = false
leftView.backgroundColor = .blackColor()
let rightView = UIView()
rightView.translatesAutoresizingMaskIntoConstraints = false
rightView.backgroundColor = .grayColor()
let middleView = UIView()
middleView.translatesAutoresizingMaskIntoConstraints = false
middleView.backgroundColor = .lightGrayColor()
// add subview
parentView.addSubview(leftView)
parentView.addSubview(middleView)
parentView.addSubview(rightView)
// config constraints
leftView.leadingAnchor.constraintEqualToAnchor(parentView.leadingAnchor).active = true
leftView.topAnchor.constraintEqualToAnchor(parentView.topAnchor).active = true
leftView.heightAnchor.constraintEqualToConstant(60).active = true
leftView.widthAnchor.constraintEqualToConstant(60).active = true
rightView.trailingAnchor.constraintEqualToAnchor(parentView.trailingAnchor).active = true
rightView.topAnchor.constraintEqualToAnchor(parentView.topAnchor).active = true
rightView.heightAnchor.constraintEqualToConstant(60).active = true
rightView.widthAnchor.constraintEqualToConstant(60).active = true
middleView.leadingAnchor.constraintEqualToAnchor(leftView.trailingAnchor).active = true
middleView.trailingAnchor.constraintEqualToAnchor(rightView.trailingAnchor).active = true
middleView.topAnchor.constraintEqualToAnchor(parentView.topAnchor).active = true
middleView.bottomAnchor.constraintEqualToAnchor(parentView.bottomAnchor).active = true
XCPlaygroundPage.currentPage.liveView = parentView
try the following:
let bubbleView = UIView()
bubbleView.translatesAutoresizingMaskIntoConstraints = false
let logoImageView = UIImageView()
logoImageView.translatesAutoresizingMaskIntoConstraints = false
logoImageView.backgroundColor = .darkGrayColor()
bubbleView.addSubview(logoImageView)
let informationView = UIView()
informationView.translatesAutoresizingMaskIntoConstraints = false
informationView.backgroundColor = .lightGrayColor()
bubbleView.addSubview(informationView)
let discImageView = UIImageView()
discImageView.translatesAutoresizingMaskIntoConstraints = false
discImageView.backgroundColor = .darkGrayColor()
bubbleView.addSubview(discImageView)
let views = ["logoImageView": logoImageView, "informationView": informationView, "discImageView": discImageView]
bubbleView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|[logoImageView(60)][informationView][discImageView(60)]|", options: [.AlignAllBottom, .AlignAllTop], metrics: nil, views: views))
bubbleView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|[logoImageView(60)]|", options: [], metrics: nil, views: views))
// helper constraint so that the information view is at least two times the imageviews' width
bubbleView.addConstraint(NSLayoutConstraint(item: informationView, attribute: .Width, relatedBy: .GreaterThanOrEqual, toItem: logoImageView, attribute: .Width, multiplier: 2.0, constant: 0.0))
view.addSubview(bubbleView)
view.addConstraint(NSLayoutConstraint(item: bubbleView, attribute: .CenterX, relatedBy: .Equal, toItem: view, attribute: .CenterX, multiplier: 1.0, constant: 0.0))
view.addConstraint(NSLayoutConstraint(item: bubbleView, attribute: .CenterY, relatedBy: .Equal, toItem: view, attribute: .CenterY, multiplier: 1.0, constant: 0.0))
result:

Resources