打印出数值是好的,但能够在父视图(或其他地方)内部使用它们更好。因此,我进一步详细说明了它。
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
作为一个非常好的虚拟视图,因为它可以扩展到填充所有空间,并且在语法上看起来很不错 :) - ahezestruct 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)
吗?我尝试的所有方法都会出现语法错误。 - Enrico您可以使用 PreferenceKey
来实现这个功能。
struct HeightPreferenceKey : PreferenceKey {
static var defaultValue: CGFloat = 0
static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {}
}
struct WidthPreferenceKey : PreferenceKey {
static var defaultValue: CGFloat = 0
static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {}
}
struct SizePreferenceKey : PreferenceKey {
static var defaultValue: CGSize = .zero
static func reduce(value: inout CGSize, nextValue: () -> CGSize) {}
}
extension View {
func readWidth() -> some View {
background(GeometryReader {
Color.clear.preference(key: WidthPreferenceKey.self, value: $0.size.width)
})
}
func readHeight() -> some View {
background(GeometryReader {
Color.clear.preference(key: HeightPreferenceKey.self, value: $0.size.height)
})
}
func onWidthChange(perform action: @escaping (CGFloat) -> Void) -> some View {
onPreferenceChange(WidthPreferenceKey.self) { width in
action(width)
}
}
func onHeightChange(perform action: @escaping (CGFloat) -> Void) -> some View {
onPreferenceChange(HeightPreferenceKey.self) { height in
action(height)
}
}
func readSize() -> some View {
background(GeometryReader {
Color.clear.preference(key: SizePreferenceKey.self, value: $0.size)
})
}
func onSizeChange(perform action: @escaping (CGSize) -> Void) -> some View {
onPreferenceChange(SizePreferenceKey.self) { size in
action(size)
}
}
}
使用方法如下:
struct MyView: View {
@State private var height: CGFloat = 0
var body: some View {
...
.readHeight()
.onHeightChange {
height = $0
}
}
}
这是我想出来的一种不太优美的方法来实现这个目标:
最初的回答:
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 mayoffmakeViewAndPrint
方法不被调用。 - Russian对于想要从Jack的解决方案中获取大小并将其存储在某个属性中以供进一步使用的人:
.overlay(
GeometryReader { proxy in
Text(String())
.onAppear() {
// Property, eg
// @State private var viewSizeProperty = CGSize.zero
viewSizeProperty = proxy.size
}
.opacity(.zero)
}
)
GeometryReader
和自定义的PreferenceKey
是最好的方法。我已经实现了一个辅助库,可以轻松完成这个任务:https://github.com/srgtuszy/measure-size-swiftui。View
,有两种不同的方式可以给你提供大小(高度和宽度)。
public extension View {
/// Determine size of a given view.
/// - Parameters:
/// - callBack: Block called after the size has been measured
func calculateFrame(_ callBack: @escaping (CGSize) -> Void) -> some View {
overlay (
GeometryReader { geo in
Color.clear.onAppear{ ///Used clear color to create a dummy view
callBack(geo.frame(in: .local).size)
}
}
)
}///Func ends
}///Extension ends
private struct SizePreferenceKey: PreferenceKey {
static var defaultValue: CGSize = .zero
static func reduce(value: inout Value, nextValue: () -> Value) { }
}
public extension View {
/// - Parameters:
/// - callBack: Block called after the size has been measured
func calculateFrame2(_ callBack: @escaping (CGSize) -> Void) -> some View {
background(
GeometryReader {
Color.clear.preference(key: SizePreferenceKey.self,
value: $0.frame(in: .local).size)
}
)
.onPreferenceChange(SizePreferenceKey.self) { callBack($0) }
}///Func ends
}
struct ContentView: View {
@State private var labelWidth: CGFloat = 0
@State private var labelHeight: CGFloat = 0
var body: some View {
VStack {
Text("Hello World")
.calculateFrame { ///<---- Here you will get size
labelWidth = $0.width
labelHeight = $0.height
}
.padding()
Text("Text Width - \(labelWidth), \nText Height - \(labelHeight)")
}
}
}
padding()
、Spacer()
、Divider()
和当然,frame()
。因此,在以声明的方式(将其余部分留给适用于所有大小的渲染引擎)表达时,你为什么要关心呢? - user7014451