SwiftUI - Align Text in a VStack - ios

How to align a Text with left alignment in a VStack that is occupying more space like this
This is the code am using
VStack(alignment:.leading) {
Text("Test")
.font(.system(size: 10))
.frame(maxWidth: .infinity, maxHeight: .infinity)
}.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(Color.green)

You need to add alignment to Text frame, like
VStack(alignment:.leading) {
Text("Test")
.font(.system(size: 10))
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .leading) // << here !!
}.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(Color.green)

Try using this modifier on your Text view: .multilineTextAlignment(.leading)

Related

How does the position of the .frame() modifier affect the view?

I'm beginner in SwiftUI and I had some misunderstanding with the .frame() modifier.
I want to create a right-aligned image, so I'm doing this:
Image(systemName: "xmark")
.frame(maxWidth: .infinity, alignment: .leading)
.padding(5)
.background(.gray)
.mask(Circle())
.padding()
But the image appears in the center of my screen. I changed my code to this:
Image(systemName: "xmark")
.padding(5)
.background(.gray)
.mask(Circle())
.frame(maxWidth: .infinity, alignment: .leading)
.padding()
And it works the way I want it to, but I'm so confused as to why this code works. Can someone explain to me why this code works and the previous one doesn't?
I also see that people write code like that:
VStack {
...
}
.padding()
.frame(maxWidth: 200)
.frame(maxWidth: .infinity, alignment: .leading)
Does that even make sense? Aren't we redefining maxWidth with the second .frame() modifier?

Set the width of an Image to half the width of its HStack container view in SwiftUI?

So I just want to do something pretty simple which is have an Image take half the size of its container and a Vstack the other half.
Im currently doing this to force both views to take as much space as they can with maxWidth: .infinity, so they end up taking equal amounts of space:
struct ContentView: View {
var body: some View {
HStack {
Image("landscape")
.resizable()
.scaledToFill()
.frame(maxWidth: .infinity, maxHeight: 170)
VStack {
Text("Test")
Text("Test 2")
}
.frame(maxWidth: .infinity)
}
.frame(maxWidth: .infinity, maxHeight: 170)
.background(Color.red)
.padding()
}
}
But it ends up in this poor result, sadly:
Any tips? I saw GeometryReader might come in handy here but seems to push my Views all the way to the top and end up with unwanted results.
you need to add a fixedSize() modifier to HStack and keep it's vertical to true.
var body: some View {
HStack {
Image("1")
.resizable()
.scaledToFill()
.frame(maxWidth: .infinity, maxHeight: 170)
VStack {
Text("Test")
Text("Test 2")
}
.frame(maxWidth: .infinity)
}
.frame(maxWidth: .infinity, maxHeight: 170)
.background(Color.red)
.padding()
.fixedSize(horizontal: false, vertical: true)
}
you can read more about fixedsize() here
Now your HStack will be like this

swiftUI form bug on XCode13 and iOS15 and iPadOS15

