Spacer Not Working with Form Inside a VStack

Spacer not working with Form inside a VStack

All scrollviews(which Form has built on) and shapes(which Circle is) are greedy in layout priority. They don't have inner limitations so if there's available space whey gonna take it

Spacer is greedy too, but it has lower priority then other greedy views

That's why in your case Form and Circle are splitting available space 50% to 50%

You need to restrict both their height to make it work.

VStack {
Circle().foregroundColor(.yellow).overlay(VStack {
Text("Example")
}).foregroundColor(.primary)
.frame(height: UIScreen.main.bounds.width)

Spacer()

Form {
TextField("test", text: $a)
TextField("test2", text: $b)
}.frame(height: 150)
}

SwiftUI Form not positioning correctly using Spacer()

As long as bottom of the Form is unknown(because Form is scrollable and its items start from the top), swiftUI does not know how much space should put between those two views. So Spacer() does not help.

Try this:

Give your form: for example, .frame(height: 500) or .padding(.top, 400) modifier.

SwiftUI Spacer not working - The element isn't pushed to the end of the row

Right now, the Circle view is taking up the entirety of the horizontal and vertical space, even though what is actually rendered as the circle is only shown in a small part (you can visualize this more clearly by adding a .border(Color.green) to the Circle view).

To fix this, you can use aspectRatio to make sure that the actual frame of the circle view stays a square (ie an aspect ratio of 1.0)

struct ContentView: View {
var body: some View {
HStack {
Text("test")
.font(.body)
.fontWeight(.light)
Spacer()
Circle()
.foregroundColor(.pink)
.background(Color.yellow)
.aspectRatio(1.0, contentMode: .fit)
}
.frame(height: 100)
}
}

It isn't immediately clear to me where you want the yellow background -- if you want it still taking up as much horizontal space as possible (but not under the text), you could use an additional HStack:

struct ContentView: View {
var body: some View {
HStack {
Text("test")
.font(.body)
.fontWeight(.light)
HStack {
Spacer()
Circle()
.foregroundColor(.pink)
.aspectRatio(1.0, contentMode: .fit)
}
.background(Color.yellow)
}
.frame(height: 100)
}
}

Sample Image

SwiftUI: Spacer doesn't work at ScrollView

Use GeometryReader

A container view that defines its content as a function of its own size and coordinate space.
https://developer.apple.com/documentation/swiftui/geometryreader

GeometryReader { geometry in
ScrollView {
VStack() {
-----
}
.frame(width: geometry.size.width, height: geometry.size.height)
}
}

Doing so, the VStack is full-screen.


You may not neeed to use ScrollView, because you do not need to scroll to see its content.

    var body: some View {

VStack() {
Text("Title_1")
.padding(.bottom, 35.0)
Text("Title_2")
.padding(.bottom, 32.0)
Text("Title_3")
.padding(.bottom, 27.0)

Spacer()
Button(action: { print("ACTION") }) {
Text("OK")
.font(.title)
.fontWeight(.semibold)
.foregroundColor(Color.red)

}
.frame(height: 35)
.cornerRadius(8.0)
.padding(.bottom, 25.0)
}
.frame(maxWidth: .infinity)
}

But if your content's height is more than the screen height, the OK button is at the bottom, regardless. Hence, you do not need to do anything.

Can't seem to utilize Spacer() between text() and textfield()

try adding .fixedSize() modifier to your TextField

   TextField("Enter URL", text: $url)
.fixedSize()

or set a frame like so

   TextField("Enter URL", text: $url)
.frame(width:200, height:50, alignment:.leading)

The problem is that TextField and Spacer() would take all available space and in this case TextField gets the priority; However, if you specify a fixed size or a frame to it then TextField won't stretch to take full space instead it will be fixed.

.fixedSize would allow your TextField to start small but eventually it will keep stretching the more text you write which can cause unwanted behavior.

.frame will fix your size to the provided width and hence there won't be any stretch and Spacer will have priority to take available space.

Questioning the dynamicity of Spacers in SwiftUI

You shouldn't need multiple Spacer()

