ScrollView in VStack align to bottom in swiftui - swiftui

I can't get this scrollview to align to the bottom in SwiftUI!
I've three items in the scrollview, but I want them to be aligned to the bottom of the screen (the red arrow shows where I WANT the items to be!)
Here is my code:
import SwiftUI
struct ContentView: View {
var body: some View {
ZStack {
Color.blue.edgesIgnoringSafeArea(.all)
VStack {
ScrollView {
Spacer(minLength: 80)
Text("a")
Text("b")
Text("c")
}.frame(maxHeight: /*#START_MENU_TOKEN#*/.infinity/*#END_MENU_TOKEN#*/)
Text("Button")
.padding()
}.frame(alignment: .bottom)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
I'm trying spacers, and rotating content by 180 degrees? What should I do?

GeometryReader to the rescue! First, wrap the ScrollView in a GeometryReader so you can get the scrolling area’s height. Then set that as the minimum height of the ScrollView’s content and use bottom alignment:
var body: some View {
ZStack {
Color.blue.edgesIgnoringSafeArea(.all)
VStack {
GeometryReader { geometry in
ScrollView {
VStack {
Text("a")
Text("b")
Text("c")
}
.frame(maxWidth: .infinity,
minHeight: geometry.size.height,
alignment: .bottom)
}
}
Text("Button")
.padding()
}
}
}

Related

Scrollable content not working as expected in SwiftUI

I'm trying to create a scroll view with my custom view, but when I add scroll to view it's not working as expected, without scroll view working fine.
struct ContentView: View {
var body: some View {
ScrollView(.vertical) {
VStack {
ForEach (0..<2) { _ in
ListItem()
}
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
// But the below code is working fine.
struct ContentView: View {
var body: some View {
VStack {
ForEach (0..<2) { _ in
ListItem()
}
}
}
}
// List Item
struct ListItem: View {
var body: some View {
VStack {
HStack {
Image("steve")
.resizable()
.clipShape(Circle())
.aspectRatio(contentMode: .fit)
.frame(maxWidth:44, maxHeight: 44)
VStack {
Text("Steve Jobs")
.font(.headline)
.frame(maxWidth: .infinity, alignment: .leading)
Text("1 hour ago")
.font(.footnote)
.frame(maxWidth: .infinity, alignment: .leading)
}
Spacer()
}
ZStack(alignment:.top) {
GeometryReader { geometry in
VStack {
ZStack {
Image("poster_1")
.resizable()
.aspectRatio(contentMode: .fit)
.cornerRadius(8)
.shadow(color: Color.black.opacity(0.12),
radius: 4, x: 1, y: 1)
.frame(width: geometry.size.width - 64,
height: geometry.size.height * 0.35)
.padding([.horizontal], 32)
.clipped()
ZStack {
Rectangle()
.fill(Color.black.opacity(0.75))
.frame(maxWidth:84 , maxHeight: 84)
.cornerRadius(12)
Image(systemName: "play.fill")
.font(.system(size: 44, weight: .bold))
.foregroundColor(.white)
}
}
VStack {
Text("Game of Thrones")
.accentColor(Color.gray.opacity(0.25))
.font(Font.subheadline.weight(.bold))
.padding([.horizontal], 32)
.padding([.bottom], 2)
.frame(maxWidth: .infinity,
alignment: .leading)
VStack {
Text("Game of Thrones is an American fantasy drama television series created by David Benioff and D. B. Weiss for HBO. ")
.accentColor(Color.gray.opacity(0.25))
.font(.footnote)
.frame(maxWidth: .infinity,
alignment: .leading)
.padding([.horizontal], 32)
Text("Show more...")
.accentColor(Color.gray.opacity(0.01))
.font(Font.footnote.weight(.bold))
.frame(maxWidth: .infinity,
alignment: .trailing)
.padding([.trailing], 32).onTapGesture {
print("okay")
}
}
}
}
}
}
}
}
}
ListItem contains multiple views which creates publisher info and movie information as shown below image.
Scrollview is scrolling but images are not shown in view as first image.
It is the geometry reader that you have in ListItem. Because neither a GeometryReader nor a Scrollview have their own size. Since neither no what size to render, they collapse. This is what you are seeing in your view. See this answer. The solution is to put the GeometryReader into ContentView outside the Scrollview and send the GeometryProxy that you called geometry into ListItem something like this:
struct ContentView: View {
var body: some View {
GeometryReader { geometry in
ScrollView(.vertical) {
VStack {
ForEach (0..<2) { _ in
ListItem(geometry: geometry)
}
}
} // Scrollview
} // GeometryReader
}
}
struct ListItem: View {
let geometry: GeometryProxy
var body: some View {
...
}
This seems to fix it in Preview, though you may have to change your multipliers in the .frame() that uses geometry to size it how you want.

How to center a second HStack View?

I'm having trouble centering that second "Date" Text within the HStack. As you can see, in the image, it is a bit farther to the left. I want only the second view to be centered in the HStack. I want the first View to be latched to leading.
Here is the code.
import SwiftUI
struct DaySummariesBarChart: View {
var body: some View {
HStack {
Text("Date")
.font(.footnote)
Text("Date")
Spacer()
}
}
}
struct BarChart_Previews: PreviewProvider {
static var previews: some View {
DaySummariesBarChart()
.previewLayout(.sizeThatFits)
}
}
This is a pretty clean way to do it:
var body: some View {
ZStack {
Text("Date")
.font(.footnote)
.frame(maxWidth: .infinity, alignment: .leading)
Text("Date")
}
}
The first Text gets a maxWidth of infinity, so it takes up the whole space, but is aligned to .leading.
The second Text is centered by default in the ZStack.
The Spacer() moves the Views to the left. Your problem should be solved by adding another Spacer() on the other side.
struct DaySummariesBarChart: View {
var body: some View {
HStack {
Spacer()
Text("Date")
.font(.footnote)
Text("Date")
Spacer()
}
}
}
You can use a GeometryReader to make the width of the Views exactly half and therefore center the second one.
struct DaySummariesBarChart: View {
var body: some View {
GeometryReader { geometry in
HStack {
Text("Date")
.font(.footnote)
.frame(width: geometry.size.width / 2)
Text("Date")
.frame(width: geometry.size.width / 2)
}
}
}
}

Dynamic height for ScrollView content not working

I have content in my ScrollView that is sometimes short and other times longer than the screen. I'm trying to stretch the content to the bottom if it is too short, but no matter what I do the content stays short:
This is what the code looks like:
struct ContentView: View {
var body: some View {
ScrollView {
VStack {
Text("Header")
.padding()
VStack {
Text("Content")
Image(systemName: "bell")
Spacer()
}
.padding()
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(Color(.systemBackground))
}
}
.background(Color.yellow.ignoresSafeArea())
}
}
I would've expected the Spacer() in my VStack to stretch it to the end. Or the maxHeight: .infinity to help me out as well, but the content always stays short even tho the ScrollView is the screen height (because of the yellow background on it). This becomes more of a problem on iPad when the one screen shows the big gap. How can this be solved for various screen height?
Using #CenkBilgen's idea of using an overlay + GeometryReader, you could do the following:
struct ContentView: View {
var body: some View {
Color.clear
.overlay(GeometryReader { geo in
ScrollView {
VStack {
ForEach(1..<10) {
Text("\($0)").padding()
}
}
HStack { Spacer() }
}
.clipped()
.frame(width: geo.size.width, height: geo.size.height)
})
}
}

How position HStack in bottom of my screen no use the spacer()

I want move the HStack in bottom of my screen no use the spacer() because when use the spacer move the logo for top of my screen.
Here is a solution without using Spacer:
struct ContentView: View {
var body: some View {
VStack {
VStack {
Text("Image")
Text("Some text")
}.frame(minHeight: 0, maxHeight: .infinity)
HStack {
Text("Bottom")
}
}
}
}
I know you asked for no spacer, but this code shows you can use them without your logo going to the top of the screen. Alternatively you can use ".position(CGPoint(...))"
struct ContentView: View {
var body: some View {
VStack {
Spacer()
VStack (alignment: .center) {
Image("your-image").resizable().frame(width: 90, height: 95)
Text("TCheck time").font(.title).foregroundColor(.gray)
}.padding()
Spacer()
HStack (alignment: .bottom) {
Text("2020 SplitWay").font(.subheadline).foregroundColor(.gray)
}
}
}
}

SwiftUI: animating between Single and HStack views

Goal:
1- Shows a view (Blue) that covers entire screen
2- When tapped on bottom (top right corner), it shows an HStack animating right side HStack (Green) "Slide Offset animation".
import SwiftUI
struct ContentView: View {
#State var showgreen = false
var body: some View {
NavigationView {
HStack {
Rectangle()
.foregroundColor(.blue)
if showgreen {
Rectangle()
.foregroundColor(.green)
.offset(x: showgreen ? 0 : UIScreen.main.bounds.width)
.animation(.easeInOut)
}
}
.navigationBarItems(trailing:
Button(action: { self.showgreen.toggle() }) {
Image(systemName: "ellipsis")
})
}
.navigationViewStyle(StackNavigationViewStyle())
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
.colorScheme(.dark)
.previewDevice("iPad Pro (12.9-inch) (3rd generation)")
}
}
The code works, however I cannot get the Green "Slide Offset animation" to work. Really appreciate any help! : )
Instead of using the if conditional, you need the green rectangle to be already present, and just offscreen. When showgreen toggles, you need to shrink the size of the blue rectangle, which will make room for the green rectangle.
struct ContentView: View {
#State var showgreen = false
var body: some View {
NavigationView {
HStack {
Rectangle()
.foregroundColor(.blue)
.frame(width: showgreen ? UIScreen.main.bounds.width / 2 : UIScreen.main.bounds.width)
.animation(.easeInOut)
Rectangle()
.foregroundColor(.green)
.animation(.easeInOut)
}
.navigationBarItems(trailing:
Button(action: { self.showgreen.toggle() }) {
Image(systemName: "ellipsis")
})
}
.navigationViewStyle(StackNavigationViewStyle())
}
}