I was building a form on Xcode 12 and using iOS 14 and everything was working as expected. But after switching to 13 and iOS 15, the height of the cells are inconsistent, even changing when scrolled offscreen like they were a reusable table cell.
For example, I have this code: *** EDIT: I changed the below code to have everything that's currently on that page.
Struct EvalViewMHx: View {
var body: some View {
VStack {
Form {
Label(
title: {
Text("Demographics & Medical History")
.font(.title)
.foregroundColor(.white)
.padding()
},
icon: {
Image(systemName: "heart.text.square.fill")
.resizable()
.aspectRatio(contentMode: .fit)
.foregroundColor(.white)
})
.listRowBackground(Color.blue)
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity, alignment: .center)
Section(header: Text("Demographics")) {
demographicsView(nameText: $nameText, DOB: $DOB, height: $height, weight: $weight)
}
Section(header: Text("Medical History")) {
VStack {
HStack {
Text("When did you get hurt?")
.padding()
Spacer()
Toggle("", isOn: $doesNotKnowInjuryDate)
.frame(width: 35)
.padding(.horizontal)
Text("I don't know")
.fontWeight(!doesNotKnowInjuryDate ? .regular : .bold)
.padding(.leading)
}
if !doesNotKnowInjuryDate {
DatePicker("Injury Date", selection: $injuryDate, displayedComponents: [.date])
.foregroundColor(Color(UIColor.systemGray3))
.padding()
}
}
.frame(minWidth: .zero, idealWidth: .infinity, maxWidth: .infinity, minHeight: 150, idealHeight: 150, maxHeight: 150, alignment: .center)
VStack(alignment: .leading) {
HStack {
Text("Where is your injury?")
Spacer()
Text("Right")
.fontWeight(isInjuryOnRightSide ? .regular : .bold)
Toggle("", isOn: $isInjuryOnRightSide)
.toggleStyle(SwitchToggleStyle(tint: Color(UIColor.systemGray5)))
.frame(width: 35)
.padding(.horizontal)
Text("Left")
.fontWeight(!isInjuryOnRightSide ? .regular : .bold)
.padding(.leading)
}
.zIndex(2.0)
Picker(selection: $injuryLocation, label: Text(""), content: {
ForEach(injuryLocations, id: \.self) { location in
Text(location)
}
})
.pickerStyle(WheelPickerStyle())
.frame(minWidth: .zero, idealWidth: .infinity, maxWidth: .infinity, minHeight: 100, idealHeight: 100, maxHeight: 100, alignment: .center)
.zIndex(1.0)
.padding()
}
.padding()
VStack(alignment: .leading) {
Text("How did you get hurt? (Method of Injury)")
.padding()
TextEditor(text: $MOI)
.frame(height: 150)
.background(Color(UIColor.systemGray5).opacity(0.75))
.cornerRadius(15)
}
.frame(minWidth: .zero, idealWidth: .infinity, maxWidth: .infinity, minHeight: 200, idealHeight: 200, maxHeight: 200, alignment: .center)
.padding()
if injuryLocation != "Lower Back" && injuryLocation != "Upper Back" && injuryLocation != "Head / Neck" && injuryLocation != "Chest" {
VStack {
pastInjuryHistory(injuryLocation: $injuryLocation, hasHadSameSideInjury: $hasHadSameSideInjury, hasHadOppositeSideInjury: $hasHadOppositeSideInjury, isSameSide: true)
pastInjuryHistory(injuryLocation: $injuryLocation, hasHadSameSideInjury: $hasHadSameSideInjury, hasHadOppositeSideInjury: $hasHadOppositeSideInjury, isSameSide: false)
}
}
}
}
}
}
}
(In the picture below, you're also seeing some buttons at the top, which are just rectangles in a horizontal scroll view. The code above is then just placed below that in a tab view like so: )
TabView(selection: $activeTab) {
EvalViewMHx()
.tag(1)
EvalObjectiveView()
.tag(2)
viewThree(activeTab: $activeTab)
.tag(3)
viewFour(activeTab: $activeTab)
.tag(4)
}
.tabViewStyle(PageTabViewStyle())
.onChange(of: activeTab, perform: { value in
print(activeTab)
})
.animation(.default)
and the output shows that one of the cells has a totally different height.
Even stranger, another section has a cell with a 0 height but still shows the text field sitting over the rest of the cells. I have a picker view that is also in these cells and putting the pickerStyle to
.pickerStyle(InlinePickerStyle())
expands the cell to larger than an iPad screen size and doesn't put it in a wheel like it did in the previous versions.
Are these bugs that I should just be patient for a fix, or is there something that I can do to fix this and keep moving with my project?
Was struggling with the same issue. Turned out .animation caused the issue, as its deprecated since iOS 15. Removing that line fixed the issue for me.

alignmentGuide() not overriding child views

I initially align my child views to the .top by giving alignment: .top to my parent HStack.
I then want to have one child to the left (.leading) and the other child to the right (.trailing).
However this is not working, they are both in the center (screenshot below):
import SwiftUI
struct TestView: View {
var body: some View {
HStack{
Text("First")
.alignmentGuide(.leading) { d in d[.leading] }
Text("Second")
.alignmentGuide(.trailing) { d in d[.trailing] }
}.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity, alignment: .top)
.padding(.top, 50)
}
}
Any idea why?
I then want to have one child to the left (.leading) and the other child to the right (.trailing).
It can be achieved in much simpler way
HStack{
Text("First")
Spacer()
Text("Second")
}.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .top)

VStack inside ScrollView bottom alignment in SwiftUI

Currently building a chat application and I need new messages to appear at the bottom of the screen. I also need to have messages aligned to the bottom. However, using VStack inside ScrollView features top alignment by default.
ScrollView {
VStack(alignment: .leading, spacing: 16) {
Spacer()
.frame(minWidth: 0, maxWidth: .infinity, minHeight:0, maxHeight: .infinity, alignment: Alignment.topLeading)
ForEach(notList, id: \.self) { not in
NotRow(not: not)
}
}
.padding()
.frame(minWidth: 0, maxWidth: .infinity, minHeight:0, alignment: Alignment.topLeading)
}
What should I do to fix this?
A bit late to answer that one but it might help someone else. You could use a good old double rotation trick to achieve the result you want. Other solutions does not work because the maximum size inside a scroll view is undefined as it depends on its child views to compute its own content size.
ScrollView {
VStack(alignment: .leading, spacing: 16) {
Spacer()
.frame(minWidth: 0, maxWidth: .infinity, minHeight:0, maxHeight: .infinity, alignment: Alignment.topLeading)
ForEach(notList, id: \.self) { not in
NotRow(not: not)
}
}
.padding()
.rotationEffect(Angle(degrees: 180))
}
.rotationEffect(Angle(degrees: 180))
ScrollView does not propose the same height to its children (VStack in this case) as it was proposed to it. It just asks for minimal size. The solution is to make its direct child respond with the minimum height which was proposed to ScrollView itself. We can achieve this by reading the proposed height via GeometryReader and setting that as minimum height of the child.
GeometryReader { reader in
ScrollView {
VStack {
/// Spacer() or Color.red will now behave as expected
}
.frame(minHeight: reader.size.height)
}
}
I think you're missing a maxHeight in VStack
VStack(alignment: .leading) {
Spacer().frame(maxWidth: .infinity)
// content here
}
.frame(maxHeight: .infinity) // <- this
If you remove Spacer() and put it after the closing braces of your ForEach, it will fix it. Here is how your updated code will look like:
ScrollView {
VStack(alignment: .leading, spacing: 16) {
.frame(minWidth: 0, maxWidth: .infinity, minHeight:0, maxHeight: .infinity, alignment: Alignment.topLeading)
ForEach(notList, id: \.self) { not in
NotRow(not: not)
}
Spacer()
}
.padding()
.frame(minWidth: 0, maxWidth: .infinity, minHeight:0, alignment: Alignment.topLeading)
}
Try changing all of your Alignment.topLeadings to Alignment.bottomLeading

Resources