【问题标题】:Measure the rendered size of a SwiftUI view?测量 SwiftUI 视图的渲染大小?
【发布时间】:2019-10-29 10:00:01
【问题描述】:

在 SwiftUI 运行视图渲染阶段后,有没有办法测量计算出的视图大小?例如,给定以下视图:

struct Foo : View {
    var body: some View {
        Text("Hello World!")
            .font(.title)
            .foregroundColor(.white)
            .padding()
            .background(Color.red)
    }
}

选择视图后,计算出的尺寸会显示在左下角的预览画布中。有谁知道在代码中访问该大小的方法?

【问题讨论】:

  • 对我来说,SwiftUI 的意图是不关心任何东西的大小。也就是说,除非你这样做——在这种情况下,你声明它。是的,范式转变。但是在这种情况下,你为什么要关心帧大小是多少? (我假设你的意思。我猜苹果实验室的专家也会问你同样的问题。你有padding()Spacer()、Divider()`,当然还有frame(),当你需要声明尺寸或添加间距。所以说declaritvely(将其余部分留给适用于所有尺寸的渲染引擎)你为什么要关心?
  • @dfd 在显示范围内随机定位是我的^^
  • 这篇由 SwiftUI Lab 的 Javier 撰写的三篇博文帮助我解决了这个问题。我强烈建议任何有兴趣的人详细阅读它。 - swiftui-lab.com/communicating-with-the-view-tree-part-1 - swiftui-lab.com/communicating-with-the-view-tree-part-2 - swiftui-lab.com/communicating-with-the-view-tree-part-3
  • @dfd 例如,当您桥接到 UIKit 时:UITableView 需要大小来配置表格视图单元格的高度。

标签: swiftui


【解决方案1】:

打印出值很好,但能够在父视图(或其他地方)中使用它们更好。所以我又迈出了一步来详细说明。

struct GeometryGetter: View {
    @Binding var rect: CGRect

    var body: some View {
        GeometryReader { (g) -> Path in
            print("width: \(g.size.width), height: \(g.size.height)")
            DispatchQueue.main.async { // avoids warning: 'Modifying state during view update.' Doesn't look very reliable, but works.
                self.rect = g.frame(in: .global)
            }
            return Path() // could be some other dummy view
        }
    }
}

struct ContentView: View {
    @State private var rect1: CGRect = CGRect()
    var body: some View {
        HStack {
            // make to texts equal width, for example
            // this is not a good way to achieve this, just for demo
            Text("Long text").background(Color.blue).background(GeometryGetter(rect: $rect1))
            // You can then use rect in other places of your view:
            Text("text").frame(width: rect1.width, height: rect1.height).background(Color.green)
            Text("text").background(Color.yellow)
        }
    }
}

【讨论】:

  • Color.clear 是一个非常好的虚拟视图,因为它会扩展以填充所有空间并且在语法上看起来不错:)
【解决方案2】:

您可以使用 GeometryReader 添加“叠加层”来查看值。但在实践中,最好使用“背景”修饰符并离散处理大小值

struct Foo : View {
    var body: some View {
        Text("Hello World!")
            .font(.title)
            .foregroundColor(.white)
            .padding()
            .background(Color.red)
            .overlay(
                GeometryReader { proxy in
                    Text("\(proxy.size.width) x \(proxy.size.height)")
                }
            )
    }
}

【讨论】:

  • 我喜欢这种方式。您能否提示如何将此值放入变量或.frame(height: xx) ?我尝试的所有方法都会出现语法错误。
  • 因此,值的存储可能不是您想要的,因为 SwiftUI 是一种声明性语言,状态通常在视图之外进行管理。但是您可以通过使用首选项在视图层次结构中向上传达子视图值。本质上,在 GeometryReader 中,您会将代理值传递给父视图可以观察到的 PreferenceKey。这是一个很好的网站,演示了如何使用 SwiftUI 首选项swiftwithmajid.com/2020/01/15/…
  • 所以在重新阅读 @Enrico 的评论时,我看到您想使用 Geometry Readers 的代理创建一个框架。这可能不是必需的,因为 GeometryReader 将占用尽可能多的空间。但是您可以在 GeometryReader 的任何子项中使用 proxy
【解决方案3】:

这是我想出的实现这一目标的丑陋方式:

struct GeometryPrintingView: View {

    var body: some View {
        GeometryReader { geometry in
            return self.makeViewAndPrint(geometry: geometry)
        }
    }

    func makeViewAndPrint(geometry: GeometryProxy) -> Text {
        print(geometry.size)
        return Text("")
    }
}

并更新Foo版本:

struct Foo : View {
    var body: some View {
        Text("Hello World!")
            .font(.title)
            .foregroundColor(.white)
            .padding()
            .background(Color.red)
            .overlay(GeometryPrintingView())
    }
}

【讨论】:

  • 也许EmptyView()Text("") 更合适。
  • @rob mayoff 我也这么认为,但由于某种原因,使用 EmptyView() 使得 makeViewAndPrint 方法不会被调用
猜你喜欢
  • 2020-12-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-25
  • 1970-01-01
  • 1970-01-01
  • 2021-07-24
  • 1970-01-01
相关资源
最近更新 更多