How to set specific width to stackview child, swift? - swift

I'm creating a stackview, inside which there are 5 sub views. (3 custom views divided by 2 separator views). Two separator views's width must be 1 and every sub views must be center and fill equally.
Here's my current code and result which doesn't look nice.
var stackView: UIStackView = {
let view = UIStackView()
view.axis = .horizontal
view.alignment = .center
view.distribution = .fillEqually
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
func setUpView() {
containerView.addSubview(stackView)
stackView.topAnchor.constraint(equalTo: containerView.topAnchor, constant: 8).isActive = true
stackView.bottomAnchor.constraint(equalTo: containerView.bottomAnchor, constant: -8).isActive = true
stackView.leadingAnchor.constraint(equalTo: homeLogoImageView.trailingAnchor , constant: 18).isActive = true
stackView.trailingAnchor.constraint(equalTo: awayLogoImageView.leadingAnchor, constant: -18).isActive = true
stackView.addArrangedSubview(homeWinView)
stackView.addArrangedSubview(seperator1)
stackView.addArrangedSubview(drawView)
stackView.addArrangedSubview(seperator2)
stackView.addArrangedSubview(awayWinView)
seperator1.widthAnchor.constraint(equalToConstant: 1).isActive = true
seperator1.heightAnchor.constraint(equalToConstant: 60).isActive = true
seperator2.widthAnchor.constraint(equalToConstant: 1).isActive = true
seperator2.heightAnchor.constraint(equalToConstant: 60).isActive = true
}
Output:
Expectation:

fillEqually doesn't work, when you just want some of your views in your stack view to fill equally.
Change fillEqually to fill, and add the "equally" part using constraints yourself.
NSLayoutConstraint.activate([
homeWinView.widthAnchor.constraint(equalTo: drawView.widthAnchor),
homeWinView.widthAnchor.constraint(equalTo: awayWinView.widthAnchor),
])

Related

UIScrollView does not scroll + second scrollView shows nothing

it is unpleasant for me but I need some help with my UIScrollViews. They are both arranged subviews of a stackView on my MainVC.
The weird thing is that only one of them is showing content, although I used the same code for both scrollViews. The second problem is that they do not scroll, here is my code:
class HomeVC: UIViewController, UIScrollViewDelegate {
var views = [UIImageView]()
//StackView
let stackView = UIStackView()
let topView = UIScrollView()
let bottomView = UIScrollView()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = MyColors.soft_pink
prepare_data()
print(views.count)
}
//MARK: - GUI
func setUpStackView() {
view.addSubview(stackView)
stackView.alignment = .center
stackView.axis = .vertical
stackView.distribution = .equalCentering
stackView.spacing = 5
stackView.addArrangedSubview(topView)
stackView.addArrangedSubview(bottomView)
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 20).isActive = true
stackView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 10).isActive = true
stackView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -10).isActive = true
stackView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -25).isActive = true
setUpTopView()
setUpBottomView()
}
func setUpTopView() {
topView.delegate = self
topView.layer.cornerRadius = 25
topView.layer.masksToBounds = true
topView.layer.borderWidth = 10
topView.layer.borderColor = UIColor.white.cgColor
topView.contentMode = .scaleAspectFit
topView.showsHorizontalScrollIndicator = false
topView.isPagingEnabled = true
topView.contentSize = CGSize(width: topView.frame.width * CGFloat(views.count),height: topView.frame.height)
for i in 0..<views.count {
topView.addSubview(views[i])
views[i].frame = CGRect(x: topView.frame.width * CGFloat(i), y: 0, width: 350, height: 250)
views[i].layer.cornerRadius = 25
}
topView.translatesAutoresizingMaskIntoConstraints = false
topView.leadingAnchor.constraint(equalTo: stackView.leadingAnchor, constant: 10).isActive = true
topView.trailingAnchor.constraint(equalTo: stackView.trailingAnchor, constant: -10).isActive = true
topView.heightAnchor.constraint(equalToConstant: 250).isActive = true
}
func setUpBottomView() {
bottomView.delegate = self
bottomView.layer.cornerRadius = 25
bottomView.layer.masksToBounds = true
bottomView.layer.borderWidth = 10
bottomView.layer.borderColor = UIColor.white.cgColor
bottomView.contentMode = .scaleAspectFit
bottomView.showsHorizontalScrollIndicator = false
bottomView.isPagingEnabled = true
bottomView.contentSize = CGSize(width: bottomView.frame.width * CGFloat(views.count),height: bottomView.frame.height)
for i in 0..<views.count {
bottomView.addSubview(views[i])
views[i].frame = CGRect(x: bottomView.frame.width * CGFloat(i), y: 0, width: 350, height: 250)
views[i].layer.cornerRadius = 25
}
bottomView.translatesAutoresizingMaskIntoConstraints = false
bottomView.leadingAnchor.constraint(equalTo: stackView.leadingAnchor, constant: 10).isActive = true
bottomView.trailingAnchor.constraint(equalTo: stackView.trailingAnchor, constant: -10).isActive = true
bottomView.heightAnchor.constraint(equalToConstant: 250).isActive = true
}
func prepare_data() {
for x in 1...6 {
let woman = UIImage(named: "woman\(x)")
let womanView = UIImageView(image: woman)
womanView.contentMode = .scaleAspectFill
views.append(womanView)
}
setUpStackView()
}
}
Could someone please be so kind and tell me what I have wrong? Thank you in advance!
Try to debug by printing values. bottomView.frame.width was zero at initialisation, so update subviews in viewDidLayoutSubviews. There are more ways you can look for frame update detection.
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
DispatchQueue.main.async {
self.updateSubviewFrames()
}
}
func updateSubviewFrames() {
print(bottomView.frame)
bottomView.contentSize = CGSize(width: bottomView.frame.width * CGFloat(views.count),height: bottomView.frame.height)
for i in 0..<views.count {
views[i].frame = CGRect(x: bottomView.frame.width * CGFloat(i), y: 0, width: 350, height: 250)
views[i].layer.cornerRadius = 25
}
}
You've done a few things wrong...
First, because it's easy -- the reason you don't see anything in your Top scroll view is because you add your image views (from the views array) to topView, and then you add them to bottomView which removes them from topView!
So, you need one array of views for topView and an array of other views for bottomView.
Next, you are using auto-layout / constraints to size and position your stack view, then trying to use the frames of the stack view's arranged subviews -- for example:
bottomView.contentSize = CGSize(width: bottomView.frame.width * CGFloat(views.count),height: bottomView.frame.height)
but, that's all being done in functions called from viewDidLoad() when auto-layout has not yet configured the view frames.
You're also adding your scroll view's as arranged subviews of the stack view, but then constraining them to the stack view (which is not the way to do it):
topView.leadingAnchor.constraint(equalTo: stackView.leadingAnchor, constant: 10).isActive = true
As a side note: the easiest way to manage a paged scroll view is to embed the "pages" (your image views) in a horizontal stack view, setting the width of each view to the width of the scroll view's Frame Layout Guide (minus desired spacing).
Here's a modified version of your code to take a look at:
class HomeVC: UIViewController, UIScrollViewDelegate {
var topViews = [UIImageView]()
var botViews = [UIImageView]()
//StackView
let stackView = UIStackView()
let topView = UIScrollView()
let bottomView = UIScrollView()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemPink // MyColors.soft_pink
prepare_data()
setUpStackView()
setUpTopAndBottomViews()
}
func prepare_data() {
// create 6 image views
// for BOTH Top and Bottom scroll views
// I'll assume you have "woman" and "man" images
for x in 1...6 {
let woman = UIImage(named: "woman\(x)")
let man = UIImage(named: "man\(x)")
let womanView = UIImageView(image: woman)
womanView.contentMode = .scaleAspectFill
topViews.append(womanView)
let manView = UIImageView(image: man)
manView.contentMode = .scaleAspectFill
botViews.append(manView)
}
}
func setUpStackView() {
// setup stack view
view.addSubview(stackView)
// .alignment should be .fill, not .center
//stackView.alignment = .center
stackView.alignment = .fill
stackView.axis = .vertical
// let's use .fillEqually instead of .equalCentering
//stackView.distribution = .equalCentering
stackView.distribution = .fillEqually
stackView.spacing = 5
stackView.addArrangedSubview(topView)
stackView.addArrangedSubview(bottomView)
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 20).isActive = true
stackView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 10).isActive = true
stackView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -10).isActive = true
stackView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -25).isActive = true
}
func setUpTopAndBottomViews() {
// setup both scroll views with the same properties
[topView, bottomView].forEach { v in
v.delegate = self
v.layer.cornerRadius = 25
v.layer.masksToBounds = true
v.layer.borderWidth = 10
v.layer.borderColor = UIColor.white.cgColor
v.showsHorizontalScrollIndicator = false
v.isPagingEnabled = true
}
// let's use auto-layout here
// if you want horizontal paged scrolling, easiest route is to
// use a horizontal stack view
// create a stack view
let topStack = UIStackView()
topStack.translatesAutoresizingMaskIntoConstraints = false
topStack.spacing = 10
// add stack view to topView
topView.addSubview(topStack)
for i in 0..<topViews.count {
topStack.addArrangedSubview(topViews[i])
topViews[i].layer.cornerRadius = 25
// set view width and height equal to
// topView's Frame Layout Guide
// allowing for 5-pts "padding" on the sides
topViews[i].widthAnchor.constraint(equalTo: topView.frameLayoutGuide.widthAnchor, constant: -10.0).isActive = true
topViews[i].heightAnchor.constraint(equalTo: topView.frameLayoutGuide.heightAnchor).isActive = true
}
// now we'll set constraints on the stack view to
// topView's Content Layout Guide
NSLayoutConstraint.activate([
topStack.topAnchor.constraint(equalTo: topView.contentLayoutGuide.topAnchor),
topStack.leadingAnchor.constraint(equalTo: topView.contentLayoutGuide.leadingAnchor, constant: 5.0),
topStack.trailingAnchor.constraint(equalTo: topView.contentLayoutGuide.trailingAnchor, constant: -5.0),
topStack.bottomAnchor.constraint(equalTo: topView.contentLayoutGuide.bottomAnchor),
])
// same thing with the bottom scroll view
// create a new stack view
let botStack = UIStackView()
botStack.translatesAutoresizingMaskIntoConstraints = false
botStack.spacing = 10
// add stack view to bottomView
bottomView.addSubview(botStack)
for i in 0..<botViews.count {
botStack.addArrangedSubview(botViews[i])
botViews[i].layer.cornerRadius = 25
// set view width and height equal to
// bottomView's Frame Layout Guide
// allowing for 5-pts "padding" on the sides
botViews[i].widthAnchor.constraint(equalTo: bottomView.frameLayoutGuide.widthAnchor, constant: -10.0).isActive = true
botViews[i].heightAnchor.constraint(equalTo: bottomView.frameLayoutGuide.heightAnchor).isActive = true
}
// now we'll set constraints on the stack view to
// bottomView's Content Layout Guide
NSLayoutConstraint.activate([
botStack.topAnchor.constraint(equalTo: bottomView.contentLayoutGuide.topAnchor),
botStack.leadingAnchor.constraint(equalTo: bottomView.contentLayoutGuide.leadingAnchor, constant: 5.0),
botStack.trailingAnchor.constraint(equalTo: bottomView.contentLayoutGuide.trailingAnchor, constant: -5.0),
botStack.bottomAnchor.constraint(equalTo: bottomView.contentLayoutGuide.bottomAnchor),
])
}
}

