Horizontal UIStackView inside UIScrollView not scrolling - ios

I have a horizontal UIStackView with 3 buttons inside UIScrollView. I'd like the stack to scroll horizontally when the user changes the font size (via Accessibility).
Right now, when the user increases the font size, one of the buttons gets squashed.
Here are my constraints:

It seems like the Greater Than or Equal constraint between the scroll view and the button on the right was the problem. I changed it to Equal and it worked.

Make sure the UIScrollView's content size is set up correctly. Here are some useful parameters.
scrollVIew.contentInset
scrollVIew.scrollIndicatorInsets
scrollView.contentSize
When the content view is taller than the scroll view, the scroll view enables vertical scrolling. When the content view is wider than the scroll view, the scroll view enables horizontal scrolling. Otherwise, scrolling is disabled by default. You must set your content view size dynamically so when they change the font, the content view gets wider than the scroll view width. You could wrap your stack view in another UIViewController and treat it as a content view.

Try this working fine my side and scrolling well.
func createHorizontalStackViewWithScroll() {
self.view.addSubview(stackScrollView)
stackScrollView.translatesAutoresizingMaskIntoConstraints = false
stackScrollView.heightAnchor.constraint(equalToConstant: 85).isActive = true
stackScrollView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor).isActive = true
stackScrollView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor).isActive = true
stackScrollView.bottomAnchor.constraint(equalTo: visualEffectViews.topAnchor).isActive = true
stackScrollView.addSubview(stackView)
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.topAnchor.constraint(equalTo: stackScrollView.topAnchor).isActive = true
stackView.leadingAnchor.constraint(equalTo: stackScrollView.leadingAnchor).isActive = true
stackView.trailingAnchor.constraint(equalTo: stackScrollView.trailingAnchor).isActive = true
stackView.bottomAnchor.constraint(equalTo: stackScrollView.bottomAnchor).isActive = true
stackView.heightAnchor.constraint(equalTo: stackScrollView.heightAnchor).isActive = true
stackView.distribution = .equalSpacing
stackView.spacing = 5
stackView.axis = .horizontal
stackView.alignment = .fill
for i in 0 ..< images.count {
let photoView = UIButton.init(frame: CGRect(x: 0, y: 0, width: 85, height: 85))
// set button image
photoView.translatesAutoresizingMaskIntoConstraints = false
photoView.heightAnchor.constraint(equalToConstant: photoView.frame.height).isActive = true
photoView.widthAnchor.constraint(equalToConstant: photoView.frame.width).isActive = true
stackView.addArrangedSubview(photoView)
}
stackView.setNeedsLayout()
}

Related

Can't get StackView to view items properly

I'm trying to add 4 items to a UIStackView, this 4 Items are all a simple square UIView, I added them all to a UIStackView but they won't stay square, it's like the UIStackView squeezes them or something. I tried setting the UIStackView to be the same height of the items, and set it's width to be the height of the items * 4 so I can try and get 1:1 ratio, but nothing worked for me.
The UIView is a simple UIView with background color. I tried to set it's widthAnchor and heightAnchor to 50, but I know the UIStackView has it's own way to size the items in it.
I don't really know what to do about this.
This is my UIStackView setup and constraints:
Setup:
private lazy var optionButtonStack: UIStackView = {
let stack = UIStackView(arrangedSubviews: [self.optionButton1, self.optionButton2, self.optionButton3, self.optionButton4])
stack.translatesAutoresizingMaskIntoConstraints = false
stack.distribution = .fillEqually
stack.axis = .horizontal
stack.spacing = 2.5
return stack
}()
Constraints:
private func setupOptionButtonStack() {
addSubview(optionButtonStack)
NSLayoutConstraint.activate([
optionButtonStack.heightAnchor.constraint(equalToConstant: 50),
optionButtonStack.widthAnchor.constraint(equalToConstant: 200),
optionButtonStack.centerXAnchor.constraint(equalTo: self.centerXAnchor),
optionButtonStack.bottomAnchor.constraint(equalTo: buyNowButton.topAnchor, constant: -8),
])
}
This is the UIView in case this is needed:
private let optionButton1: UIView = {
let view = UIView()
view.backgroundColor = .appBlue
view.translatesAutoresizingMaskIntoConstraints = false
view.widthAnchor.constraint(equalToConstant: 50).isActive = true
view.heightAnchor.constraint(equalToConstant: 50).isActive = true
view.tag = 1
return view
}()
Give the button view a single constraint setting its width equal to its height:
view.widthAnchor.constraint(equalTo: view.heightAnchor).isActive = true
and set the stack view alignment at center.

