I am using scrollview for my topCardView and bottomView. When I try to scroll everytime it gives me an error of invalid frame dimensions or sometimes it get freeze. Bound preference SizePreferenceKey tried to update multiple times per frame.
How can I fix this? Any help I really appreciate.
My completed code:
import SwiftUI
struct ContentView: View {
var body: some View {
ScrollView(.vertical, showsIndicators: false, content: {
GeometryReader { reader in
topCardView
.offset(y: -reader.frame(in: .global).minY)
.frame(width: reader.size.width, height: reader.frame(in: .global).minY + 450)
}
.frame(height: 450)
VStack(spacing: 40) {
BottomView
}
.padding(.vertical, 32)
.padding(.horizontal, 16)
.background(
Color(red: 232/255, green: 230/255, blue: 225/255)
.cornerRadius(40)
)
.offset(y: -25)
})
}
@ViewBuilder
private var topCardView: some View {
ScrollView(.horizontal, showsIndicators: false) {
LazyHGrid(rows: Array(repeating: GridItem(), count: 1), spacing: 20) {
ForEach(0..<2) { _ in
RoundedRectangle(cornerRadius: 12)
.fill(Color(red: 248/255, green: 250/255, blue: 220/255))
.frame(width: 300, height: 377)
}
}
.padding(.horizontal, 20)
}
}
@ViewBuilder
private var BottomView: some View {
ForEach(0..<2) { _ in
VStack(alignment: .center) {
Text("Fastest Near You")
.bold()
.frame(width: UIScreen.main.bounds.width - 32 * 6, height: 35)
Text("Order your food here")
.frame(width: UIScreen.main.bounds.width - 32 * 4, height: 25)
LazyVGrid(columns: Array(repeating: GridItem(), count: 2), spacing: 12) {
ForEach(0..<4) { _ in
RoundedRectangle(cornerRadius: 12)
.fill(Color(red: 248/255, green: 250/255, blue: 220/255))
.frame(width: UIScreen.main.bounds.width / 2 - 24, height: 200)
}
}
.padding(.top, 20)
}
}
}
}
Error Image:
I was asking in the comments, why you were setting a dynamic frame height at all. You replied to say, you want to create a parallax effect.
I think this can be achieved without using a dynamic frame height:
GeometryReader
).frame(in: .scrollView)
to reference the coordinate space of the ScrollView
ScrollView
insteadVStack
that contains them bothzIndex
to the bottom view, just to be quite sure that it is always shown above the top viewGeometryReader
can be used to measure the size of the screen, instead of using UIScreen.main
(which is deprecated).So maybe try something like this:
struct ContentView: View {
var body: some View {
GeometryReader { outer in
let screenSize = outer.size
ScrollView(.vertical, showsIndicators: false) {
VStack(spacing: 40) {
GeometryReader { reader in
let minY = reader.frame(in: .scrollView).minY
topCardView
.frame(maxHeight: .infinity)
.offset(y: -minY / 2)
}
.frame(height: screenSize.height / 2)
VStack(spacing: 40) {
bottomView(screenSize: screenSize)
}
.padding(.horizontal, 16)
.padding(.bottom, 40)
.background(
RoundedRectangle(cornerRadius: 40)
.fill(Color(red: 232/255, green: 230/255, blue: 225/255))
)
.zIndex(1)
}
}
}
}
@ViewBuilder
private var topCardView: some View {
// content as before
}
@ViewBuilder
private func bottomView(screenSize: CGSize) -> some View {
// content as before, but using screenSize instead of UIScreen.main.bounds
}
}