SwiftUI:如何为 ScrollView 制作可拉伸(灵活)的粘性标题?

Asp*_*eri 3 scrollview ios swift swiftui

好吧,老实说,我做到了,因为我需要它,然后才环顾四周,没有在 SwiftUI 中找到任何原生的东西,所以想分享。因此,这只是一个自我回答的问题。

最初,我需要粘性可拉伸粘性标头,用于仅依赖于ScrollView.

后来(在我得到解决方案后)我在 Medium 上找到了这个,但我不喜欢它(并且至少不会推荐原样),因为:

  1. 过于复杂(许多不需要的代码,许多不需要的计算)
  2. 仅依赖(并连接)安全区域,因此适用性有限
  3. 基于offset(我不喜欢使用offset,因为它与布局不一致等)
  4. 它不具有粘性,要使其具有粘性,需要更多代码

所以,实际上所有这些文本只是为了满足所以问题的要求 - 谁认识我在这里知道我不喜欢输入很多文本,最好输入代码,简而言之 - 我的方法在下面的答案中,也许有人发现它很有用。

SwiftUI 免费为我们提供的初始代码

ScrollView {
    LazyVStack(spacing: 8, pinnedViews: [.sectionHeaders]) {
        Section {
            ForEach(0...100) {
                Text("Item \($0)")
                    .frame(maxWidth: .infinity, minHeight: 60)
            }
        } header: {
           Image("picture").resizable().scaledToFill()
               .frame(height: 200)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

标题在向上滚动时会粘住,但在向下滚动(随内容拖动)时不会粘住,并且不可拉伸。

Asp*_*eri 5

iOS 15.5(初始)

演示

好吧,我们需要解决两个问题:

  1. 使标题顶部固定到ScrollView向下拖动时的顶部
  2. 向下拖动时拉伸标题以使标题内容(大多数情况下是图像)缩放以填充

解决这个问题的可能方法:

  1. ScrollView现在私下管理内容偏移(UIKit 变体不在此处的主题范围内),因此可以使用覆盖层固定到顶部
    ScrollView {
        // ...
    }
    .overlay(
        // >> any header
        Image("picture").resizable().scaledToFill()
        // << header end
            .frame(height: imageHeight)  // will calculate below
            .clipped()
Run Code Online (Sandbox Code Playgroud)
  1. 使用Section默认标题(作为占位符)计算当前距ScrollView顶部的距离

     Section(...) {
       // ...
     } header: {
         // here is only caculable part
         GeometryReader {
             // detect current position of header bottom edge
             Color.clear.preference(key: ViewOffsetKey.self,
                 value: $0.frame(in: .named("area")).maxY)
         }
         .frame(height: headerHeight)
         .onPreferenceChange(ViewOffsetKey.self) {
             // prevent image negative height if header is not pinned 
             // for simplicity (can be optional, etc.)
             imageHeight = $0 < 0 ? 0.001 : $0
         }
     }
    
    Run Code Online (Sandbox Code Playgroud)

实际上就是这样,其他一切都只是演示部分。

使用 Xcode 13.4 / iOS 15.5 进行测试

测试模块在这里