GeometryReader { geometry in
VStack {
Spacer()

VStack {
HStack {
ForEach(0..<5) {_ in
Rectangle()
.scaledToFit()
}
}
.padding(.vertical)

Spacer()
}
.background(Color.blue)
.frame(height: geometry.size.height * 0.4)
}
}

Is this what you are trying to achieve?
If you know the size of the view with blue background which I made it as relative to 40% of screen height for now and then Spacer() will fill up the rest of the screen with space.

SwiftUI spacer layout issue

Since relativeWidth is deprecated and not available, if you want a responsive design you could use UIScreen.main.bounds.width, assigning this value to a variable you can use it in code to get the login frame of the right size proportional to the screen :

var loginViewWidth = UIScreen.main.bounds.width / 1.3 

I would nest the login view in a separate second ZStack.
Also I would use the (alignment: .bottom) modifier this ZStack to position the "forgotPasswordBottom" button to the bottom of the view automatically, and add the modifier 'frame' to the whole ZStack like this:

import SwiftUI

struct ContentView: View {

var loginViewWidth = UIScreen.main.bounds.width / 1.3

var body: some View {
ZStack{
Image("LoginBackground")
.resizable()
.aspectRatio(contentMode: .fill)
.edgesIgnoringSafeArea(.all)

ZStack(alignment: .bottom) {

RoundedRectangle(cornerRadius: 30).foregroundColor(.white)

Image("forgotPasswordBottom")
.resizable()
.aspectRatio(contentMode: .fit)

}.frame(width: loginViewWidth, height: loginViewWidth * 1.7)
}
}
}

SwiftUI Picker in Form - Can't Dynamically Size the Form Space

Form (and List) is not meant to be stacked inside other views like this, which is why it has such strange behavior.

Thankfully, it's fairly simple to recreate the stuff you do want using NavigationLink. Here’s a quick example of a couple custom views that do just that:

// drop-in NavigationLink replacement for Picker
struct NavigationButton<Content: View, SelectionValue: Hashable> : View {
@Binding var selection: SelectionValue
@ViewBuilder let content: () -> Content
@ViewBuilder let label: () -> Text

var body: some View {
NavigationLink {
PickerView(selection: $selection, content: content, label: label)
} label: {
HStack {
label()
Spacer()
Text(String(describing: selection))
.foregroundColor(.secondary)
}
.contentShape(Rectangle())
}
.buttonStyle(NavigationLinkButtonStyle())
}
}

// subview for the Picker page, which lets us use `dismiss()`
// to pop the subview when the user selects an option
struct PickerView<Content: View, SelectionValue: Hashable> : View {
@Binding var selection: SelectionValue
@ViewBuilder let content: () -> Content
@ViewBuilder let label: () -> Text
@Environment(\.dismiss) private var dismiss

var body: some View {
Form {
Picker(selection: $selection, content: content, label: label)
.pickerStyle(.inline)
.labelsHidden()
.onChange(of: selection) { _ in
dismiss()
}
}
.navigationTitle(label())
}
}

// recreate the appearance of a List row
struct NavigationLinkButtonStyle: ButtonStyle {
func makeBody(configuration: Configuration) -> some View {
HStack {
configuration.label
.frame(maxWidth: .infinity)
Image(systemName: "chevron.right")
.font(.footnote.bold())
.foregroundColor(Color(UIColor.tertiaryLabel))
}
.padding()
.background(
Rectangle()
.fill(configuration.isPressed ? Color(UIColor.quaternaryLabel) : Color(UIColor.systemBackground))
)
}
}

If you like the .insetGrouped style you got using Form, we can replicate that by putting NavigationButton inside a clipped VStack:

VStack(spacing: 0) {

NavigationButton(selection: $selectedNumber1) {
ForEach(0..<10) {
Text("\($0)")
}
} label: {
Text("A")
}

Divider()

NavigationButton(selection: $selectedNumber2) {
ForEach(0..<10) {
Text("\($0)")
}
} label: {
Text("B")
}
}
.clipShape(RoundedRectangle(cornerRadius: 11))
.padding()
.background(Color(UIColor.systemGroupedBackground))

And here’s a screenshot showing my custom views above your original Form.

Sample Image

(And if you like Picker as a popup menu, you could use Menu instead of NavigationLink)



Related Topics



Leave a reply



Submit