How can I change height of the view which is present in StackView

I have a custom UIView using for Custom UITabBar whose height is fixed 50 and I am adding a stackview for each button but I need middle button height should be more.
let view: UIView = views[3]
view.heightAnchor.equalToConstant(100.0).isActive = true
let stackView: UIStackView = UIStackView(arrangedSubviews: [views])
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.axis = .horizontal
stackView.distribution = .fillEqually
addSubview(stackView)
stackView.topAnchor.constraint(equalTo: topAnchor).isActive = true
stackView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
stackView.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true
stackView.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true
How can I change the height of a particular view inside the stackView?
I may not understand your problem but you can change anyviews height using constraints as you have already done.
It is same when the view is subview of a stackview. Stackview updates it's size to satisfy its constraint.
You have to have a reference to height constraint of the view inside stackview.
Then you can change it whenever need to.
Also it would be nice to set stackviews alignment property to have desired look.
let heightConstraint = view.heightAnchor.equalToConstant(100.0)
heightConstraint.isActive = true
.
.
.
heightConstraint.constant = 150
You want to change the Alignment property of the stack view...
Assuming 5 views, each with a height constraint of 60, and you want the 4th view (views[3]) to have a height of 100.
StackView Alignment Top:
StackView Alignment Center:
StackView Alignment Bottom:

Horizontal UIStackView - fix center item's size and stretch other items

I am trying to achieve a layout like this:
Essentially, I need the or label to stay in the middle and occupy a fixed width and the lines to stretch out towards the edges of the screen.
Here's what I did in the XIB:
created a horizontal UIStackview, with center alignment.
Set the height constraint of the stackview to 20, distribution to fill.
added two UIView elements(for the gray lines), with a height constraint set to 5.
Added a UILabel between the two UIView elements above.
Added more constraints:
left UIView leads with 0 from superview and trails with 5 to middle label
right UIView leads with 4 from middle label and trails with 0 to superview.
Looks fine on the Interface builder, but on different screen sizes and landscapes, I find the middle "or" label to stretch and kick away the left and right UIViews to make up for the available space, which seems right:
If I set a width constraint of 20 on the middle label, the right UIView stretches unevenly like this:
I know the CHP of the elements matters to some extent here, I have even tried setting CHPs like this:
CHP of the middle label is 251
CHP of left and right UIViews is 250.
This still leaves me with the uneven stretching of right UIView.
What is it that I am doing wrong? Insights much appreciated!
Thanks a lot!
You need to set widthConstraint on UIStackView and the make leftView.widthAnchor = rightView.widthAnchor. Alternatively you can set leading and trailing constraints on UIStackView and then set leftView.widthAnchor = rightView.widthAnchor.
Below is the sample code you can try out in Playgrounds
let leftView = UIView()
leftView.translatesAutoresizingMaskIntoConstraints = false
leftView.backgroundColor = .lightGray
let rightView = UIView()
rightView.translatesAutoresizingMaskIntoConstraints = false
rightView.backgroundColor = .lightGray
let orLabel = UILabel()
orLabel.translatesAutoresizingMaskIntoConstraints = false
orLabel.textColor = .black
orLabel.text = "or"
let stackView = UIStackView(arrangedSubviews: [leftView, orLabel, rightView])
stackView.alignment = .center
stackView.distribution = .fill
stackView.axis = .horizontal
stackView.spacing = 5
stackView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(stackView)
stackView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
stackView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
stackView.heightAnchor.constraint(equalToConstant: 20).isActive = true
stackView.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true
leftView.heightAnchor.constraint(equalToConstant: 5).isActive = true
rightView.heightAnchor.constraint(equalToConstant: 5).isActive = true
leftView.widthAnchor.constraint(equalTo: rightView.widthAnchor).isActive = true
Don't set any leading or trailing constraints...
Set "right view" width constraint equal to "left view" width, and give your stack view a spacing value of 4 or 5.
Storyboard:
Portrait:
Landscape:

Stackview inside Other stackview width issue

