VStack inside ForEach loop not respecting alignment parameter in WatchOS - swift

I have a VStack with alignment .leading inside of a ForEach loop inside a ScrollView and the alignment parameter is not being respected. If I have a single VStack outside of the ForEach loop the alignment parameter is being used and the view is displayed correctly.
ScrollView{
// THIS DISPLAYS CORRECTLY
VStack(alignment: .leading){
Text("namename")
.font(.headline)
.lineLimit(1)
Text("namename name")
.font(.subheadline)
.lineLimit(1)
}
.padding()
.frame(minWidth: 0, maxWidth: .infinity)
.background(Color.gray.opacity(0.20))
.cornerRadius(5)
.padding(.bottom)
ForEach(self.list_of_names, id: \.self) { item in
// THIS DOES NOT DISPLAY CORRECTLY
VStack(alignment: .leading){
Text(item)
.font(.headline)
.lineLimit(1)
Text(item + " name")
.font(.subheadline)
.lineLimit(1)
}
.frame(minWidth: 0, maxWidth: .infinity)
.background(Color.gray.opacity(0.20))
.cornerRadius(5)
.padding(.bottom)
}
}
.padding()
The first row has the correct alignment and it's outside the ForEach loop, meanwhile the other rows are inside the loop. The blue lines represent me highlighting each VStack inside the ForEach loop

The VStacks created in the ForEach loop do seem to be conforming to the alignment parameter. Aligning the text to the leading edge only applies to the shorter items in the VStack.
In the top VStack, "namename" at the top is only that far to the left because "namename name" below is that much wider from the center of the ScrollView. So the top VStack is not the proper way to fully achieve the alignment that you want to the ScrollView.
What you're looking for:
Leave the alignment parameter for VStack.
Add the alignment parameter to the frame of the VStack.
.frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)

I'm a bit late, but another solution I usually use is wrapping the values in an HStack and adding a Spacer. Something like this will achieve the desired effect:
var body: some View {
ScrollView {
ForEach(self.list_of_names, id: \.self) { item in
HStack {
VStack(alignment: .leading) {
Text(item)
.font(.headline)
.lineLimit(1)
Text(item + " name")
.font(.subheadline)
.lineLimit(1)
}
Spacer()
}
.padding()
.frame(minWidth: 0, maxWidth: .infinity)
.background(Color.gray.opacity(0.20))
.cornerRadius(5)
.padding(.bottom)
}
}
.padding()
}

Related

How to align text to the left and in the middle of a view

I have two Texts: Text("1") and Text("2") in an HStack. I want Text("1") to be in the leftmost part of ContentView and Text("2") to be in the exact horizontal center of ContentView.
Here's my code:
import SwiftUI
struct ContentView: View {
var body: some View {
HStack {
Text("1")
.multilineTextAlignment(.leading)
.frame(
alignment: .leading
)
Text("2")
.multilineTextAlignment(.center)
.frame(
alignment: .center
)
}
.frame(width: .infinity)
}
}
When I run this though, both Text("1") and Text("2") remain in the general horizontal center of ContentView. Not sure what I'm doing wrong.
Here's another way you can do this by using ZStack. Second text will always be in exact centre.
ZStack {
HStack {
Text("1")
Spacer()
}
HStack {
Spacer()
Text("2")
Spacer()
}
}
.padding()
Try the below code:
HStack {
Text("1")
.multilineTextAlignment(.leading)
Text("2")
.multilineTextAlignment(.center)
.frame(minWidth: 0, maxWidth: .infinity)
}
Add a Spacer to your HStack after each Text. This will give you the first Text at the left and the second starting in the middle
HStack {
Text("1")
.multilineTextAlignment(.leading)
.frame(alignment: .leading)
.padding(.leading)
Spacer()
Text("2")
.multilineTextAlignment(.center)
.frame(alignment: .center)
.padding(.trailing)
Spacer()
}

SwiftUI: Navigation link alignment with the newer navigation stack