Creating scrollview with 20 labels with programmatically in Swift

I am pretty new in Programming Swift so I am trying to achieve a scrollable task includes 20 labels inside. I made it with storyboard but programmatically I failed..
Thanks in advance, a green view background and 20 labels orange. Can anyone show me how ?
So i am not very fond of advertising, but raywenderlich has an awesome tutorial on how to do this. It is quite easy, but it comes down to a scrollable view and a tableview with navigation links.
https://www.raywenderlich.com/5662524-your-second-ios-and-swiftui-app
Again, i am not advertising, just sharing some videos that really helped me understand Swift!
And for answering your question:
Use
Scrollview { put here your to-do code
} end of body
There is your scrollable view with all the labels!
Below code will print your label 20 times with scrollview.
import UIKit
class ViewController: UIViewController {
let scrollView = UIScrollView()
var a = 0
var COnstant: CGFloat = 20
override func viewDidLoad() {
super.viewDidLoad()
scrollView.translatesAutoresizingMaskIntoConstraints = false
scrollView.backgroundColor = .green
self.view.addSubview(scrollView)
scrollView.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 0).isActive = true
scrollView.topAnchor.constraint(equalTo: view.topAnchor, constant: 0).isActive = true
scrollView.rightAnchor.constraint(equalTo: view.rightAnchor, constant: 0).isActive = true
scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 0).isActive = true
while a<20 {
let label = UILabel()
label.backgroundColor = .orange
label.translatesAutoresizingMaskIntoConstraints = false
label.textColor = .black
label.text = "I'm label \(a)"
label.textAlignment = .center
scrollView.addSubview(label)
label.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor, constant: 0).isActive = true
label.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor, constant: 0).isActive = true
label.widthAnchor.constraint(equalTo: scrollView.widthAnchor, constant: 0).isActive = true
label.topAnchor.constraint(equalTo: scrollView.topAnchor, constant: COnstant).isActive = true
COnstant += 40
a = a+1
if a >= 20 { // if bottomanchor is not set then our scroll view doesnot work
label.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor, constant: 0).isActive = true
}
}
}
}

