ScrollView + NavigationView动画故障SwiftUI [英] ScrollView + NavigationView animation glitch SwiftUI

查看:60
本文介绍了ScrollView + NavigationView动画故障SwiftUI的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个简单的看法:

var body: some View {
        NavigationView {
            ScrollView {
                VStack {
                    ForEach(0..<2) { _ in
                        CardVew(for: cardData)
                    }
                }
            }
            .navigationBarTitle("Testing", displayMode: .automatic)
        }
    }

但是您可以用任何东西替换CardView-故障仍然存在.小故障视频

But you can replace the CardView with anything - the glitch presists. Glitch video

有办法解决吗?

Xcode 12.0.1,Swift 5

Xcode 12.0.1, Swift 5

推荐答案

将顶部填充设置为1会破坏至少两个主要方面:

Setting the top padding to 1 is breaking at least 2 major things:

  1. 滚动视图不会在NavigationView和TabView下扩展-从而使滚动条下方的内容失去了优美的模糊效果.
  2. 在滚动视图上设置背景将导致大标题NavigationView停止折叠.

当我不得不在正在处理的应用程序的所有屏幕上更改背景颜色时,我遇到了这些问题.因此,我做了一些进一步的挖掘和试验,并设法找到了解决该问题的不错的解决方案.

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.

我们将ScrollView包装到2个几何读取器中.

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.

然后,通过应用安全区域填充,使用VStack添加内容.

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"))
        }
    }
}

优雅的解决方案

由于该解决方案现在很明确-我们可以创建一个优雅的解决方案,只需替换填充修补程序即可将其重复使用并应用于任何现有ScrollView.

The elegant solution

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.

我们创建了ScrollView的扩展,该扩展声明了 fixFlickering 函数.

We create an extension of ScrollView that declares the fixFlickering function.

基本上,逻辑是我们将接收器包装到几何读取器中,并使用安全区域填充将其内容包装到VStack中.

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.

之所以使用ScrollView,是因为编译器错误地推断出嵌套滚动视图的内容,这与接收者应该是相同的.显式声明AnyView将使其接受包装的内容.

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.

有2个重载:

  • 第一个不接受任何参数,您可以在任何现有的滚动视图中调用它,例如.您可以将 .padding(.top,1)替换为 .fixFlickering()-就是这样.
  • 第二个接受配置程序闭包,该闭包用于为您提供设置嵌套滚动视图的机会.那是必需的,因为我们不使用接收器而只是包装它,而是创建一个新的ScrollView实例,并且仅使用接收器的配置和内容.在此关闭中,您可以按任何方式修改提供的ScrollView,例如.设置背景颜色.
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)
        }
    }
}

示例1

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"))
        }
    }
}

示例2

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"))
        }
    }
}

这篇关于ScrollView + NavigationView动画故障SwiftUI的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