SwiftUI 下拉滑动 局部放大

TableView的头随着列表的滑动而拉伸,常规做法是给List做一个背景。再通过监听滚动视图的偏移量,从而通过拉伸背景从而实现拉伸的效果、

一、那么在SwiftUI中该如何监听滚动视图的偏移量?

这里需要了解三个概念:

  1. GeometryReader它允许我们根据父视图的大小和位置来动态调整子视图的布局和位置
  2. CoordinateSpace坐标系,它允许我们在不同坐标系中转化坐标;
  3. preference偏好,它允许我们跨视图传递信息;
  • GeometryReader的特性常常会使我们不能有效的获取到我们想要的坐标,它总是读取到最大的可持有空间。解决这个问题我们需要了解另一个概念background背景
  • background背景的最大空间就是当前的视图的空间,因而GeometryReader的最大空间就是当前视图的空间。

如此的话,我们可以通过读取背景的偏移量获取到滚动视图的偏移量,而又不影响原有的视图布局;

public struct ListScaleViewModifier: ViewModifier {
    @State private var headFirstHeight: CGFloat = 0
    @Binding var scaleHeight: CGFloat
    
    public init(scaleHeight: Binding<CGFloat>) {
        self._scaleHeight = scaleHeight
    }
    
    public func body(content: Content) -> some View {
        content.background(GeometryReader { geometry in
            Color.clear
                .preference(key: ScaleKey.self, value: ScaleKey.Offset(minY: geometry.frame(in: .global).minY, height: geometry.frame(in: .local).height))
                .onPreferenceChange(ScaleKey.self, perform: { value in
                    if headFirstHeight == 0 {
                        headFirstHeight = geometry.frame(in: .global).minY
                    }
                    scaleHeight = max(0, value.minY - headFirstHeight + value.height)
                })
        })
    }
}

利用偏好获取到滚动偏移量,从而计算出列表头放大或缩小后的高度

fileprivate extension ListScaleViewModifier {
    struct ScaleKey: PreferenceKey {
        struct Offset: Equatable {
            let minY: CGFloat
            let height: CGFloat
            
            static let zero = Offset(minY: 0, height: 0)
        }
        
        static var defaultValue: Offset = .zero
        static func reduce(value: inout Offset, nextValue: () -> Offset) {
            value = nextValue()
        }
    }
}

二、动态改变背景高度

动态改变背景高度,实现头随着滚动视图的滚动而放大或者缩小。

struct ListScaleView: View {
    @State private var headheight: CGFloat = 0
 
    var body: some View {
        List {
            ListHeader()
                .modifier(ListScaleViewModifier(scaleHeight: $headheight))
                .listRowBackground(Color.clear)
            // 列表内容
            ForEach(0..<10, id: \.self) { index in
                Text("Item \(index)")
            }
            .listRowBackground(Color.clear)
        }
        .listStyle(.plain)
        .background(
            VStack(spacing: 0) {
                Color.red.frame(height: headheight)
                Spacer()
            }
        )
    }
}
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容