I am using the new NavigationStack and I have a VStack with the code below:
VStack(alignment: .leading) {
NavigationLink(post.phrase, value: post)
.lineLimit(2)
.font(.system(size: 20))
.multilineTextAlignment(.leading)
.foregroundColor(.black)
Text(post.Translation.chinese)
.lineLimit(1)
}
The NavigationLink is always centrally aligned but the Text is ok which is left aligned. Is there a way to force the 2-line NavigationLink to align to the left?
The code below will correctly align the navigation link:
VStack(alignment: .leading) {
NavigationLink(value: post) {
Text(post.phrase)
.lineLimit(2)
.font(.system(size: 20))
.multilineTextAlignment(.leading)
.foregroundColor(.black)
}
Text(post.Translation.chinese)
.lineLimit(1)
}

Align elements both leading and trailing in same container, without stretching it, in SwiftUI

I'm trying to build a simple chat with a design like the most usual messengers.
A message consists of a user name (if not your own message), the message itself and a timestamp.
I want the width of a message to be determined by the widest child element (most likely the message, but in rare cases maybe the user name or timestamp).
I also want the user name and message to be aligned leading/left and the timestamp to be aligned trailing/right, just like group chats in WhatsApp, Signal or the likes..
I tried weird amounts of H/V/Z-Stacks, which lead to weird happenings. I also tried both adding Spacers and maxLength = .infinity, which lead to the message stretching to the far right with lots of empty space.
My current code has all elements aligned centered:
And this is what I want:
This code is only for the comment, which itself is embedded into a ScrollView/VStack for each one.
HStack() {
if comment.author?.id == currentUserId {
Spacer()
.frame(width: 50)
Spacer()
}
VStack(spacing: 3) {
if comment.author?.id != currentUserId {
Text("Test User")
.font(.body)
.frame(alignment: .leading)
.border(.red)
}
Text("Test message content")
.frame(alignment: .leading)
.border(.red)
Text(date)
.foregroundColor(.gray)
.frame(alignment: .trailing)
.border(.red)
}
.border(.green)
.padding(10)
.background(Color.gray)
.cornerRadius(10)
if comment.author?.id != currentUserId {
Spacer()
Spacer()
.frame(width: 50)
}
}
Also I thought the double Spacers on the left or right side looked kinda weird. I tried using .frame(minWidth: 50), which ended up way too wide and pushes even a small message like the one on the image into a second line.
I'm happy for any ideas for these problems, but I'm trying to go for a simple solution and no complex calculations with alignmentGuides or GeometryReader or similar.
.frame allows you to specify the alignment.
So you could just make the VStack leading and then use .frame on the last element with alignment: .trailing. Keep in mind that the View will then grow to its available space. If you want to prevent that, just add .fixedSize() (or .fixedSize(horizontal: true, vertical: false)) to the parent view.
GroupBox {
VStack(alignment: .leading) {
Text("Test User")
Text("Test Message Content")
Text("9.5.2022, 19:31")
.font(.caption)
.foregroundColor(.secondary)
.frame(maxWidth: .infinity, alignment: .trailing)
}
.fixedSize()
}
or if you want to keep the VStack on the default alignment
GroupBox {
VStack {
Text("Test User")
.frame(maxWidth: .infinity, alignment: .leading)
Text("Test Message Content")
Text("9.5.2022, 19:31")
.font(.caption)
.foregroundColor(.secondary)
.frame(maxWidth: .infinity, alignment: .trailing)
}
.fixedSize()
}
but it also works using a HStack + Spacer
GroupBox {
VStack(alignment: .leading) {
Text("Test User")
Text("Test Message Content")
HStack {
Spacer()
Text("9.5.2022, 19:31")
.font(.caption)
.foregroundColor(.secondary)
}
}
.fixedSize()
}

How to move this to the top?

I just learned how to implement specific rounded corners, but now it seems nothing will align to the top of the screen, even with spacers. How can I get it to align to the top of the screen?
Additionally, I would like the green to ignore the top safe area, but it wasn't doing that earlier either.
import SwiftUI
struct Dashboard: View {
#State var bottomLeft: CGFloat = 25
var body: some View {
ZStack {
Color("background")
.edgesIgnoringSafeArea(.all)
VStack {
VStack {
Text("Good Morning, Sarah")
.font(Font.system(size: 36))
.foregroundColor(.standaloneLabelColor)
.fontWeight(.semibold)
.frame(maxWidth: .infinity, alignment: .leading)
.padding(.horizontal)
Text("We're glad to see you and hope you're doing well. Let's take on the day.")
.font(Font.system(size: 20))
.foregroundColor(.standaloneLabelColor)
.frame(maxWidth: .infinity, alignment: .leading)
.padding(.horizontal)
.padding(.bottom)
}
.background(Color.appColor)
.cornerRadius(bottomLeft, corners: .bottomLeft)
.padding(.bottom, 10)
}
Spacer()
}
}
}
Thanks in advance.
You can set the alignment on the ZStack to .top. You can then also remove the Spacer.
A Spacer does nothing in a ZStack - since it's on its own layer. But setting the alignment on the ZStack will align all views to the top. If this is not what you want, you can also just put the Spacer in the inner VStack and the contents of that will all be aligned to the top also.
Code:
ZStack(alignment: .top) {
Color("background")
.edgesIgnoringSafeArea(.all)
VStack {
/* ... */
}
}
Also, to ignore the safe area at the top when applying the rounded corners, you should clip the corners and use ignoresSafeArea() within background. This ensures that you are only ignoring the safe area for the background, and not the whole view. Do the following:
.background(
Color.appColor
.cornerRadius(bottomLeft, corners: .bottomLeft)
.ignoresSafeArea()
)
// .background(Color.appColor)
// .cornerRadius(bottomLeft, corners: .bottomLeft)
.padding(.bottom, 10)
I had to remove some bits from your code as they were producing errors on my end. Working with what I had, you needed to add a Spacer() below the appropriate VStack.
Since your content was stored in a VStack embedded in another VStack, the outer VStack was essentially where the entire view lived. Putting a Spacer beneath this pushes it up to the top of the screen.
You can additionally add padding to the top of the VStack to move the view lower if you do not want it touching the top of the screen.
Code below:
import SwiftUI
struct ContentView: View {
#State var bottomLeft: CGFloat = 25
var body: some View {
ZStack {
Color("background")
.edgesIgnoringSafeArea(.all)
VStack {
VStack {
Text("Good Morning, Sarah")
.font(Font.system(size: 36))
.foregroundColor(Color.blue)
.fontWeight(.semibold)
.frame(maxWidth: .infinity, alignment: .leading)
.padding(.horizontal)
Text("We're glad to see you and hope you're doing well. Let's take on the day.")
.font(Font.system(size: 20))
.foregroundColor(Color.blue)
.frame(maxWidth: .infinity, alignment: .leading)
.padding(.horizontal)
.padding(.bottom)
}
.background(Color.green)
.cornerRadius(bottomLeft)
.padding(.bottom, 10)
Spacer() //Added a spacer here
}
}
}
}

SwiftUI : How to ignore NavigationView When Add ScrollView

My previous code is
VStack {
MapView(coordinate: landmark.locationCoordinate)
.frame(height: 300, alignment: .center)
.edgesIgnoringSafeArea(.top)
CircleImageView(image: landmark.image)
VStack(alignment: .leading) {
Text("\(landmark.name)")
.font(.title)
.multilineTextAlignment(.center)
HStack {
Text("\(landmark.park)")
Spacer()
Text("\(landmark.state)")
}
.font(.subheadline)
.foregroundColor(.secondary)
Divider()
Text("\(landmark.category)")
.font(.title2)
Text("\(landmark.description)")
}
.padding()
Spacer()
}
My current build works fine. But when I add ScrollView as parent of my code, the map does not ignore the safe area.
Current build
After adding ScrollView
I used edgesIgnoringSafeArea(.all) but it didn't work. By the way, I am new to SwiftUI so I will appreciate any help.