try 使用safeAreaInset修改器将页眉固定在导航栏下方.当我开始向下滑动导航栏时,safeAreaInset个内容保持不变,尽管导航标题和List个内容向下移动.

可以使标题与导航栏一起滚动吗?

struct ContentView: View {
  
  var body: some View {
    NavigationStack {
      List([0, 1, 2, 3], id: \.self) {
        Text("\($0)")
      }
      .navigationTitle("Navigation Title")
      .safeAreaInset(edge: .top, alignment: .leading) {
        Text("Safe Area Header")
          .foregroundStyle(Color.blue)
          .font(.title)
          .padding()
      }
    }
  }
}

enter image description here

推荐答案

我没有找到开箱即用的SwiftUI解决方案.但我能够通过观察List偏移量,然后计算正确的标题插入(firstRowInset - safeAreaTopInset)来解决这个问题.

struct ContentView: View {
  
  @State private var scrollOffset = 0.0
  
  var body: some View {
    NavigationStack {
      ItemsList(items: [0, 1, 2, 3])
        .safeAreaInset(edge: .top, alignment: .leading) {
          Text("Safe Area Header")
            .foregroundStyle(Color.blue)
            .font(.title)
            .padding()
            .offset(y: max(scrollOffset, 0.0))
        }
        .navigationTitle("Navigation Title")
        .onPreferenceChange(
          ScrollOffsetPreferenceKey.self,
          perform: { scrollOffset = $0 }
        )
    }
  }
  
  private struct ScrollOffsetPreferenceKey: PreferenceKey {
    static var defaultValue: CGFloat = 0
    
    static func reduce(
      value: inout CGFloat,
      nextValue: () -> CGFloat
    ) {
      value += nextValue()
    }
  }
  
  private struct ItemsList: View {
    let items: [Int]
    
    var body: some View {
      GeometryReader { geometry in
        List(items, id: \.self) { item in
          Row(value: item)
            .background {
              if item == items.first {
                ScrollOffsetPreferenceView(
                  safeAreaInsets: geometry.safeAreaInsets
                )
              }
            }
        }
      }
    }
  }
  
  private struct Row: View {
    let value: Int
    
    var body: some View {
      Text("\(value)")
        .frame(
          maxWidth: .infinity,
          maxHeight: .infinity,
          alignment: .leading
        )
        .listRowInsets(EdgeInsets())
        .padding()
    }
  }
  
  private struct ScrollOffsetPreferenceView: View {
    let safeAreaInsets: EdgeInsets
    
    var body: some View {
      GeometryReader { geometry in
        Color.clear
          .preference(
            key: ScrollOffsetPreferenceKey.self,
            value: offset(geometry: geometry)
          )
      }
    }
    
    private func offset(geometry: GeometryProxy) -> CGFloat {
      let safeAreaTopInset = safeAreaInsets.top
      let rowOffset = geometry.frame(in: .global).origin.y
      return rowOffset - safeAreaTopInset
    }
  }
}

Swift相关问答推荐

如何在HStack中均匀分布视图?

是否可以循环遍历SWIFT子类并访问它们的静态变量S?

使用MKLocalSearch获取坐标

如何将函数参数(字符串文字)标记为可本地化?

NavigationLink和ObservableObject的动画片段

Swift图表';chartXVisibleDomain挂起或崩溃

使用UICollectionViewFlowLayout创建水平的UICollectionView,并同时将单元格对齐到左边和右边的能力

在SwiftUI中如何预览嵌套的ScrollView,以避免触发可刷新修饰符

如何使用 Swift 在非本地函数中切换()一个 Bool?

在 struct 中的序列和非序列泛型函数之间进行 Select

SwiftUI - Select 器没有 Select 值

从 Swift 列表中的行中检索值

有什么方法可以快速为泛型参数分配默认值?

Apple 的自然语言 API 返回意外结果

在 Swift 中强制崩溃的最简单方法

如何从一个可观察的数组创建一个可观察的数组?

使用 phimagemanager 将图像保存到自定义相册?

playground 导入:没有这样的模块Foo

Swift 5 秒后关闭 UIAlertView

在这个例子中美元符号有什么作用?