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)
Related
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
I am developing an app in where there is a list of navigation links which are images. I want the image to be the full width of the screen but there is padding from the left. I have tried several things:
.frame( maxWidth: .infinity)
.ignoresSafeArea()
.listStyle(PlainListStyle())
NavigationView {
VStack {
Text("Available guides in: ").padding(.bottom, -1000).padding(.top,100).font(.custom("SF Mono-Light", size: 28)).edgesIgnoringSafeArea(.all)//.foregroundColor(Color.white)
Text("Somewhere").padding(.bottom, -90).padding(.top,-70).font(.custom("SF Mono-Light", size: 32))//.foregroundColor(Color.white)//.edgesIgnoringSafeArea(.all)
List {
ForEach(guides) { guide in
NavigationLink(destination: GuideView(guideSessionManager: GuideSessionManager(guide: guide)), tag : guide.guideName, selection: $selection) {
Button(action: {
selection = guide.guideName
}) {
Image(guide.imageName)
.renderingMode(Image.TemplateRenderingMode?.init(Image.TemplateRenderingMode.original))
}.buttonStyle(GrowingButtonImage())
.frame( maxWidth: .infinity)
.ignoresSafeArea()
}
.frame( maxWidth: .infinity)
.listRowInsets(EdgeInsets())
.ignoresSafeArea()
}
}
.frame( maxWidth: .infinity)
.ignoresSafeArea()
.listStyle(PlainListStyle())
.frame( maxWidth: .infinity)
.ignoresSafeArea()
.listStyle(PlainListStyle())
You're giving it EdgeInsets without parameters and setting the width to .infinity with .ignoresSafeArea, which causes a weird interaction. Either remove .ignoresSafeArea, use parameters on EdgeInsets or don't use .infinity
List {
ForEach(guides) { guide in
NavigationLink(destination: GuideView(guideSessionManager: GuideSessionManager(guide: guide)), tag : guide.guideName, selection: $selection) {
Button(action: {
selection = guide.guideName
}) {
Image(guide.imageName)
.renderingMode(Image.TemplateRenderingMode?.init(Image.TemplateRenderingMode.original))
}
.buttonStyle(GrowingButtonImage())
.frame( maxWidth: .infinity)
.edgesIgnoringSafeArea(.all)
}
// Modify these to your liking
.listRowInsets(EdgeInsets(top: 0.0, leading: 0.0, bottom: 0.0, trailing: 0.0))
}
You can also try using .scaledToFill()
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.
I'd like to ask you some help. I'm trying to capture VStack height using geometry and then based on that VStack height value, calculate its child element's height (inside VStack).
Image of my current view of VStack
I used .frame outside of VStack to fill the whole screen. Then I used .border to visually check if it actually fills the screen (it works fine)
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity, alignment: Alignment.topLeading)
The problem can be seen inside the VStack, the height is displayed as GCFloat 734. Even though the border size is a lot bigger.
struct BodyView: View {
#State var stackHeight : CGFloat = 0
var body: some View {
GeometryReader { geometry in
VStack {
Text("VStack size: \(self.stackHeight)")
.bold()
.frame(maxWidth: .infinity, alignment: .leading)
.padding(.horizontal)
.background(Color.green)
}
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity, alignment: Alignment.topLeading)
.onAppear{
self.stackHeight = geometry.size.height
}
.border(Color.purple, width: 5)
}
}
}
struct BodyView_Previews: PreviewProvider {
static var previews: some View {
BodyView()
}
}
How could I capture the actual size of the VStack when it finishes loading?
When I trigger (onAppear) the VStack height appears correct, however I need it to be captured instantly.
If I understand your questions, you're very close — you just need to use to GeometryProxy type:
import SwiftUI
struct BodyView: View {
var body: some View {
GeometryReader { geometry in
VStack {
Text("VStack size: \(geometry.size.height)") // no need for state var anymore
.bold()
.frame(maxWidth: .infinity, alignment: .leading)
.padding(.horizontal)
.background(Color.green)
}
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity, alignment: Alignment.topLeading)
.border(Color.purple, width: 5)
}
}
}
struct BodyView_Previews: PreviewProvider {
static var previews: some View {
BodyView()
}
}
A GeometryReader will lay itself out again if its size changes.
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