How to resize content view in UIScrollView programmatically?

Here is the issue: I need to add a view containing other subviews to a scroll view programmatically. In addition, I also need to make the frame of such a view to stick to the bounds of the main super view. The code below shows the approach I was trying to implement, but as you can see from the pictures below the 'contentView' is not updating its frame size when the screen is rotated. The initial code is taken from here for demonstration purposes. Any help would be greatly appreciated.
import UIKit
class TestViewController : UIViewController {
var contentViewSize = CGSize()
let contentView: UIView = {
let view = UIView()
view.backgroundColor = .magenta
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
let labelOne: UILabel = {
let label = UILabel()
label.text = "Scroll Top"
label.backgroundColor = .red
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
let labelTwo: UILabel = {
let label = UILabel()
label.text = "Scroll Bottom"
label.backgroundColor = .green
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
let scrollView: UIScrollView = {
let v = UIScrollView()
v.translatesAutoresizingMaskIntoConstraints = false
v.backgroundColor = .cyan
return v
}()
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
contentViewSize = view.bounds.size
labelTwo.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: (contentViewSize.width - labelTwo.frame.size.width - 16.0)).isActive = true
labelTwo.topAnchor.constraint(equalTo: contentView.topAnchor, constant: (contentViewSize.height - labelTwo.frame.size.height - 16.0)).isActive = true
}
override func viewDidLoad() {
super.viewDidLoad()
contentViewSize = view.bounds.size
view.backgroundColor = .yellow
self.view.addSubview(scrollView)
scrollView.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 8.0).isActive = true
scrollView.topAnchor.constraint(equalTo: view.topAnchor, constant: 8.0).isActive = true
scrollView.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -8.0).isActive = true
scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -8.0).isActive = true
scrollView.addSubview(contentView)
contentView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor, constant: 16).isActive = true
contentView.topAnchor.constraint(equalTo: scrollView.topAnchor, constant: 16).isActive = true
contentView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor, constant: 16).isActive = true
contentView.rightAnchor.constraint(equalTo: scrollView.rightAnchor, constant: 16).isActive = true
contentView.addSubview(labelOne)
labelOne.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 16.0).isActive = true
labelOne.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 16.0).isActive = true
contentView.addSubview(labelTwo)
labelTwo.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: (contentViewSize.width - labelTwo.frame.size.width - 16.0)).isActive = true
labelTwo.topAnchor.constraint(equalTo: contentView.topAnchor, constant: (contentViewSize.height - labelTwo.frame.size.height - 16.0)).isActive = true
labelTwo.rightAnchor.constraint(equalTo: contentView.rightAnchor, constant: -16.0).isActive = true
labelTwo.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -16.0).isActive = true
}
}
You made some odd changes to the code from the answer you linked to. Also, that answer is a little out-of-date.
Here's a better example. Assuming you want only vertical scrolling, this will:
add a Cyan scroll view, inset 8-pts on each side from the safe-area
add a Magenta "content view" to the scroll view, with 16-pts on each side constrained to the scroll view's contentLayoutGuide, with a width 32-pts less than the scroll view's frame (16-pts on each side)
add a label at top-left of the content view
add a label at bottom-right of the content view
constrain the bottom label 1500-pts below the top label (so it will scroll vertically)
Code:
class ScrollTestViewController : UIViewController {
let contentView: UIView = {
let view = UIView()
view.backgroundColor = .magenta
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
let labelOne: UILabel = {
let label = UILabel()
label.text = "Scroll Top"
label.backgroundColor = .red
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
let labelTwo: UILabel = {
let label = UILabel()
label.text = "Scroll Bottom"
label.backgroundColor = .green
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
let scrollView: UIScrollView = {
let v = UIScrollView()
v.translatesAutoresizingMaskIntoConstraints = false
v.backgroundColor = .cyan
return v
}()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .yellow
// add the scroll view
self.view.addSubview(scrollView)
// add contentView to scroll view
scrollView.addSubview(contentView)
// add two labels to contentView
contentView.addSubview(labelOne)
contentView.addSubview(labelTwo)
// respect safe-area
let g = view.safeAreaLayoutGuide
// if you want to ignore the safe-area (bad idea),
// use this instead
//let g = view!
//scrollView.contentInsetAdjustmentBehavior = .never
// we're going to constrain the contentView to the scroll view's content layout guide
let scg = scrollView.contentLayoutGuide
NSLayoutConstraint.activate([
// constrain scrollView Top / Leading / Trailing / Bottom to view (safe-area)
// with 8-pts on each side
scrollView.topAnchor.constraint(equalTo: g.topAnchor, constant: 8.0),
scrollView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 8.0),
scrollView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -8.0),
scrollView.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: -8.0),
// constrain contentView Top / Leading / Trailing / Bottom to scroll view's Content Layout Guide
// with 16-pts on each side
contentView.topAnchor.constraint(equalTo: scg.topAnchor, constant: 16.0),
contentView.leadingAnchor.constraint(equalTo: scg.leadingAnchor, constant: 16.0),
contentView.trailingAnchor.constraint(equalTo: scg.trailingAnchor, constant: -16.0),
contentView.bottomAnchor.constraint(equalTo: scg.bottomAnchor, constant: -16.0),
// if we only want vertical scrolling, constrain contentView Width
// to scrollView's Frame Layout Guide minus 32-pts (because we have 16-pts on each side)
contentView.widthAnchor.constraint(equalTo: scrollView.frameLayoutGuide.widthAnchor, constant: -32.0),
// constrain labelOne Top / Leading 16-pts to contentView Top / Leading
// (so it shows up at top-left corner)
labelOne.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 16.0),
labelOne.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 16.0),
// constrain labelTwo Bottom / Trailing 16-pts to contentView Bottom / Trailing
// (so it shows up at bottom-right corner)
labelTwo.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -16.0),
labelTwo.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -16.0),
// constrain labelTwo Top to labelOne Bottom + 1500-pts
// so we'll have some vertical scrolling to get to it
labelTwo.topAnchor.constraint(equalTo: labelOne.bottomAnchor, constant: 1500.0),
])
}
}

