SWIFTUI: .overlay breaks alignment - swift

I have this bit of code below where I'm trying to have a rectangle centered in the screen, where I overlay a text outupt from some function and a share button. The concept works, except that when I add the .overlay, the "text box" (the rectangle with the text and share button overlayed to it) lose the centered alignment and they move to the left.If I remove the .overlay, the rectangle is centered but the content remains behind it. What am I doing wrong? Thanks.
GeometryReader { geometry in // Debug alignment
VStack(alignment: .center) { //TextBox
Rectangle()
.fill(Color.cyan)
.cornerRadius(20)
.frame(width: geometry.size.width * 0.75, height: 200, alignment: .center)
.overlay(
ScrollView {
HStack(alignment: .top){
VStack(alignment: .center) {
Text(responseText)
.foregroundColor(.black)
.frame(minWidth: 0, maxWidth: .infinity, alignment: .center)
.layoutPriority(2)
}
ShareLink(item: responseText){
Image(systemName: "square.and.arrow.up")
.resizable()
.scaledToFit()
.frame(width: 25, height: 25)
}
}
}
)
} //Vstack Top
} //Geo Reader
I tried to center the recantgle while using the .overlay to put the conetent on top of it but the centered aligment gets lost. I also tried to use a ZStack instead of a .overlay but the content remains hidden behind.

GeometryReader positions its children at its top left. The VStack child of GeometryReader hugs its children—in this case, its single child, which is a Rectangle with a fixed frame.
Put the Rectangle in a container that expands to the size of the enclosing GeometryReader and centers the Rectangle within the expanded size.
For example, add a frame(maxWidth: .infinity, maxHeight: .infinity) modifier on the VStack:
GeometryReader { geometry in // Debug alignment
VStack {
Rectangle()
.fill(Color.cyan)
etc. etc.
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
}
Result:
But if you don't have anything else to put in the VStack, you can remove it and put the frame modifier directly on the Rectangle, after the overlay:
GeometryReader { geometry in // Debug alignment
Rectangle()
.fill(Color.cyan)
.cornerRadius(20)
.frame(width: geometry.size.width * 0.75, height: 200)
.overlay(
ScrollView {
HStack(alignment: .top){
Text(responseText)
.foregroundColor(.black)
ShareLink(item: responseText) {
Image(systemName: "square.and.arrow.up")
.resizable()
.scaledToFit()
.frame(width: 25, height: 25)
}
}
}
)
.frame(maxWidth: .infinity, maxHeight: .infinity)
}

Related

Override HStack alignment for one child

Is there a way to dynamically override the alignment property of an HStack in an individual element?
Consider this scenario
There is a parent HStack with alignment = bottom
There are 3 elements inside the HStack of different sizes
I want the 3rd element to align to the top of the HStack. This alignment is different from the Hstack's bottom alignment
var body: some View {
HStack(alignment: .bottom) {
Rectangle()
.fill(.yellow)
.frame(height: 100)
Rectangle()
.fill(.blue)
.frame(height: 20)
// I want this to go to the top of the HStack
Rectangle()
.fill(.green)
.frame(height: 50)
}
.background {
Color.red
}
}
I'm trying to get the HStack to respect the highest height of 100 and just alter the last element's alignment.
I've tried wrapping the 3rd element in another stack but that only works if I specify a maxHeight equal to the tallest height among the parent's children, 100.
This means these rectangles have to know about their sibling elements.
HStack {
Rectangle()
.fill(.green)
.frame(height: 50)
}
.frame(maxHeight: 100, alignment: .top)
You could try this:
var body: some View {
HStack(alignment: .bottom) {
Rectangle()
.fill(.yellow)
.frame(height: 100)
Rectangle()
.fill(.blue)
.frame(height: 20)
VStack {
Rectangle()
.fill(.green)
.frame(height: 50)
Spacer()
}
}
.background {
Color.red
}
}
If, for some reason, you want to limit the range of how much space can the Spacer take up, you can add a modifier as per following example:
VStack {
Rectangle()
.fill(.green)
.frame(height: 50)
Spacer()
.frame(minHeight: 10, maxHeight: 50)
}

How to put LazyVGrid next to Image swiftUI on MacOS (11.4)

(MacOS app)
I am trying to place a lazyvgrid next to an image. to have the image in a specific size and let the LazyVGrid take the rest of the width. I set the frame of the image but than the grid does not take the extra space and there are extra empty space to the left and right of the image and the grid.
import SwiftUI
struct ContentView: View {
var body: some View {
HStack{
LazyVGrid(columns: [GridItem(
.adaptive(minimum: 40)
)]){
Text("Placeholder")
Text("Placeholder")
}
.padding()
.background(Color.blue)
Image("bla")
.resizable()
//.scaledToFit()
.frame(width: 100, height: 100)
.background(Color.pink)
}
}
}
try something like this:
struct ContentView: View {
var body: some View {
HStack {
LazyVGrid(columns: [GridItem(.adaptive(minimum: 40))]){
Text("Placeholder")
Text("Placeholder")
}
.padding()
// put this here if you want the width and the height to expand
// .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .leading)
.background(Color.blue)
// put this here if you want just the width to expand
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .leading)
Image(systemName: "globe")
.resizable()
//.scaledToFit()
.frame(width: 100, height: 100)
.background(Color.pink)
}.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .leading)
}
}

Adding 2 backgrounds within a Z and HStack?