I have an Stackview created in IB and it has Vertical orientation. This stackview has equal width to parent view.
Now I am created a Stackview programmatically for example
let stackViewHorizontal = UIStackView()
stackViewHorizontal.axis = UILayoutConstraintAxis.horizontal
stackViewHorizontal.distribution = UIStackViewDistribution.fillEqually
stackViewHorizontal.alignment = UIStackViewAlignment.leading
stackViewHorizontal.spacing = 8
stackViewHorizontal.translatesAutoresizingMaskIntoConstraints = false
stackViewHorizontal.leadingAnchor.constraint(equalTo: mainStackView.leadingAnchor,constant:0)
stackViewHorizontal.trailingAnchor.constraint(equalTo: mainStackView.trailingAnchor,constant:0)
Here mainStackView is a stackview which is created Via IB.and stackviewHorizontal is a stackview that is created programatically.
I am putting to UILabels inside stackViewHorizontal. I expected this will expand to full length and each UiLabel will take 50% of the screen in width since stackview has horizontal axis and distribution is fillEqually.
But I am having a UiLabels next to eachother horizontally. but not taking full width of screen
What I am doing wrong please notify?
Activate constraints, also give it a height:
stackViewHorizontal.leadingAnchor.constraint(equalTo: mainStackView.leadingAnchor,constant:0).isActive = true
stackViewHorizontal.trailingAnchor.constraint(equalTo: mainStackView.trailingAnchor,constant:0).isActive = true
OR
NSLayoutConstraint.activate([
stackViewHorizontal.leadingAnchor.constraint(equalTo: mainStackView.leadingAnchor,constant:0),
stackViewHorizontal.trailingAnchor.constraint(equalTo: mainStackView.trailingAnchor,constant:0)
])
//
let stackViewHorizontal = UIStackView()
stackViewHorizontal.axis = UILayoutConstraintAxis.horizontal
stackViewHorizontal.distribution = UIStackViewDistribution.fillEqually
stackViewHorizontal.alignment = UIStackViewAlignment.leading
stackViewHorizontal.spacing = 8
self.view.addSubview(stackViewHorizontal) //// add it here
stackViewHorizontal.translatesAutoresizingMaskIntoConstraints = false
stackViewHorizontal.leadingAnchor.constraint(equalTo: mainStackView.leadingAnchor, constant: 0).isActive = true
stackViewHorizontal.trailingAnchor.constraint(equalTo: mainStackView.trailingAnchor, constant: 0).isActive = true

UIStackView - Want a percentage to define each item

I want to put two views into a UIStackView. I want the top item to always be 30% of the space no matter what device (or orientation) that it appears. Thus, I want to have the bottom view to be 70%. How do I tell a UIStackView that I want to use a percentage?
Example resulting view
Just set a constraint for the top view.
In the storyboard's Document Outline control-drag from the top view to the stack view and select Equal heights. This will make their heights equal. Now go to the Size Inspector and you should see the constraint. Double click on it and set the multiplier to 0.3. Then update the frames.
If the bottom view doesn't automatically size or it gives you an error telling that you need a height constraint, just repeat the process, but now set the multiplier to 0.7.
Swift 5.2
To implement this programmatically
Setting UIView to always be 30% of the size of it's superview within a UIStackView
viewA.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.3).isActive = true
Full code implementation:
// Configure the StackView
let stackView = UIStackView()
stackView.axis = .vertical
stackView.distribution = .fill
view.addSubview(stackView)
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
stackView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
stackView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
stackView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
// Configure View A
let viewA = UIView()
viewA.backgroundColor = .darkGray
viewA.translatesAutoresizingMaskIntoConstraints = false
// Configure View B
let viewB = UIView()
viewB.backgroundColor = .lightGray
viewB.translatesAutoresizingMaskIntoConstraints = false
// Add Views to StackView
stackView.addArrangedSubview(viewA)
stackView.addArrangedSubview(viewB)
// Set the height percentage multiplier to View A
viewA.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.3).isActive = true
From apple documentation
UIStackViewDistributionFillProportionally A layout where the stack
view resizes its arranged views so that they fill the available space
along the stack view’s axis. Views are resized proportionally based on
their intrinsic content size along the stack view’s axis.
So according to this you should set UIStackView distribution property to UIStackViewDistributionFillProportionally and set intrinsic size, its.
You can get more info here and on Intrinsic content size

Resources