I've got a simple view:
var body: some View { NavigationView { ScrollView { VStack { ForEach(0..<2) { _ in CardVew(for: cardData) } } } .navigationBarTitle("Testing", displayMode: .automatic) } }
But you can replace the CardView with anything - the glitch presists. Glitch video
Is there a way to fix it?
Xcode 12.0.1, Swift 5
Setting the top padding to 1 is breaking at least 2 major things:
I've encountered these issues when i had to change the background color on all screens of the app i was working on. So i did a little bit more digging and experimenting and managed to figure out a pretty nice solution to the problem.
We wrap the ScrollView into 2 geometry readers.
The top one is respecting the safe area - we need this one in order to read the safe area insets The second is going full screen.
We put the scroll view into the second geometry reader - making it size to full screen.
Then we add the content using VStack, by applying safe area paddings.
At the end - we have scroll view that does not flicker and accepts background without breaking the large title of the navigation bar.
struct ContentView: View { var body: some View { NavigationView { GeometryReader { geometryWithSafeArea in GeometryReader { geometry in ScrollView { VStack { Color.red.frame(width: 100, height: 100, alignment: .center) ForEach(0..<5) { i in Text("\(i)") .frame(maxWidth: .infinity) .background(Color.green) Spacer() } Color.red.frame(width: 100, height: 100, alignment: .center) } .padding(.top, geometryWithSafeArea.safeAreaInsets.top) .padding(.bottom, geometryWithSafeArea.safeAreaInsets.bottom) .padding(.leading, geometryWithSafeArea.safeAreaInsets.leading) .padding(.trailing, geometryWithSafeArea.safeAreaInsets.trailing) } .background(Color.yellow) } .edgesIgnoringSafeArea(.all) } .navigationBarTitle(Text("Example")) } } }
Since the solution is clear now - lets create an elegant solution that can be reused and applied to any existing ScrollView by just replacing the padding fix.
We create an extension of ScrollView that declares the fixFlickering
function.
The logic is basically we wrap the receiver into the geometry readers and wrap its content into the VStack with the safe area paddings - that's it.
The ScrollView is used, because the compiler incorrectly infers the Content of the nested scroll view as should being the same as the receiver. Declaring AnyView explicitly will make it accept the wrapped content.
There are 2 overloads:
.padding(.top, 1)
with .fixFlickering()
- thats it.extension ScrollView { public func fixFlickering() -> some View { return self.fixFlickering { (scrollView) in return scrollView } } public func fixFlickering<T: View>(@ViewBuilder configurator: @escaping (ScrollView<AnyView>) -> T) -> some View { GeometryReader { geometryWithSafeArea in GeometryReader { geometry in configurator( ScrollView<AnyView>(self.axes, showsIndicators: self.showsIndicators) { AnyView( VStack { self.content } .padding(.top, geometryWithSafeArea.safeAreaInsets.top) .padding(.bottom, geometryWithSafeArea.safeAreaInsets.bottom) .padding(.leading, geometryWithSafeArea.safeAreaInsets.leading) .padding(.trailing, geometryWithSafeArea.safeAreaInsets.trailing) ) } ) } .edgesIgnoringSafeArea(.all) } } }
struct ContentView: View { var body: some View { NavigationView { ScrollView { VStack { Color.red.frame(width: 100, height: 100, alignment: .center) ForEach(0..<5) { i in Text("\(i)") .frame(maxWidth: .infinity) .background(Color.green) Spacer() } Color.red.frame(width: 100, height: 100, alignment: .center) } } .fixFlickering { scrollView in scrollView .background(Color.yellow) } .navigationBarTitle(Text("Example")) } } }
struct ContentView: View { var body: some View { NavigationView { ScrollView { VStack { Color.red.frame(width: 100, height: 100, alignment: .center) ForEach(0..<5) { i in Text("\(i)") .frame(maxWidth: .infinity) .background(Color.green) Spacer() } Color.red.frame(width: 100, height: 100, alignment: .center) } } .fixFlickering() .navigationBarTitle(Text("Example")) } } }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With