SwiftUI: Spacer doesn't work at ScrollView - ios

How can I get space in VStack to pin the button at the bottom?
ScrollView {
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)
}
what I have
what I want to have

The ScrollView gives that VStack infinite possible height. That's why the spacers not working.
The Fix:
Give the VStack a minimumHeight that’s the height of the ScrollView or View
Also important to set the scrollview's width to be the view's width
GeometryReader { geometry in
ScrollView(showsIndicators: false) {
VStack {
...
}
.frame(minHeight: geometry.size.height)
}.frame(width: geometry.size.width)
}
THIS WAY THE CONTENT WILL BE ABLE TO LAYOUT BEYOND THE VIEW's HEIGHT IF NEEDED

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.

Setting minHeight to screen height like other answers suggested works, but it brings issue where the ScrollView will no longer scroll beyond the screen height.
Proper solution is wrapping a VStack outside of the ScrollView.
For your case,
VStack {
ScrollView {
VStack() {
Text("Title_1")
.padding(.bottom, 35.0)
Text("Title_2")
.padding(.bottom, 32.0)
Text("Title_3")
.padding(.bottom, 27.0)
}
.frame(maxWidth: .infinity)
}
Spacer()
Button(action: { print("ACTION") }) {
Text("OK")
.font(.title)
.fontWeight(.semibold)
.foregroundColor(Color.red)
}
.frame(height: 35)
.cornerRadius(8.0)
.padding(.bottom, 25.0)
}

Related

How to place a non-sticky button inside a scrollview to the bottom of a main view in Swiftui? Spacer seems to have no effect inside a scrollview

I am trying to place a button inside a scrollview to the bottom of the main view with some bottom padding after a spacer. But looks like there is no effect of spacer inside a scrollview. I don't want to hardcode spacer with minWidth. Any suggestion would be appreciated?
ScrollView {
VStack {
Spacer()
NavigationLink(destination: Text("Hi")) {
Button {
} label: {
Text("Submit")
.frame(height: 42)
}
.buttonStyle(FilledButton())
.frame(width: 248, height: 42, alignment: .center)
}
.padding(.top, 33)
.padding(.bottom, 12)
}
}
I believe you need a Geometry Reader to grab the height of the ScrollView itself.
struct ContentView: View {
var body: some View {
GeometryReader { geometry in
ScrollView {
VStack {
Spacer()
NavigationLink(destination: Text("Hi")) {
Button {
} label: {
Text("Submit")
.frame(height: 42)
}
.buttonStyle(FilledButton())
.frame(width: 248, height: 42, alignment: .center)
}
.padding(.top, 33)
.padding(.bottom, 12)
}
.frame(maxWidth: .infinity)
.frame(height: geometry.size.height)
}
}
}
}

SwiftUI: Build View from bottom alignment in scrollView