Constraint to set max height for items inside stackView

I have a stackView with UiViews boxes inside as you can see in the picture. I would like to set a max height of 50 or less for the boxes inside as they look too big right now but i also would like to keep the 1:1 ratio so that they would be square. I've tried to set a height constraint to the stackView but when i do that, the aspect ratio of 1:1 is being ignored.
Here's what it looks like right now:
And here's the code:
let stackview = UIStackView(arrangedSubviews: letterBoxes)
stackview.axis = .horizontal
stackview.spacing = 5
stackview.distribution = .fillEqually
stackview.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(stackview)
NSLayoutConstraint.activate([
stackview.topAnchor.constraint(equalTo: secondLine.bottomAnchor, constant: c/2 +
20),
stackview.centerXAnchor.constraint(equalTo: self.view.centerXAnchor),
stackview.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: 10)
])
for box in letterBoxes{
box.aspectRation(1.0/1.0).isActive = true
}
Edit: i want the box to auto-resize and not overflow like the following (each time there's a different amount of boxes. so for example when there are only 3 boxes i want that the max size will be 50 but when there are 6 or higher number I want it to auto-resize so that it will fit the screen and not overflow. once I take the leading constraint off it overflows) :
The following will do the job.
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let letterBoxes = [UIView(), UIView(), UIView()]
for box in letterBoxes {
box.translatesAutoresizingMaskIntoConstraints = false
box.backgroundColor = .systemBlue
view.addSubview(box)
let heightConstraint = box.heightAnchor
.constraint(equalToConstant: 50)
heightConstraint.priority = .defaultHigh
NSLayoutConstraint.activate([
box.widthAnchor.constraint(equalTo: box.heightAnchor),
heightConstraint
])
}
let stackview = UIStackView(arrangedSubviews: letterBoxes)
stackview.axis = .horizontal
stackview.spacing = 5
stackview.distribution = .fillEqually
stackview.alignment = .center
stackview.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(stackview)
NSLayoutConstraint.activate([
stackview.centerYAnchor.constraint(equalTo: view.centerYAnchor),
stackview.centerXAnchor.constraint(equalTo: view.centerXAnchor),
stackview.leadingAnchor.constraint(
greaterThanOrEqualTo: view.leadingAnchor,
constant: 10
),
stackview.trailingAnchor.constraint(
lessThanOrEqualTo: view.trailingAnchor,
constant: -10
)
])
}
}
The point is to make the priorities of the heighConstraints for the boxes to be less than required.
So when there are not a lot of boxes, they will be of size 50, but when there are many, the height constraints will be ignored.
Also note that the stackview's leadingAnchor and the trailingAnchor constraint keeps the boxes in the screen bounds.
The following screenshots show the result:

