Make SwiftUI Rectangle same height or width as another Rectangle
Here is a working approach, based on view preferences. Tested with Xcode 11.4 / macOS 10.15.6
struct ViewWidthKey: PreferenceKey {
typealias Value = CGFloat
static var defaultValue: CGFloat { 0 }
static func reduce(value: inout Value, nextValue: () -> Value) {
value = value + nextValue()
}
}
struct ContentView: View {
@State private var boxWidth = CGFloat.zero
var body: some View {
VStack {
HStack {
ZStack {
Rectangle()
.fill(Color.purple)
.frame(width: 20)
Text("1")
.font(.subheadline)
.foregroundColor(.white)
}
ZStack {
Rectangle()
.fill(Color.orange)
Text("2")
.font(.subheadline)
.foregroundColor(.white)
}
.background(GeometryReader {
Color.clear.preference(key: ViewWidthKey.self,
value: $0.frame(in: .local).size.width) })
}
HStack {
ZStack {
Rectangle()
.fill(Color.red)
.frame(height: 20)
Text("3")
.font(.subheadline)
.foregroundColor(.white)
}.frame(width: boxWidth)
}.frame(maxWidth: .infinity, alignment: .bottomTrailing)
}
.onPreferenceChange(ViewWidthKey.self) { self.boxWidth = $0 }
.frame(minWidth: 400, minHeight: 250)
}
}
How to make a rectangle's height the same height of a VStack
You need to know the GeometryReader and PreferenceKey to make this possible.
struct SiblingHeightKey: PreferenceKey {
static var defaultValue: CGSize? {
nil
}
static func reduce(value: inout CGSize?, nextValue: () -> CGSize?) {
value = value ?? nextValue()
}
}
struct TodoView: View {
@State var vStackSize: CGSize? = nil
@State var todos = ["feed the dog", "take the dog out for a walk", "make coffee"]
@State var height: CGFloat = 45
var body: some View {
HStack{
RoundedRectangle(cornerRadius: 2)
.foregroundColor(.gray)
.frame(width: self.vStackSize?.width, height: self.vStackSize?.height)
VStack{
Text("Todo")
.font(.title)
ForEach(todos, id: \.self){ todo in
Text(todo)
}
}.background(
GeometryReader { proxy in
Color.clear.preference(key: SiblingHeightKey.self, value: proxy.size)
}
)
Spacer()
}.onPreferenceChange(SiblingHeightKey.self) {
self.vStackSize = $0
}
}
}
SwiftUI resize a rectangle to fit the content
Here is a demo of possible approach (simplified your code w/o dependent model). Prepared & tested with Xcode 11.4 / iOS 13.4
struct DemoView: View {
var dateFormatter: DateFormatter {
let formatter = DateFormatter()
formatter.dateStyle = .medium
return formatter
}
let dateMatch = Date()
var timeFormatter: DateFormatter {
let formatter = DateFormatter()
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.dateFormat = "HH:mm a"
formatter.amSymbol = "AM"
formatter.pmSymbol = "PM"
return formatter
}
var body: some View {
HStack {
VStack(alignment:.leading){
HStack{
Text("\(self.dateMatch, formatter: self.dateFormatter)")
Spacer()
Text("Time:\(self.dateMatch, formatter: self.timeFormatter)")
}.font(.headline).foregroundColor(.blue)
Divider().padding(.horizontal)
HStack{
VStack(alignment:.leading){
Text("Match Name:").bold()
Text("Demo1").font(.body)
}
Spacer()
VStack(alignment:.leading){
Text("Pitch Name").bold()
Text("Demo2").font(.body)
}
Spacer()
VStack(alignment:.trailing){
Text("Player").bold()
Text("10").font(.body)
}
}.font(.headline)
}.padding(.leading, 38)
}.overlay(
Rectangle().frame(width: 30)
, alignment: .leading)
}
}
Dynamic Height of Rectangle as from text content in SwiftUI
From what I understood of your question is that:
- You have an
HStack
in which the leftmost view is aRectangle
and the rightmost view is aText
. - You want the
Rectangle
to be the same height as theText
.
The problem is that the height of the HStack
is based on the tallest child view which happens to be the Rectangle
but a Rectangle
view does not have any intrinsic size like Text
and will occupy all the space the parent provides, or if you manually apply a frame.
You set a width of 20 but leave height and so it takes the entire height it can get.
This indicates that we need to set the height of the Rectangle
to be same as the dynamic Text
but the problem is that we don't know the height upfront.
To solve this:
- First we need to know the height of the dynamic
Text
.- For this we will use
GeometryReader
and access the height value.
- For this we will use
- The height is in a child view so we need it to notify the parent it's height value.
- For this we will use
PreferenceKey
- For this we will use
- The parent view should update the
Rectangle
when it gets to know theText
height- A simple
@State
variable will suffice now
- A simple
Solution:
struct ContentLengthPreference: PreferenceKey {
static var defaultValue: CGFloat { 0 }
static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
value = nextValue()
}
}
struct ContentView: View {
@State var textHeight: CGFloat = 0 // <-- this
var body: some View {
HStack {
Rectangle()
.frame(width: 20, height: textHeight) // <-- this
Text(String(repeating: "lorem ipsum ", count: 25))
.overlay(
GeometryReader { proxy in
Color
.clear
.preference(key: ContentLengthPreference.self,
value: proxy.size.height) // <-- this
}
)
}
.onPreferenceChange(ContentLengthPreference.self) { value in // <-- this
DispatchQueue.main.async {
self.textHeight = value
}
}
}
}
- Create
ContentLengthPreference
as ourPreferenceKey
implementation - on
Text
; Applyoverlay
containingGeometryReader
overlay
will have same height asText
- in
GeometryReader
,Color.clear
is just a filler invisible view anchorPreference
modifier allows us to access and store heightonPreferenceChange
modifier on parentHStack
can catch the value passed by child view- parent saves the height to a state property
textHeight
textHeight
can be applied onRectangle
and will update the view when this value updates
Credits: https://www.wooji-juice.com/blog/stupid-swiftui-tricks-equal-sizes.html
Output (including your header + footer views):
EDIT:
If you have multiple of these in a List
then you don't need to do anything. Each row will size automatically upto the Text
height.
It's free!!!
struct ContentView: View {
var body: some View {
List(0..<20) { _ in
ArticleView()
}
}
}
struct ArticleView: View {
var body: some View {
VStack(alignment: .leading) {
HStack {
Circle()
.fill(Color.blue)
.frame(width: 15, height: 15)
.overlay(Circle().inset(by: 2).fill(Color.white))
Text("Headline").font(.headline)
}
HStack {
Rectangle().frame(width: 20)
Text(String(repeating: "lorem ipsum ", count: (5...50).randomElement()!))
}
HStack {
Circle()
.fill(Color.orange)
.frame(width: 15, height: 15)
.overlay(Circle().inset(by: 2).fill(Color.white))
Text("Footer").font(.subheadline)
}
}
}
}
SwiftUI rectangle taking up the wrong amount of space
You can achieve this by simply using an overlay.
Checkout the following example:
HStack {
Text("Lorem ipsum")
.font(.body)
.padding(.leading, 20)
.foregroundColor(Color.black)
Spacer()
}.overlay(RoundedRectangle(cornerRadius: 2, style: .continuous)
.frame(width: 3)
.foregroundColor(Color.gray)
.padding(.leading, 10),
alignment: .leading)
Why this swiftUI rectangle is shift to the right?
Set horizontal padding before frame.
RoundedRectangle(cornerRadius: 20)
.stroke(Color.primary, lineWidth: 2)
.padding(.horizontal)// << Here
.frame(width: geo.size.width, height: 200)
or Set size to VStack
.
VStack(alignment: .trailing, spacing: 10) {
RoundedRectangle(cornerRadius: 20)
.stroke(Color.primary, lineWidth: 2)
.frame(width: geo.size.width, height: 200)
.padding(.horizontal)
}.frame(width: geo.size.width, height: geo.size.height) // <<=== Here
SwiftUI - Animate height of rectangle from 0 to height
You could try this. But as the information you provided do not include how you want to use this, this might be incomplete.
struct View1: View {
@State private var height: CGFloat = 0 // add this to modify
// the height of the Rounded Rectangle
let targetHeight: CGFloat = 100 // targetHeight var to syncronise with the VStack
var body: some View {
ZStack {
Color.white
.edgesIgnoringSafeArea(.all)
Text("Hola")
.foregroundColor(.black)
.font(.headline)
.background{
VStack{
Spacer(minLength: 0)
RoundedRectangle(cornerRadius: 5)
.frame(width: 100, height: height)
.foregroundColor(.red)
.onShake {
withAnimation(.linear(duration: 5)) {
height = targetHeight
}
}
}.frame(height: targetHeight) // VStack with fixed height
//to animate from bottom up
}
}
}
}
How to set UIImage's height to always be the same as the width?
If your image is already of a square aspect ratio you can just make it resizable, and select aspectRatio to fit.
Image(systemName: "plus")
.resizable()
.aspectRatio(contentMode: .fit)
You can delete .frame(maxWidth: .infinity) because it is intrinsic when you use resizable.
This is the effect obtained :
Obviously if your source Images are not squared, you have to specify the aspect ratio that you want.
UPDATE :
If you want a square colored background with a smaller image in the middle you can't do it directly from an Image object like you did in the Example, but you need a more complex object like this :
struct WidgetView : View
{
var backgroundColor : Color
var imageName : String
var body : some View
{
ZStack {
Rectangle()
.fill(backgroundColor)
.cornerRadius(10)
.aspectRatio(contentMode: .fit)
Image(systemName: imageName)
.resizable()
.aspectRatio(contentMode: .fit)
.frame(maxWidth: 50)
.font(.system(size: 30))
}
.padding(5)
}
}
The usage is very simple :
ForEach(0 ... 6, id: \.self) {
WidgetView(backgroundColor: colors[$0 % colors.count], imageName: "plus")
}
And the obtained effect is the following :
Related Topics
Passing Unknown Number of Arguments in a Function
Downloading Firebase Storage Files Device Issue
Swift Format Text Field When User Is Typing
How to Increment Exponentially in Swift
Elegant 'Bounded' Methodology in Swift
App Window on Top of All Windows Including Others App Windows
Replace Multiple Words from a String Based on the Values in an Array
iOS 13 Swiftui: App Crashes Upon Launch on Real Device
Generic Class with Class-Bound Constraint Cannot Be Parametrized by Class-Bound Protocol
Collection View Layout Bug When Selectitem (Swift 5)
Swift: Filter a Dictionary with Array as Value
Undefined Symbol Objc_Class_$ When Creating iOS Framework
Filtering Dictionary in Swift 4 Fails in Xcode, But Succeeds in Playground
How to Read Ansi Escape Code Response Value in Swift
Exc Bad Access After Coding Signing
How to Give Dynamic Height to Uitableview
Linking Error When Building Parse in Xcode 7
What the Difference of Keys and Values in Dictionary of Swift