Is there a way to build UI elements in SwiftUI inside scroll view with bottom Alignment?
My use case: I have a screen where the screen has
Spacer (What ever is left after allocating below elements)
LogoView
Spacer() - 30
Some Text - 4/5 lines
Spacer() - 50 (this will be calculated off of GR size Height)
HStack with Two Button - This should be pinned to bottom of view / ScrollView
I would like to know how Can I pin the HStack view to ScrollView Bottom
I've replicated my setup and reproduced my problems in a Swift playground like so
struct ContentView: View {
var body: some View {
GeometryReader { gr in
ScrollView {
VStack {
Spacer()
Image(systemName: "applelogo")
.resizable()
.frame(width: gr.size.width * 0.5, height: gr.size.height * 0.3, alignment: .center)
Spacer().padding(.bottom, gr.size.height * 0.01)
Text("SOME TEXT SOME TEXT SOME TEXT SOME TEXT SOME TEXT SOME TEXT SOME TEXT SOME TEXT SOME TEXT SOME TEXT SOME TEXT SOME TEXT SOME")
.fontWeight(.regular)
.foregroundColor(.green)
.multilineTextAlignment(.center)
.padding(.top, gr.size.height * 0.05)
.padding(.horizontal, 40)
.layoutPriority(1)
Spacer()
.frame(minHeight: gr.size.height * 0.12)
HStack {
Button(action: {
}, label: {
Text("Button 1")
})
.foregroundColor(Color.white)
.padding(.vertical)
.frame(minWidth: 0, maxWidth: .infinity)
.background(Color.blue)
.cornerRadius(8)
Button(action: {
}, label: {
Text("Button 2")
})
.foregroundColor(Color.white)
.padding(.vertical)
.frame(minWidth: 0, maxWidth: .infinity)
.background(Color.blue)
.cornerRadius(8)
}
.padding(.horizontal, 20)
}
//.frame(maxWidth: .infinity, maxHeight: .infinity)
}
}
}
}
I understand that when scrollView is introduced in SwiftUI view Spacer length is changed to Zero, Would like to know what's the best way to achieve this
struct SampleView: View {
var body: some View {
GeometryReader { gr in
VStack {
ScrollView {
VStack {
// Fills whatever space is left
Rectangle()
.foregroundColor(.clear)
Image(systemName: "applelogo")
.resizable()
.frame(width: gr.size.width * 0.5, height: gr.size.height * 0.3, alignment: .center)
.padding(.bottom, gr.size.height * 0.06)
Text("SOME TEXT SOME TEXT SOME TEXT SOME TEXT SOME TEXT SOME TEXT SOME TEXT SOME TEXT SOME TEXT SOME TEXT SOME TEXT SOME TEXT SOME")
.fontWeight(.regular)
.foregroundColor(.green)
.multilineTextAlignment(.center)
.padding(.horizontal, 40)
.layoutPriority(1)
// Fills 12 %
Rectangle()
.frame(height: gr.size.height * 0.12)
.foregroundColor(.clear)
HStack {
Button(action: {
}, label: {
Text("Button 1")
})
.foregroundColor(Color.white)
.padding(.vertical)
.frame(minWidth: 0, maxWidth: .infinity)
.background(Color.blue)
.cornerRadius(8)
Button(action: {
}, label: {
Text("Button 2")
})
.foregroundColor(Color.white)
.padding(.vertical)
.frame(minWidth: 0, maxWidth: .infinity)
.background(Color.blue)
.cornerRadius(8)
}
.padding(.horizontal, 20)
.padding(.bottom, 20)
}
// Makes the content stretch to fill the whole scroll view, but won't be limited (it can grow beyond if needed)
.frame(minHeight: gr.size.height)
}
}
}
}
}
I wanted the scrollview to appear bottom to top instead of top to bottom. This had my answer: https://www.thirdrocktechkno.com/blog/implementing-reversed-scrolling-behaviour-in-swiftui/
TLDR: .rotationEffect(Angle(degrees: 180)).scaleEffect(x: -1.0, y: 1.0, anchor: .center) on the scrollview AND on the items in the scrollview

How to adjust navigationBarTitle and Label Text alignment in SwiftUI

