繁体   English   中英

SwiftUI | GeometryReader:滚动列表时平滑可调整大小的标题

[英]SwiftUI | GeometryReader: Smooth resizable Header when scrolling through List

我是 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)
    }
}

不是一个完美的解决方案,但您可以在ZStack中将 header 和List分成 2 层:

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。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM