GeometryReader 计算视图的总高度

Posted

技术标签:

【中文标题】GeometryReader 计算视图的总高度【英文标题】:GeometryReader to calculate total height of views 【发布时间】:2020-10-23 17:43:00 【问题描述】:

我有一个固定宽度和高度的主视图,因为该视图将被转换为 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)
   

【问题讨论】:

【参考方案1】:

一种可能的解决方案是使用查看首选项

您可以计算项目的总高度,并在onPreferenceChange 中将它们的count 增加1(直到totalHeight 达到maxHeight)。


这是完整的实现:

    创建自定义PreferenceKey 以检索视图高度:
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)
        
    

    创建一个TaskRowView(任意高度):
struct TaskRowView: View 
    let index: Int

    var body: some View 
        Text("\(index)")
            .padding()
            .background(Color.red)
    

    在您的视图中使用自定义PreferenceKey
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
                
            
        
    

【讨论】:

我还没有完全理解你的解决方案,但它看起来很有希望。 @squarehippo10 这是一个关于视图偏好的非常好的教程:Inspecting the View Tree – Part 1: PreferenceKey【参考方案2】:

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

   VStack 
     ForEach(tasks)  task in
        TaskRowView(task: task)
     
   
  .overlay(      
     GeometryReader  geo in
        // geo.size.height // << move it here and get total height at once
     )

【讨论】:

好的,所以通过您的解决方案,我可以获得主视图的总高度。通过我提供的代码,我可以得到每个子视图的高度。目标是仅包含适合主视图的子视图。那么,一旦达到限制,如何停止添加视图? 适合主视图是什么意思?在 SwiftUI 中,堆栈容器视图与其内容一样大,因此您的所有子视图*适合到主视图中。 你是对的。然而,在我的情况下,高度是固定的,所以只有一定数量的子视图适合。我添加了一张图片来帮助澄清我的问题。谢谢! 如果您已经定义了高度,那么您不需要计算任何东西,只需将高度应用于堆栈和剪辑内部,例如VStack.frame(height: 792).clipped() - 这将为您提供您所描述的内容。 这可以工作,但是当剪辑发生时我怎么知道我在数组中的哪个位置?保持运行总数的目的是使剩余的子视图可以添加到第二个主视图中。顺便说一句,谢谢你 - 我感谢你的坚持。

以上是关于GeometryReader 计算视图的总高度的主要内容,如果未能解决你的问题,请参考以下文章

SwiftUI - 如何从不同的视图中获取 GeometryReader 的大小/高度?

GeometryReader 大小计算 SwiftUI

SwiftUI之深入解析如何使用组合矩形GeometryReader创建条形(柱状)图

SwiftUI之深入解析如何使用组合矩形GeometryReader创建条形(柱状)图

如何在 SwiftUI 中根据其子级高度设置 GeometryReader 高度?

在 SwiftUI 中根据文本高度自动调整视图高度