如何根据滚动位置更改 ScrollTargetBehaviour?

问题描述 投票:0回答:1

我正在开发一款应用程序,在其余“探索”内容之前,我会先呈现一个或多个特色全屏视图(想想 Spotify 的主页)。

我目前有以下结构:

GeometryReader { proxy in
        let h = proxy.size.height
        
        ScrollView {
          if data == nil {
            Recommended(event: nil)
              .padding(.vertical, 8)
              .frame(height: h)
              .frame(maxWidth: .infinity)
          } else {
            LazyVStack(spacing: proxy.safeAreaInsets.bottom / 2) {
              ForEach(data?.recommended ?? []) { _ in
                Recommended(event: events[0])
                  .padding(.vertical, 8)
                  .frame(height: h)
                  .frame(maxWidth: .infinity)
                  .padding(.bottom, proxy.safeAreaInsets.bottom / 2)
              }
            }
            .scrollTargetLayout()
          }
          
          if let sections = data?.sections {
            LazyVStack(spacing: 32) {
              ForEach(Array(sections.enumerated()), id: \.offset) { e in
                let section = e.element
                  
                EventRow(title: section.title, events: section.events)
              }
            }
            .scrollTargetLayout()
          }
          
          Spacer()
        }
    //    .scrollPosition(id: $scrollID) this was an attempt
    //    .scrollTargetBehavior(.custom) this was another attempt
        .scrollDisabled(data == nil)
        .scrollIndicators(.hidden)
        .ignoresSafeArea(.container)
        .refreshable {
          print("refresh")
        }
        .onChange(of: scrollID) { _, newValue in
          print(newValue ?? "")
        }
      }

我尝试根据

scrollID
切换分页样式,但在编写三元后得到以下结果

.scrollTargetBehavior(true ? .paging : .viewAligned)

// Member 'viewAligned' in 'PagingScrollTargetBehavior' produces result of type 'ViewAlignedScrollTargetBehavior', but context expects 'PagingScrollTargetBehavior'

我也在研究滚动目标行为的自定义实现,但我的实验没有取得什么结果。

最小可重现示例

struct ContentView: View {
  let colors: [Color] = [.blue, .green, .yellow, .red]

  var body: some View {
    GeometryReader { proxy in
      ScrollView(.vertical) {
        LazyVStack(spacing: proxy.safeAreaInsets.bottom / 2) {
          ForEach(0 ..< 2) { i in
            ZStack {
              Rectangle()
                .fill(colors[i % colors.count].opacity(0.6))
                .containerRelativeFrame([.horizontal, .vertical])
                .padding(.bottom, proxy.safeAreaInsets.bottom / 2)
                .frame(
                  width: proxy.size.width,
                  height: proxy.size.height + (proxy.safeAreaInsets.bottom / 2)
                )
              Text("Video \(i + 1)")
                .font(.title)
                .bold()
            }
          }
        }
        .scrollTargetLayout()
        
        // these elements should scroll normally without snapping
        ForEach(0 ..< 20) { i in
          Rectangle()
            .fill(.purple)
            .frame(width: 100, height: 100)
            .overlay {
              Text("\(i)")
            }
        }
      }
      .scrollTargetBehavior(.paging)
      .ignoresSafeArea(edges: .bottom)
    }
  }
}

#Preview {
  ContentView()
}
swiftui scrollview
1个回答
0
投票

您可以编写自己的

ScrollTargetBehaviour
。它的
updateTarget
将检查滚动视图自然滚动到的位置,如果它仍在“分页区域”中,则委托给
PagingScrollTargetBehavior
。否则它什么也不做,即自然滚动。

struct PartlyPagingBehaviour: ScrollTargetBehavior {
    let pageCount: Int
    func updateTarget(_ target: inout ScrollTarget, context: TargetContext) {
        let targetDimension = context.axes == .vertical ? target.rect.maxY : target.rect.maxX
        let containerDimension = context.axes == .vertical ? context.containerSize.height : context.containerSize.width
        if targetDimension < containerDimension * CGFloat(pageCount) {
            PagingScrollTargetBehavior.paging.updateTarget(&target, context: context)
        }
    }
}

通过检查

target.rect.maxY
,如果非分页区域的任何部分可见,则此实现将使用自然行为。如果您希望在自然行为开始之前分页区域完全不可见,您可以选中
target.rect.minY

对于 MRE 中的示例,您可以使用

.scrollTargetBehavior(PartlyPagingBehaviour(pageCount: 2))

也就是说,捕捉到每个页面比实际的

.paging
行为要慢一些。我认为
PagingScrollTargetBehavior
是 SwiftUI 检查的特殊情况,并且处理方式有点不同。

最新问题
© www.soinside.com 2019 - 2024. All rights reserved.