The color.red is my widgets main background but I would like to add a black background to the top behind the text "Line1" and image "2". So basically a black bar that stretches across the top under the text and image.
var body: some View {
ZStack {
Color.red
VStack(alignment: .leading, spacing: 0) {
HStack {
Text("Line1")
.foregroundColor(.orange)
.bold()
.font(.system(size: 17.5))
.padding(.top)
Spacer()
Image("2")
.resizable()
.frame(width: 25, height: 25)
.padding(.top)
}
.padding(.horizontal)
.border(Color.green)
VStack(alignment: .leading) {
Image("2")
.resizable()
.frame(width: 67, height: 30)
Text("State")
.foregroundColor(Color.green)
.font(.system(size: 15))
Text("1")
.foregroundColor(Color.blue)
.font(.system(size: 10))
.opacity(0.5)
Spacer()
Spacer()
}
.padding(.horizontal)
.border(Color.blue)
}
}
}
You can add a .background modifier to your first HStack:
HStack {
//content here
}
.padding(.horizontal)
.border(Color.green)
.background(Color.black)
Keep in mind that order of modifiers is important, so if you put the background before the padding, the padding will not have that background color -- that's why I used it last.

Aligning objects in ZStack

The white bar is supposed to be aligned at the left of the darker bar. I've tried using spacers, or changing the alignment of the individual objects but nothing works. This is my code:
VStack {
HStack {
Text("Calories eaten today:")
.foregroundColor(.white)
.fontWeight(.semibold)
Spacer(minLength: 100)
HStack {
ZStack(alignment: .leading) {
Capsule()
.rotation(.degrees(90))
.frame(width: 20, height: CGFloat((calorieGoal/proportion)))
.foregroundColor(.black)
.opacity(0.2)
Capsule()
.rotation(.degrees(90))
.frame(width: 20, height: CGFloat((Swift.min((eatendatabase.dayNutrients[0]/proportion), 180))), alignment: .leading)
}
Spacer(minLength: 20)
Text("\(String(format: "%.0f", eatendatabase.dayNutrients[0]))")
.font(.caption)
.foregroundColor(.white)
.frame(alignment: .trailing)
}
Spacer()
}
}
.padding(.horizontal, 30)
As calorieGoal, proportional, and eatendatabase.dayNutrients were not provided as values, I have used the following constant and variable to keep track of the capsule's widths, which I will denote in the code as the "progress bar":
let MAX_WIDTH: CGFloat = 100.0
let currentProgress: CGFloat = 40.0
With that in mind, first you should consider moving the calorie count label ("832") outside of the HStack containing the progress bar. Second, you nested both the background and foreground capsules forming the progress bars in a ZStack. We can take the foreground capsule and align it to the left by nesting it in an HStack with a Spacer:
HStack {
Capsule()
.rotation(.degrees(90))
.frame(width: 20.0, height: currentProgress /*This is the progress width of the bar, out of 100*/)
Spacer()
}
Of course, we need to set the width of the HStack to the width of the background capsule, so that the foreground capsule aligns properly with the leading edge of the background capsule:
HStack {
...
}.frame(width: MAX_WIDTH)
Overall, here is a possible solution to your issue:
VStack {
HStack {
Text("Calories eaten today:")
.foregroundColor(.white)
.fontWeight(.semibold)
// Made this font smaller for the preview
.font(.footnote)
// Add auto space between the label and the progress bar
Spacer()
// Separate the capsule progress bar from the calorie count
ZStack {
Capsule()
.rotation(.degrees(90))
.frame(width: 20.0, height: MAX_WIDTH /*This is the max width of the bar*/)
.foregroundColor(.black)
.opacity(0.2)
// Nesting the progress Capsule in an HStack so we can align it to the left
HStack {
Capsule()
.rotation(.degrees(90))
.frame(width: 20.0, height: currentProgress /*This is the progress width of the bar, out of 100*/)
// Adding a Spacer will force the Capsule to the left when it is in an HStack
Spacer()
}
// Set the frame width of the HStack to the same width as the background capsule
.frame(width: MAX_WIDTH)
}
// Add auto space between the progress bar and the calorie count
Spacer()
// The calorie count text, nested in an HStack with the label and the progress bar
Text("832")
.font(.caption)
.foregroundColor(.white)
.frame(alignment: .trailing)
}
}
.padding(.horizontal, 30)
// Added a gray background for the preview
.background(Color.gray)
And it would look like this:

SwiftUI layers in HStack

When I drag the rectangle to the left, the symbol is under the rectangle but on the right the symbol is over the rectangle, when I drag it to the right. I know, that the hierarchy of the HStack decides the layer but if I would do the right symbol over the rectangle, then both of the symbols are on the right side. I'm new at SwiftUI
HStack(spacing: 0.0){
Image(systemName: "lessthan.square")
.resizable()
.frame(width: 20.0, height: 20.0)
.padding(.leading, 30.0)
ZStack(alignment: .center) {
Group {
Rectangle()
.cornerRadius(20.0)
.frame(width: 250, height: 150)
.foregroundColor(Color(.white))
.position(rectPosition)
.shadow(radius: 5)
VStack{
Text("i")
.font(.system(size: 30))
Text("l")
.font(.system(size: 25))
}.position(rectPosition)
}.gesture(DragGesture().onChanged({ value in
self.rectPosition = CGPoint(x: value.location.x, y: 130)
}))
}
Image(systemName: "greaterthan.square")
.resizable()
.frame(width: 20.0, height: 20.0)
.padding(.trailing, 30.0)```
zIndex should help you... add it to ZStack
HStack {
Image(...)
ZStack {
...
}.zIndex(1) // << here !!
Image(...)
}