实现SwiftUI下带头部嵌套滚动的页面框架

2024-09-17  本文已影响0人  zackzheng

实现了SwiftUI下带头部嵌套滚动的页面框架,目前有两种实现方案:

  1. 监听页面手势,实时改变header的高度,手势停止时,修改header的高度为完全消失或完全出现,即自动吸附。
  2. 根据header的高度情况,设置页面是否可滚动。
struct HeaderBarPagerView<Header: View, Tab: Hashable, TabItem: View, TabPage: View>: View {

    /// 参数
    var header: () -> Header

    let tabList: [Tab]
    @Binding var selectedTab: Tab
    let tabItemHeight: CGFloat
    let tabItem: (_ tab: Tab, _ isSelected: Bool) -> TabItem
    let showSlider: Bool
    let tabEdgeInsets: EdgeInsets
    let tabPage: (_ tab: Tab, _ isSelected: Bool, _ didScroll: ((CGFloat) -> Void)?) -> TabPage
    
    /// 其他
    @State private var headerSize: CGSize = .zero
    
    /// 手势
    @GestureState private var dragValue: CGSize = .zero
    @State private var preDragValue = CGSize.zero

    var body: some View {

        VStack(spacing: 0) {
            
            ZStack(alignment: .bottom) {
                ChildSizeReader(size: $headerSize) {
                    self.header()
                }
            }
            .frame(height: min(max(headerSize.height + preDragValue.height + dragValue.height, 0), headerSize.height))

            BarPagerView(tabList: tabList,
                         selectedTab: $selectedTab,
                         tabItemHeight: tabItemHeight,
                         tabItem: tabItem,
                         showSlider: showSlider,
                         tabEdgeInsets: tabEdgeInsets,
                         tabPage: { tab, isSelected in

                tabPage(tab, isSelected, { offset in
                    
                    if offset < 0 {
                        withAnimation {
                            self.preDragValue.height = 0
                        }
                    }
                })
                .scrollDisabled(headerSize.height + dragValue.height + preDragValue.height > 0)
            })
        }
        .gesture(
            
            DragGesture()
                .updating($dragValue, body: { value, state, _ in
                    state = value.translation
                })
                .onEnded { value in
                    
                    let newDragHeight = self.preDragValue.height + value.translation.height
                    let oldDragHeight = self.preDragValue.height
                    self.preDragValue.height = newDragHeight
                    withAnimation {
                        self.preDragValue.height = newDragHeight > oldDragHeight ? 0 : -headerSize.height
                    }
                }
        )
    }
}
  1. 监听页面的滚动距离,回调页面框架。
  2. 页面框架根据滚动距离,协同调整header的高度
struct HeaderBarPagerSynchronousView<Header: View, Tab: Hashable, TabItem: View, TabPage: View>: View {

    /// 参数
    var header: () -> Header

    let tabList: [Tab]
    @Binding var selectedTab: Tab
    let tabItemHeight: CGFloat
    let tabItem: (_ tab: Tab, _ isSelected: Bool) -> TabItem
    let showSlider: Bool
    let tabEdgeInsets: EdgeInsets
    let tabPage: (_ tab: Tab, _ isSelected: Bool, _ didScroll: ((CGFloat) -> Void)?) -> TabPage
    
    /// 其他
    @State private var headerSize: CGSize = .zero

    @State private var preDragValue = CGSize.zero

    var body: some View {

        VStack(spacing: 0) {
            
            ZStack(alignment: .bottom) {
                ChildSizeReader(size: $headerSize) {
                    self.header()
                }
            }
            .frame(height: min(max(headerSize.height + preDragValue.height, 0), headerSize.height))

            BarPagerView(tabList: tabList,
                         selectedTab: $selectedTab,
                         tabItemHeight: tabItemHeight,
                         tabItem: tabItem,
                         showSlider: showSlider,
                         tabEdgeInsets: tabEdgeInsets,
                         tabPage: { tab, isSelected in

                tabPage(tab, isSelected, { offset in
                    self.preDragValue.height = -offset
                })
            })
        }
    }
}

本文 DEMO 链接:
https://github.com/sapphirezzz/ScrollWithHeader-SwiftUI-Demo

上一篇下一篇

猜你喜欢

热点阅读