【问题标题】:SwiftUI | GeometryReader: Smooth resizable Header when scrolling through ListSwiftUI | GeometryReader:滚动列表时平滑可调整大小的标题
【发布时间】:2021-07-02 00:11:26
【问题描述】:

我是 SwiftUI 的新手,我想从联系人应用程序重新创建联系人卡片视图。 在下面的列表中滚动时,我正在努力平滑地调整顶部图像的大小。

我曾尝试使用 GeometryReader,但在那里遇到了问题。 例如,当向上滚动时,图片大小会突然跳到我指定的 minimumPictureSize。向上滚动时会发生相反的情况:当我停止滚动时,它会突然停止调整大小。

想要的行为:https://gifyu.com/image/Ai04

当前行为:https://gifyu.com/image/AjIc

 struct SwiftUIView: View {
    @State var startOffset: CGFloat = 0
    @State var offset: CGFloat = 0
    
    var minPictureSize: CGFloat = 100
    var maxPictureSize: CGFloat = 200
    
    var body: some View {
        VStack {
            Image("person")
                .resizable()
                .frame(width: max(minPictureSize, min(maxPictureSize, minPictureSize + offset)),
                       height: max(minPictureSize, min(maxPictureSize, minPictureSize + offset)))
                .mask(Circle())
            Text("startOffset: \(startOffset)")
            Text("offset: \(offset)")
            List {
                Section {
                    Text("Top Section")
                }.overlay(
                    GeometryReader(){ geometry -> Color in
                        let rect = geometry.frame(in: .global)
                        
                        if startOffset == 0 {
                            DispatchQueue.main.async {
                                startOffset = rect.minY
                            }
                        }
                        DispatchQueue.main.async {
                            offset = rect.minY - startOffset
                        }
                        return Color.clear
                    }
                )
                ForEach((0..<10)) { row in
                    Section {
                        Text("\(row)")
                    }
                }
            }.listStyle(InsetGroupedListStyle())
        }.navigationBarHidden(true)
    }
}

【问题讨论】:

    标签: swift swiftui geometryreader


    【解决方案1】:

    不是一个完美的解决方案,但您可以将标题和 List 分成两层 ZStack:

    struct SwiftUIView: View {
        @State var startOffset: CGFloat!
        @State var offset: CGFloat = 0
        
        let minPictureSize: CGFloat = 100
        let maxPictureSize: CGFloat = 200
        
        var body: some View {
            ZStack(alignment: .top) {
                if startOffset != nil {
                    List {
                        Section {
                            Text("Top Section")
                        } header: {
                            // Leave extra space for `List` so it won't clip its content
                            Color.clear.frame(height: 100)
                        }
                        .overlay {
                            GeometryReader { geometry -> Color in
                                DispatchQueue.main.async {
                                    let frame = geometry.frame(in: .global)
                                    offset = frame.minY - startOffset
                                }
                                
                                return Color.clear
                            }
                        }
                        
                        ForEach((0..<10)) { row in
                            Section {
                                Text("\(row)")
                            }
                        }
                    }
                    .listStyle(InsetGroupedListStyle())
                    .padding(.top, startOffset-100)  // Make up extra space
                }
                
                VStack {
                    Circle().fill(.secondary)
                        .frame(width: max(minPictureSize, min(maxPictureSize, minPictureSize + offset)),
                               height: max(minPictureSize, min(maxPictureSize, minPictureSize + offset)))
                    Text("startOffset: \(startOffset ?? -1)")
                    Text("offset: \(offset)")
                }
                .frame(maxWidth: .infinity)
                .padding(.bottom, 20)
                .background(Color(uiColor: UIColor.systemBackground))
                .overlay {
                    if startOffset == nil {
                        GeometryReader { geometry -> Color in
                            DispatchQueue.main.async {
                                let frame = geometry.frame(in: .global)
                                
                                startOffset = frame.maxY +  // Original small one
                                    maxPictureSize - minPictureSize -
                                    frame.minY // Top safe area height
                            }
                            
                            return Color.clear
                        }
                    }
                }
                    
            }
            .navigationBarHidden(true)
        }
    }
    

    请注意,Color.clear.frame(height: 100) 和 .padding(.top, startOffset-100) 旨在为 List 留出额外的空间以避免被剪裁,这将导致滚动条被剪裁。或者,UIScrollView.appearance().clipsToBounds = true 也可以。但是,它会使移动到List 范围之外的元素消失。不知道是不是bug。

    【讨论】:

    • 不幸的是,您的很多代码都无法在我的项目中编译。我正在使用 Swift 5,并且我将 iOS 14.0 作为部署目标。
    • @mathisfoxius 很抱歉给您带来不便。这是iOS 14.0 version。 .fill()、Color(uiColor:)、header 代表 Section 并使用花括号代表 .overlay 造成了麻烦。
    • 谢谢,这可以编译:) 不幸的是,它并没有真正解决问题。在尝试您的代码时,标头有时会变小。目前我认为使用手势可能有助于解决问题
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-01-21
    • 2011-04-03
    • 1970-01-01
    • 2016-01-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多