I just learned swiftUI and I got little trouble. I want to make navigationBarTitle and title headline alignment like this:
Image 1: I want to make my view like this
I have tried to make like below but it does not work:
struct HeaderView: View {
var body: some View {
NavigationView {
VStack {
Image("kante_training_champions_league")
.resizable()
.scaledToFill()
.frame(width: 370, height: 150)
.cornerRadius(10.0)
Text("KANTE: NEW PLAYERS DON’T SEEM NEW")
.font(.title)
.fontWeight(.bold)
.multilineTextAlignment(.leading)
.frame(width: 370)
Spacer()
}
.navigationBarTitle("Chelsea FC")
}
}
}
From my code above, I got a view like this:
Image 2: I got a view like this from my code above
Could someone help me how to get a view like I want
Try leading alignment
var body: some View {
NavigationView {
VStack(alignment: .leading) { // << here !!
// ... no changes in image
Text("KANTE: NEW PLAYERS DON’T SEEM NEW")
.font(.title)
.fontWeight(.bold)
.padding(.leading) // << here !!
.multilineTextAlignment(.leading)
}
You should add alignment to StackView. You can change alignment to .leading, .trailing or .center. It is centered by default thats why you are having the label in center.
var body: some View {
NavigationView {
VStack(alignment: .leading) {
// Your Code
}
}
}
Remove .frame(width: 370) and use .frame(maxWidth: .infinity) so that the text takes the whole width of its parent.
VStack {
Image("kante_training_champions_league")
.resizable()
.scaledToFill()
.frame(width: 370, height: 150)
.cornerRadius(10.0)
Text("KANTE: NEW PLAYERS DON’T SEEM NEW")
.font(.title)
.fontWeight(.bold)
.multilineTextAlignment(.leading)
.frame(maxWidth: .infinity)
Spacer()
}

SwiftUI vertical ScrollView springs back up and doesn't show all the views within it

Im working on a project with a similar layout to this where I have a few views stacked within a VStack and a button on the bottom, all embedded in a scrollview.
as you can see in the image the scrollview springs back and doesn't show the button.
var body: some View {
VStack {
Rectangle()
.fill(Color.green)
.frame(height: 300)
Spacer()
ScrollView {
RectanglesView()
Button(action: /*#START_MENU_TOKEN#*/{}/*#END_MENU_TOKEN#*/) {
Text("Click me")
}
.offset(y: 50)
.frame(width: 300)
}
}.edgesIgnoringSafeArea(.all)
}
I do believe that the issue is due to the button's offset since it behaves normally if I remove it, but I don't want to lose the placement of the button.
Instead of offset, add padding, like this and within a VStack (slightly amended code below)
var body: some View {
ZStack{
VStack {
Rectangle()
.fill(Color.green)
.frame(height: 300)
Spacer()
ScrollView {
VStack{
Rectangle()
.fill(Color.red)
.frame(height: 300)
Rectangle()
.fill(Color.yellow)
.frame(height: 300)
Button(action: /*#START_MENU_TOKEN#*/{}/*#END_MENU_TOKEN#*/) {
Text("Click me")
}.padding() //instead of offset
.frame(width: 300)
}.frame(alignment: .leading)
}
}.edgesIgnoringSafeArea(.all)
}
}
Have you tried moving the Button out of the ScrollView()?
Here is what looks like:
struct ContentView: View {
var body: some View {
VStack {
Rectangle()
.fill(Color.green)
.frame(height: 300)
Spacer()
ScrollView(.vertical) {
Rectangle().fill(Color.red).frame(width: 400, height: 200)
Rectangle().fill(Color.blue).frame(width: 400, height: 200)
Rectangle().fill(Color.yellow).frame(width: 400, height: 200)
Rectangle().fill(Color.green).frame(width: 400, height: 200)
}
Button(action: /*#START_MENU_TOKEN#*/{}/*#END_MENU_TOKEN#*/) {
Text("Click me")
.font(.headline)
}
}.edgesIgnoringSafeArea(.all)
}
}

Full Screen View not working inside HStack and Horizontal ScrollView

I have been trying to create onboarding flow using horizontal lists etc. I have created a view called OnboardingViewand inside it I have a VStack with image, and two Text views.
struct OnboardingView: View {
var body: some View {
GeometryReader { geometry in
VStack(spacing: 10) {
Spacer()
Image("1")
.resizable()
.frame(width: 300, height: 300)
.clipShape(Circle())
.padding(20)
Text("Travel the World")
.frame(width: geometry.size.width, height: 20, alignment: .center)
.font(.title)
Text("Explore the world and get to know different cultures and people from all around the world")
.lineLimit(nil)
.padding(.leading, 15)
.padding(.trailing, 15)
.font(.system(size: 16))
.frame(width: geometry.size.width, height: 50, alignment: .center)
.multilineTextAlignment(.center)
Spacer()
}.background(Color.red)
}
}
}
This is what I get with the above code:
Now I am trying to add this view inside a ScrollView, using HStack.
struct ContentView: View {
var body: some View {
ScrollView(.horizontal, showsIndicators: false) {
HStack {
OnboardingView()
OnboardingView()
}
}.background(Color.black)
}
}
The result of the above code is absurd! This is what I get. How to go about fixing this? Help will be appreciated! Thanks.
One possible solution would be to set the width of the OnboardingView to geometry.size.width:
var body: some View {
GeometryReader { geometry in
ScrollView(.horizontal, showsIndicators: false) {
HStack {
OnboardingView()
.frame(width: geometry.size.width)
OnboardingView()
.frame(width: geometry.size.width)
}
}.background(Color.black)
}
}
Result

Resources