SwiftUI HStack with equal Height
Here is possible approach using .alignmentGuide
struct Test7: View {
@State
private var height: CGFloat = .zero // < calculable height
var body: some View {
HStack (alignment: .top) {
Text( "111")
.frame(minHeight: height) // in Preview default is visible
.background(Color.red)
VStack(alignment: .leading) {
Text("2222222")
.background(Color.gray)
Text("333333333")
.background(Color.blue)
}
.alignmentGuide(.top, computeValue: { d in
DispatchQueue.main.async { // << dynamically detected - needs to be async !!
self.height = max(d.height, self.height)
}
return d[.top]
})
}
.background(Color.yellow)
}
}
Note: real result is visible only in LivePreview, because height is calculated dynamically and assign in next rendering cycle to avoid conflicts on @State.
SwiftUI HStack elements with equal height
You can set the max value in the ViewHeightKey
preference key:
struct ViewHeightKey: PreferenceKey {
static var defaultValue: CGFloat { 0 }
static func reduce(value: inout Value, nextValue: () -> Value) {
value = max(value, nextValue()) // set the `max` value (from both buttons)
}
}
and then read view height from both buttons and force vertical fixedSize
:
struct DynamicallyScalingView: View {
@State private var labelHeight = CGFloat.zero
var body: some View {
HStack {
Button(action: {}, label: {
Text("SOME TEXT SOME TEXT SOME TEXT SOME TEXT SOME TEXT SOME TEXT SOME TEXT SOME TEXT SOME TEXT SOME TEXT SOME TEXT SOME TEXT SOME")
})
.foregroundColor(Color.white)
.padding(.vertical)
.frame(minWidth: 0, maxWidth: .infinity)
.frame(minHeight: labelHeight) // min height for both buttons
.background(Color.blue)
.cornerRadius(8)
.fixedSize(horizontal: false, vertical: true) // expand vertically
.background(GeometryReader { // apply to both buttons
Color.clear
.preference(
key: ViewHeightKey.self,
value: $0.frame(in: .local).size.height
)
})
Button(action: {}, label: {
Text("jahlsd")
})
.foregroundColor(Color.white)
.padding(.vertical)
.frame(minWidth: 0, maxWidth: .infinity)
.frame(minHeight: labelHeight)
.background(Color.blue)
.cornerRadius(8)
.fixedSize(horizontal: false, vertical: true)
.background(GeometryReader {
Color.clear
.preference(
key: ViewHeightKey.self,
value: $0.frame(in: .local).size.height
)
})
}
.onPreferenceChange(ViewHeightKey.self) {
self.labelHeight = $0
}
.padding(.horizontal)
}
}
Note: as the buttons are similar now, the next step would be to extract them as another component to avoid duplication.
How do I make all views the same height in a SwiftUI View with an HStack?
Here is possible (seems simplest) approach. Tested with Xcode 12 / iOS 14
struct DemoView: View {
var dataSource = [1, 0, 34, 12, 59, 44]
var body: some View {
HStack(alignment: .top) {
Spacer()
/// i want these to all be the same height
ForEach(0 ..< 6) { index in
VStack {
Text("\(index)")
Color.clear
.frame(width: 20, height: CGFloat(dataSource.max() ?? 0))
.overlay(
Color.orange
.frame(height: CGFloat(self.dataSource[index]))
, alignment: .top)
Text("\(dataSource[index])")
}
}
Spacer()
}
}
}
Align heights of HStack members
Wrap your Image
in a Text
. Since your image is from SF Symbols, SwiftUI will scale it to match the dynamic type size. (I'm not sure how it will scale other images.)
VStack {
let background = RoundedRectangle(cornerRadius: 10)
.foregroundColor(.red)
ForEach(Font.TextStyle.allCases, id: \.self) { style in
HStack {
Text("\(style)" as String)
.padding()
.background(background)
Spacer()
Text(Image(systemName: "checkmark.seal.fill"))
.padding()
.background(background)
}
.font(.system(style))
}
}
SwiftUI HStack fill whole width with equal spacing
The frame
layout modifier, with .infinity
for the maxWidth
parameter can be used to achieve this, without the need for an additional Shape
View.
struct ContentView: View {
var data = ["View", "V", "View Long"]
var body: some View {
VStack {
// This will be as small as possible to fit the data
HStack {
ForEach(data, id: \.self) { item in
Text(item)
.border(Color.red)
}
}
// The frame modifier allows the view to expand horizontally
HStack {
ForEach(data, id: \.self) { item in
Text(item)
.frame(maxWidth: .infinity)
.border(Color.red)
}
}
}
}
}
Make a grid of buttons of same width and height in SwiftUI
How about using a GeometryReader
(docs here)? They give the dimensions of their parent view to the children. Here's a partial implementation based on what you already have:
import SwiftUI
struct ContentView : View {
var body: some View {
VStack {
Text("0")
NumpadView()
}
}
}
struct NumpadView : View {
let rows: Length = 5
let columns: Length = 4
let spacing: Length = 10
var horizontalEdges: Length {
return columns - 1
}
var verticalEdges: Length {
return rows - 1
}
func getItemWidth(containerWidth: Length) -> Length {
return (containerWidth - spacing * horizontalEdges) / columns
}
func getItemHeight(containerHeight: Length) -> Length {
return (containerHeight - spacing * verticalEdges) / rows
}
var body: some View {
GeometryReader { geometry in
VStack(alignment: .center, spacing: self.spacing) {
HStack(alignment: .center, spacing: self.spacing) {
Button(action: {}) {
Text("7")
.frame(width: self.getItemWidth(containerWidth: geometry.size.width), height: self.getItemHeight(containerHeight: geometry.size.height))
.background(Color.green)
}
Button(action: {}) {
Text("8")
.frame(width: self.getItemWidth(containerWidth: geometry.size.width), height: self.getItemHeight(containerHeight: geometry.size.height))
.background(Color.yellow)
}
Button(action: {}) {
Text("9")
.frame(width: self.getItemWidth(containerWidth: geometry.size.width), height: self.getItemHeight(containerHeight: geometry.size.height))
.background(Color.green)
}
}
HStack(alignment: .center, spacing: self.spacing) {
Button(action: {}) {
Text("4")
.frame(width: self.getItemWidth(containerWidth: geometry.size.width), height: self.getItemHeight(containerHeight: geometry.size.height))
.background(Color.yellow)
}
Button(action: {}) {
Text("5")
.frame(width: self.getItemWidth(containerWidth: geometry.size.width), height: self.getItemHeight(containerHeight: geometry.size.height))
.background(Color.green)
}
Button(action: {}) {
Text("6")
.frame(width: self.getItemWidth(containerWidth: geometry.size.width), height: self.getItemHeight(containerHeight: geometry.size.height))
.background(Color.yellow)
}
}
HStack(alignment: .center, spacing: self.spacing) {
Button(action: {}) {
Text("1")
.frame(width: self.getItemWidth(containerWidth: geometry.size.width), height: self.getItemHeight(containerHeight: geometry.size.height))
.background(Color.green)
}
Button(action: {}) {
Text("2")
.frame(width: self.getItemWidth(containerWidth: geometry.size.width), height: self.getItemHeight(containerHeight: geometry.size.height))
.background(Color.yellow)
}
Button(action: {}) {
Text("3")
.frame(width: self.getItemWidth(containerWidth: geometry.size.width), height: self.getItemHeight(containerHeight: geometry.size.height))
.background(Color.green)
}
}
HStack(alignment: .center, spacing: self.spacing) {
Button(action: {}) {
Text("0")
.frame(width: self.getItemWidth(containerWidth: geometry.size.width) * 2 + self.spacing, height: self.getItemHeight(containerHeight: geometry.size.height))
.background(Color.yellow)
}
Button(action: {}) {
Text(".")
.frame(width: self.getItemWidth(containerWidth: geometry.size.width), height: self.getItemHeight(containerHeight: geometry.size.height))
.background(Color.yellow)
}
}
}.frame(maxWidth: .infinity, maxHeight: .infinity)
}
}
}
#if DEBUG
struct ContentView_Previews : PreviewProvider {
static var previews: some View {
ContentView()
}
}
#endif
Looks like this:
SwiftUI: Two buttons with the same width/height
Here is run-time based approach without hard-coding. The idea is to detect max width of available buttons during drawing and apply it to other buttons on next update cycle (anyway it appears fluently and invisible for user).
Tested with Xcode 11.4 / tvOS 13.4
Required: Simulator or Device for testing, due to used run-time dispatched update
struct ButtonsView: View {
@State private var maxWidth: CGFloat = .zero
var body: some View {
VStack {
Button(action: { print("PLAY tapped") }){
Text("Play")
.background(rectReader($maxWidth))
.frame(minWidth: maxWidth)
}.id(maxWidth) // !! to rebuild button (tvOS specific)
Button(action: { print("PAUSE tapped") }) {
Text("Pause Long Demo")
.background(rectReader($maxWidth))
.frame(minWidth: maxWidth)
}.id(maxWidth) // !! to rebuild button (tvOS specific)
}
}
// helper reader of view intrinsic width
private func rectReader(_ binding: Binding<CGFloat>) -> some View {
return GeometryReader { gp -> Color in
DispatchQueue.main.async {
binding.wrappedValue = max(binding.wrappedValue, gp.frame(in: .local).width)
}
return Color.clear
}
}
}
Make a View the same size as another View which has a dynamic size in SwiftUI
As @Asperi pointed out, this solves the problem: stackoverflow.com/a/62451599/12299030
This is how I solved it in this case:
import SwiftUI
struct TestScreen: View {
@State private var imageHeight = CGFloat.zero
var body: some View {
VStack {
HStack {
Image("blue-test-image")
.resizable()
.scaledToFit()
.frame(maxWidth: .infinity)
.background(GeometryReader {
Color.clear.preference(
key: ViewHeightKeyTestScreen.self,
value: $0.frame(in: .local).size.height
)
})
VStack {
Text("Some Text")
.padding()
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(Color.gray)
Text("Other Text")
.padding()
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(Color.gray)
}
.frame(maxWidth: .infinity, minHeight: self.imageHeight, maxHeight: self.imageHeight)
}
.onPreferenceChange(ViewHeightKeyTestScreen.self) {
self.imageHeight = $0
}
Spacer()
}
.padding()
}
}
struct ViewHeightKeyTestScreen: PreferenceKey {
static var defaultValue: CGFloat { 0 }
static func reduce(value: inout Value, nextValue: () -> Value) {
value += nextValue()
}
}
struct TestScreen_Previews: PreviewProvider {
static var previews: some View {
TestScreen()
}
}
Related Topics
Swift: Reusable Uiview in Storyboard and Sizing Constraints
Workarounds for Generic Variable in Swift
How to Replace Limited Number of Occurrences in String
How to Implement Default Associated Values with Swift Enums
How to Initialise a New Nsdocument Instance in Swift
Module Compiled with Swift 4.0 Cannot Be Imported in Swift 3.0.2
Passing Arguments to #Selector Method in Swift
Swiftui on MACos, How to Use Custom Image Symbol on Button
Swift Threading with Dispatchgroup
How to Add an Admob Gadbannerview to Every View
Instagram Explorer Searchbar and Tableview
Correct Way to Call "Realloc" in Swift with a Float Array
Why Do I Get "Static Member '...' Cannot Be Used on Instance of Type '...'" Error
Using @Fetchrequest(Entity: ) for Swiftui MACos App Crashes
Uicollectionview Autosize and Dynamic Number of Rows