防止列表中的最后一行被 ZStack 中的其他视图覆盖

Prevent last rows in List to be covered by other view in ZStack

我有一个 List 部分被半透明视图覆盖(我们称它为 overlay)。我的问题是,对于长列表,最后一行无法访问,因为它们被覆盖层覆盖了。

我正在使用 ZStack 布局最终视图。我考虑过在最后几行添加某种 填充 可以使列表内容更大一些,这样它就可以完全滚动到覆盖层之外,但我不知道如何,或者即使使用 ZStack 是处理列表的正确方法。

import SwiftUI

struct ListWithBottomOverlay: View {
  var body: some View {
    GeometryReader { proxy in
      ZStack {
        List {
          ForEach(1..<20) { number in
            Text("\(number)")
          }
        }

        VStack {
          Spacer().frame(maxHeight: .infinity)

          VStack {
            HStack {
              Button(action: {}, label: { Text("Hello") })
                .frame(minHeight: 100)
            }
            HStack {
              Button(action: {}, label: { Text("World!") })
                .frame(minHeight: 100)
            }
          }
          .frame(maxWidth: .infinity)
          .background(Color(.yellow).opacity(0.8))
        }
      }
    }
  }
}

struct ListWithBottomOverlay_Previews: PreviewProvider {
  static var previews: some View {
    ListWithBottomOverlay()
  }
}

如果这是一个重复的问题,我深表歉意,我刚刚开始学习 SwiftUI,所以我对如何搜索正确的术语有点迷茫。

可能的解决方案是计算覆盖区域的高度,并在列表底部添加一些具有该高度的透明视图。

这是使用视图首选项的方法演示。用 Xcode 12 / iOS 14

测试

struct ListWithBottomOverlay: View {
  @State private var height = CGFloat.zero
  var body: some View {
    GeometryReader { proxy in
      ZStack {
        List {
          ForEach(1..<20) { number in
            Text("\(number)")
          }
          Color.clear.frame(height: height)  // injected empty space
        }

        VStack {
          Spacer().frame(maxHeight: .infinity)

          VStack {
            HStack {
              Button(action: {}, label: { Text("Hello") })
                .frame(minHeight: 100)
            }
            HStack {
              Button(action: {}, label: { Text("World!") })
                .frame(minHeight: 100)
            }
          }
          .frame(maxWidth: .infinity)
          .background(GeometryReader {
                // use color filled area in background to read covered frame
                Color(.yellow).opacity(0.8)
                    .edgesIgnoringSafeArea(.bottom)
                    .preference(key: ViewHeightKey.self, value: [=10=].frame(in: .local).size.height)
            })
        }
      }
      .onPreferenceChange(ViewHeightKey.self) {
        // view preferences transferred in one rendering cycle and
        // give possibility to update state
        self.height = [=10=]
      }
    }
  }
}

struct ViewHeightKey: PreferenceKey {
    typealias Value = CGFloat
    static var defaultValue = CGFloat.zero
    static func reduce(value: inout Value, nextValue: () -> Value) {
        value += nextValue()
    }
}