SwiftUI 中带有 NavigationView 的 GeometryReader 最初给出的大小为 .zero

问题描述 投票:0回答:2

我在 NavigationView 中有一个 GeometryReader,当视图首次显示时,最初大小为 0。我不确定这是一个错误还是正确的行为,但我正在寻找一种方法来解决这个问题,因为我的子视图无法正确渲染。

这个结构演示了这个问题。

下面的打印输出是:(0.0, 0.0) 大小。

是否有办法强制 NavigationView 在最初显示时提供正确的几何形状?

struct ContentView: View {
    var body: some View {
        NavigationView {
            GeometryReader { geometry in
                Text("Geometry Size Is Wrong")
                    .onAppear {
                        print(geometry.size)  // prints out (0.0, 0.0)
                    }
            }
        }
    }
}
swift xcode swiftui navigationview geometryreader
2个回答
3
投票

不幸的是,我认为您无法做任何事情来使 NavigationView 在最初显示时提供正确的几何形状。

但是,如果您确实想从您的视图中访问最终的

geometry.size
,您可以按照新开发人员的建议使用
onChange(of:)

struct ContentView: View {
  @State var currentSize: CGSize?

  var body: some View {
    NavigationView {
      GeometryReader { geometry in
        Text("currentSize will soon be correct")
          .onChange(of: geometry.size) { newSize in
            currentSize = newSize
            print(currentSize!) // prints (320.0, 457.0)
          }
      }
    }
  }
}

上面的方法在许多情况下都可以正常工作,但请注意,从 GeometryReader 子视图中的 geometry.size 计算出的任何

local
变量在 onChange 块中将
不准确
(它将捕获原始的错误值):

struct ContentView: View {
  @State var currentSize: CGSize?
  @State var halfWidth: CGFloat?

  var body: some View {
    NavigationView {
      GeometryReader { geometry in
        let halfWidthLocal = geometry.size.width / 2

        Text("Half Width is really: \(halfWidthLocal)") // will read as "Half Width is really 160.000000"
          .onChange(of: geometry.size) { newSize in
            currentSize = newSize
            halfWidth = halfWidthLocal
            print(currentSize!) // prints (320.0, 457.0)
            print(halfWidth!) // prints 0.0
          }
      }
    }
  }
}

为了使用最新版本的局部变量更新状态属性,您可以更新返回 GeometryReader 中视图的函数内的属性:

struct ContentView: View {
  @State var currentSize: CGSize?
  @State var halfWidth: CGFloat?

  var body: some View {
    NavigationView {
      GeometryReader { geometry in
        let halfWidthLocal = geometry.size.width / 2

        makeText(halfWidthLocal: halfWidthLocal)
          .onChange(of: geometry.size) { newSize in
            currentSize = newSize
            print(currentSize!) // prints (320.0, 457.0)
          }
      }
    }
  }

  func makeText(halfWidthLocal: CGFloat) -> some View {
    DispatchQueue.main.async { // Must update state properties on the main queue
      halfWidth = halfWidthLocal
      print(halfWidth!) // prints 0.0 the first time, then 160.0 the second time
    }
    return Text("Half Width is really: \(halfWidthLocal)") // will read as "Half Width is really 160.000000"
  }
}

这种情况发生在我身上,所以我只是想将知识传授给其他人。


0
投票

导航堆栈内的视图最初以大小 0 渲染,然后立即以非零大小重新渲染。因为

.onAppear()
闭包只运行一次,所以你的代码只能看到零,而不是后续的实际大小值。在 SwiftUI 中,由于多种原因多次渲染视图是很常见的。

但是您可以等待在

GeometryReader
内渲染视图,直到几何图形非零。因此,在几何形状良好或至少非零(*)之前,您的
.onAppear()
根本不会运行。只需在
geometry.size != .zero

上绘制您的视图即可

对于您的示例代码,可能如下所示:

struct ContentView: View {
var body: some View {
    NavigationView {
        GeometryReader { geometry in

            if geometry.size != .zero {

                Text("Geometry Size Is \(geometry.size.width) x \(geometry.size.height)")
                    .onAppear {
                        print(geometry.size)  // prints out a nonzero size
                    }
                }

            }
        }
    }
}

(*) 警告:如果您向导航过渡添加动画,这可能不会/不会为您提供最终的几何图形,因为您的视图将以各种尺寸绘制许多次。

© www.soinside.com 2019 - 2024. All rights reserved.