Swiftui 用于计算视图总高度的GeometryReader

Swiftui 用于计算视图总高度的GeometryReader,swiftui,geometryreader,Swiftui,Geometryreader,我有一个固定宽度和高度的主视图,因为此视图将转换为PDF。接下来,我将在主视图中垂直堆叠一系列子视图。可能有更多的子视图可用,无法容纳在主视图中,因此我需要一种方法来确保我不超过主视图的总高度。例如,如果我的数组中有八个子视图,但只有三个子视图适合主视图,那么我需要停止在三个子视图之后添加子视图。其余的子视图将在新的主视图上重新开始该过程 我的问题是,如果我使用GeometryReader获取视图的高度,首先我必须添加它。但一旦添加视图,就太晚了,无法确定它是否超过了可用的总高度 下面显示了如何

我有一个固定宽度和高度的主视图,因为此视图将转换为PDF。接下来,我将在主视图中垂直堆叠一系列子视图。可能有更多的子视图可用,无法容纳在主视图中,因此我需要一种方法来确保我不超过主视图的总高度。例如,如果我的数组中有八个子视图,但只有三个子视图适合主视图,那么我需要停止在三个子视图之后添加子视图。其余的子视图将在新的主视图上重新开始该过程

我的问题是,如果我使用GeometryReader获取视图的高度,首先我必须添加它。但一旦添加视图,就太晚了,无法确定它是否超过了可用的总高度

下面显示了如何在添加每个视图时获取其高度。我知道,这没什么好谈的,但我被卡住了

更新: 我目前的策略是创建一个临时视图,在其中我可以添加子视图并返回一个只包含适合的子视图的数组

struct PDFView: View {
    var body: some View {
       VStack {
         ForEach(tasks) { task in
            TaskRowView(task: task)
              .overlay(
                 GeometryReader { geo in
                    // geo.size.height - to get height of current view
                 })
         }
       }
       .layoutPriority(1)
   }
}

您可以在一个位置获得总高度,如下所示:

   VStack {
     ForEach(tasks) { task in
        TaskRowView(task: task)
     }
   }
  .overlay(      
     GeometryReader { geo in
        // geo.size.height // << move it here and get total height at once
     })
VStack{
ForEach(tasks){task in
TaskRowView(任务:任务)
}
}
.叠加(
GeometryReader{geo-in

//geo.size.height/一种可能的解决方案是使用视图首选项

您可以计算项目的总高度,并在
onPreferenceChange
中将其
计数增加1(直到
totalHeight
达到
maxHeight


以下是完整的实施:

  • 创建用于检索视图高度的自定义
    PreferenceKey
  • 创建一个
    TaskRowView
    (任意高度):
  • 在视图中使用自定义
    首选项键
  • struct ContentView:View{
    @国家私有变量计数=0
    @国家私有变量totalHeight:CGFloat=0
    @状态私有变量maxheightreach=false
    设maxHeight:CGFloat=300
    var body:一些观点{
    VStack{
    文本(“总高度:\(总高度)”)
    VStack{
    ForEach(0..<计数,id:\.self){
    TaskRowView(索引:$0)
    }
    }
    .background(ViewGeometry())
    .onPreferenceChange(ViewHeightKey.self){
    总高度=$0
    打印(计数、总高度)
    守卫!MaxheightElse{return}
    如果$0<最大高度{
    计数+=1
    }否则{
    计数-=1
    maxheightreach=true
    }
    }
    }
    }
    }
    
    好的,使用您的解决方案,我可以得到主视图的总高度。使用我提供的代码,我可以得到每个子视图的高度。目标是只包含将适合主视图的子视图。那么,一旦达到限制,我如何停止添加视图?您所说的适合主视图是什么意思?在SwiftUI中er视图和它的内容一样大,所以你的所有子视图*都适合主视图。你是对的。但是,在我的例子中,高度是固定的,所以只有一定数量的子视图适合。我添加了一个图像来帮助澄清我的问题。谢谢!如果你已经定义了高度,那么你不需要计算任何东西,只需将高度应用到堆栈和剪辑内部,like
    VStack{}.frame(高度:792).clipped()
    -这将为您提供您所描述的内容。这可能会起作用,但当剪辑发生时,我如何知道我在阵列中的位置?保持运行总计的目的是为了将剩余的子视图添加到第二个主视图。顺便说一句,谢谢您-我感谢您的坚持。我不完全理解您的解决方案JU现在还没有,但它看起来很有前途。@squarehippo10这里有一个关于视图首选项的非常好的教程:
    struct ViewHeightKey: PreferenceKey {
        static var defaultValue: CGFloat = 0
    
        static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
            value += nextValue()
        }
    }
    
    struct ViewGeometry: View {
        var body: some View {
            GeometryReader { geometry in
                Color.clear
                    .preference(key: ViewHeightKey.self, value: geometry.size.height)
            }
        }
    }
    
    struct TaskRowView: View {
        let index: Int
    
        var body: some View {
            Text("\(index)")
                .padding()
                .background(Color.red)
        }
    }
    
    struct ContentView: View {
        @State private var count = 0
        @State private var totalHeight: CGFloat = 0
        @State private var maxHeightReached = false
        let maxHeight: CGFloat = 300
    
        var body: some View {
            VStack {
                Text("Total height: \(totalHeight)")
                VStack {
                    ForEach(0 ..< count, id: \.self) {
                        TaskRowView(index: $0)
                    }
                }
                .background(ViewGeometry())
                .onPreferenceChange(ViewHeightKey.self) {
                    totalHeight = $0
                    print(count, totalHeight)
                    guard !maxHeightReached else { return }
                    if $0 < maxHeight {
                        count += 1
                    } else {
                        count -= 1
                        maxHeightReached = true
                    }
                }
            }
        }
    }