container view constraint issues programmatically

I am trying to include a container UIView right above my tabBar however it doesnt seem to be displayed on my view because what i believe to be constraint issues. I would like my container view to look so in the view.
However my container is not showing in the view at all.
Here is my code:
self.mapContainer.layer.cornerRadius = 8
self.mapContainer.backgroundColor = UIColor.cyan
self.mapContainer.translatesAutoresizingMaskIntoConstraints = false
self.mapContainer.autoresizingMask = [.flexibleWidth, .flexibleHeight]
view.addSubview(self.mapContainer)
//constraints of the map view
let heightTabBar = self.tabBarController?.tabBar.frame.size.height
self.mapContainer.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant : (heightTabBar)! + 220).isActive = true
self.mapContainer.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
//self.topContainer.heightAnchor.constraint(equalTo: view.heightAnchor).isActive = true
self.mapContainer.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.3).isActive = true
//self.mapContainer.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true
self.mapContainer.leadingAnchor.constraint(equalTo: view.trailingAnchor, constant: 20).isActive = true
self.mapContainer.trailingAnchor.constraint(equalTo: view.leadingAnchor, constant: 40).isActive = true
where am I going wrong here to where my container is not displaying
?
Problem:
You push your view with a positive value at your bottom anchor outside of the view. That's the reason why you don't see it.
Solution:
Instead of adding with (equalTo: view.bottomAnchor, constant : (heightTabBar)! + 220) you should subtract with self.mapContainer.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant : -(heightTabBar! + 220)).isActive = true at your bottomAnchor. You should also make your leading anchor of the container depend on the leading anchor of the view, the same for the trailing anchor. Also you need to set a negative value for your constant to your trailing anchor to add padding on the right side. Keep in mind: If you want to push from top and left then add and if you want to push from right and bottom then subtract
This should help you (Hints are in the code comments):
import UIKit
class ViewController: UIViewController {
var mapContainer:UIView!
override func loadView() {
super.loadView()
view.backgroundColor = .white
self.mapContainer = UIView()
self.mapContainer.layer.cornerRadius = 8
self.mapContainer.backgroundColor = UIColor.cyan
self.mapContainer.translatesAutoresizingMaskIntoConstraints = false
self.mapContainer.autoresizingMask = [.flexibleWidth, .flexibleHeight]
view.addSubview(self.mapContainer)
//constraints of the map view
let heightTabBar = self.tabBarController?.tabBar.frame.size.height
// You need to subtract to push from the bottom of the view
self.mapContainer.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant : -(heightTabBar! + 220)).isActive = true
self.mapContainer.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
self.mapContainer.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.3).isActive = true
// Your leading anchor of map container should depend on the leading anchor of the view
self.mapContainer.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20).isActive = true
// Your trailingAnchor anchor of map container should depend on the trailingAnchor anchor of the view
// Here you need to subtract to push from the right
self.mapContainer.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20).isActive = true
}
}
Result should look similar to this:
Result on iPhone5S simulator: