Adjusting UIView Height Inside UIStackView Control
If you are trying to achieve a 20x20 square like the one below:
Then all you have to do is add another UIView to your green view. Make that UIView's size 20x20pts. Then change the small view background to green and the larger view's background to clear.
That said, this solution doesn't require a stackview and possibly over-complicates your UI. I would recommend just using autolayout instead.
Stackviews are most useful when
- you want to compensate for a change in device orientation,
- want to stack a lot of things on top of each other,
- want to create a very complicated layout with many elements but that has
patterns you can repeat, or - simply want to be able to remove an element or add an element in with the sizing correcting itself
That list isn't exhaustive, but should give you an idea of when and why you'd use a stack view.
Outside of a prototype cell
This behavior is trivial. You can simply set the constraints on your view:
'
And then set the constraints on the stackview:
Hope this helps, cheers!
How to update the size of stackView after adding subviews (programatically)?
You should call sizeToFit()
and layoutIfNeeded()
on the subviews of the stackView. Constrain the UIStackView as you normally would, and constrain the subviews as you normally would.
Also, you need to have it set to Fill Proportionally and it will resize to fit the new content.
And Make sure you don't have a bottom constraint of a height constraint on the stackview. You should only need left, right and top constraint.
How to add fixed size UIView to UIStackView?
The main purpose of a UIStackView
is to arrange its subviews.
So, you are constraining your stack view to "fill the screen" and then adding your "blue view" as an arranged subview
... at which point the stack view will "take over" the arrangement of the blue view.
Assuming you are using a stack view because you are planning on adding additional views, you can either allow the subviews to determine the stack view's frame (that is, don't constrain your stack view's width and/or height), or you need to change the stack view's .alignment
and/or .distribution
properties.
Here is a modification of your playground page to put the 100 x 100
blue view centered in the superview:
import UIKit
import PlaygroundSupport
class MyViewController : UIViewController {
override func loadView() {
let view = UIView()
view.backgroundColor = .white
self.view = view
// vertical stack view (full screen)
let stackView = UIStackView()
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.axis = .vertical
view.addSubview(stackView)
// NSLayoutConstraint.activate([
// stackView.topAnchor.constraint(equalTo: view.topAnchor),
// stackView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
// stackView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
// stackView.widthAnchor.constraint(equalTo: view.widthAnchor),
// ])
NSLayoutConstraint.activate([
stackView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
stackView.centerYAnchor.constraint(equalTo: view.centerYAnchor),
])
// view (100x100)
let fixedSizeView = UIView()
fixedSizeView.translatesAutoresizingMaskIntoConstraints = false
fixedSizeView.backgroundColor = .blue
stackView.addArrangedSubview(fixedSizeView)
NSLayoutConstraint.activate([
fixedSizeView.widthAnchor.constraint(equalToConstant: 100),
fixedSizeView.heightAnchor.constraint(equalToConstant: 100),
])
}
}
// Present the view controller in the Live View window
PlaygroundPage.current.liveView = MyViewController()
and, here's a modification where two views - blue and red, each at 100 x 100
- get added to a stack view that is constrained to the top of the superview, with .alignment
set to .center
:
import UIKit
import PlaygroundSupport
class MyViewController : UIViewController {
override func loadView() {
let view = UIView()
view.backgroundColor = .white
self.view = view
// vertical stack view (full screen)
let stackView = UIStackView()
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.axis = .vertical
stackView.alignment = .center
view.addSubview(stackView)
NSLayoutConstraint.activate([
stackView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
stackView.topAnchor.constraint(equalTo: view.topAnchor),
])
// view (100x100)
let fixedSizeBlueView = UIView()
fixedSizeBlueView.translatesAutoresizingMaskIntoConstraints = false
fixedSizeBlueView.backgroundColor = .blue
stackView.addArrangedSubview(fixedSizeBlueView)
NSLayoutConstraint.activate([
fixedSizeBlueView.widthAnchor.constraint(equalToConstant: 100),
fixedSizeBlueView.heightAnchor.constraint(equalToConstant: 100),
])
// view (100x100)
let fixedSizeRedView = UIView()
fixedSizeRedView.translatesAutoresizingMaskIntoConstraints = false
fixedSizeRedView.backgroundColor = .red
stackView.addArrangedSubview(fixedSizeRedView)
NSLayoutConstraint.activate([
fixedSizeRedView.widthAnchor.constraint(equalToConstant: 100),
fixedSizeRedView.heightAnchor.constraint(equalToConstant: 100),
])
}
}
// Present the view controller in the Live View window
PlaygroundPage.current.liveView = MyViewController()
UIStackView: consistent height based on all subviews even if they are missing
I don't think you want to try to use a stack view for this.
Instead, create a custom UIView
, with three labels:
- Top label
- Top constrained to the view Top
- Bottom label
- Top constrained to the bottom of the Top label (with desired space)
- Bottom constrained to the view Bottom
- Center label
- constrained centerY to the view
Then:
- If both labels have text
- show them and hide the center label
- If only one label has text
- give them both " " as text
- hide them both
- show the center label
- set the center label's text and font to the respective top or bottom label
Here's an example:
Custom View
class WalterView: UIView {
let labelA = UILabel()
let labelB = UILabel()
let labelC = UILabel()
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() -> Void {
for (v, c) in zip([labelA, labelB, labelC], [UIColor.cyan, UIColor.green, UIColor.yellow]) {
addSubview(v)
v.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
v.leadingAnchor.constraint(equalTo: leadingAnchor),
v.trailingAnchor.constraint(equalTo: trailingAnchor),
])
v.backgroundColor = c
v.textAlignment = .center
}
NSLayoutConstraint.activate([
labelA.topAnchor.constraint(equalTo: topAnchor),
labelB.topAnchor.constraint(equalTo: labelA.bottomAnchor, constant: 8.0),
labelB.bottomAnchor.constraint(equalTo: bottomAnchor),
labelC.centerYAnchor.constraint(equalTo: centerYAnchor),
])
labelA.text = " "
labelB.text = " "
labelC.isHidden = true
// set fonts as desired
labelA.font = .preferredFont(forTextStyle: .headline)
labelB.font = .preferredFont(forTextStyle: .subheadline)
labelA.adjustsFontForContentSizeCategory = true
labelB.adjustsFontForContentSizeCategory = true
}
func setLabels(topLabel strA: String, botLabel strB: String) -> Void {
if !strA.isEmpty && !strB.isEmpty {
// if neither string is empty
// show A & B
// set text for A & B
// hide C
labelA.text = strA
labelB.text = strB
labelC.text = ""
labelA.isHidden = false
labelB.isHidden = false
labelC.isHidden = true
} else {
// if either top or bottom string is empty
// hide A & B
// show C
// set A & B text to " " (so they're not empty)
// set text for C to the non-empty string
// set C's font & background color to respective top or bottom label
labelA.isHidden = true
labelB.isHidden = true
labelC.isHidden = false
labelA.text = " "
labelB.text = " "
if strA.isEmpty {
labelC.text = strB
labelC.backgroundColor = labelB.backgroundColor
guard let f = labelB.font else {
return
}
labelC.font = f
}
if strB.isEmpty {
labelC.text = strA
labelC.backgroundColor = labelA.backgroundColor
guard let f = labelA.font else {
return
}
labelC.font = f
}
}
}
}
Example Controller - each tap will cycle through "both", "Top Only" and "Bottom Only":
class WalterViewController: UIViewController {
var wView = WalterView()
var idx: Int = 0
var testStrings: [[String]] = [
["Top Label", "Bottom Label"],
["Top Only", ""],
["", "Bottom Only"],
]
override func viewDidLoad() {
super.viewDidLoad()
wView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(wView)
// respect safe area
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
wView.topAnchor.constraint(equalTo: g.topAnchor, constant: 40.0),
wView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 40.0),
wView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -40.0),
// NO Height constraint...
// height will be determined by wView's labels
])
// so we can see the frame of wView
wView.layer.borderWidth = 1
wView.layer.borderColor = UIColor.red.cgColor
updateLabels()
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
updateLabels()
}
func updateLabels() -> Void {
let strs = testStrings[idx % testStrings.count]
wView.setLabels(topLabel: strs[0], botLabel: strs[1])
idx += 1
}
}
Results:
Related Topics
Stroke Width with a Scenekit Line Primitive Type
How to Change a Swiftui Color to Uicolor
Finding Distance Between Cllocationcoordinate2D Points
How to Add Custom Text in Nsdateformatter's Format String
Com.Facebook.Sdk Error 2 on iOS
Xcode 5/iOS 7 - Localization Not Working in Simulator
Using Sysctlbyname() from Swift
How to Control Avassetwriter to Write at the Correct Fps
How to Read Incoming Sms by Using Application in iOS
How to Determine If a User Has an iOS App Installed
Optional Binding Succeeds If It Shouldn'T
How to Ensure to Run Some Code on Same Background Thread
Alert When New Version of iOS App Is Available
Fonts Not Displaying in Interface Builder