我有一个标题视图,它使用edgesIgnoringSafeArea
将其背景扩展到状态栏下。为了正确对齐标题视图的内容/子视图,我需要GeometryReader
中的safeAreaInsets
。然而,当使用GeometryReader
时,我的视图不再有合适的大小。
不使用GeometryReader
编码
struct MyView : View {
var body: some View {
VStack(alignment: .leading) {
CustomView()
}
.padding(.horizontal)
.padding(.bottom, 64)
.background(Color.blue)
}
}
预览
使用GeometryReader
编码
struct MyView : View {
var body: some View {
GeometryReader { geometry in
VStack(alignment: .leading) {
CustomView()
}
.padding(.horizontal)
.padding(.top, geometry.safeAreaInsets.top)
.padding(.bottom, 64)
.background(Color.blue)
.fixedSize()
}
}
}
预览
有没有一种方法可以在不修改底层视图大小的情况下使用GeometryReader
?
4条答案
按热度按时间jv4diomz1#
回答标题中的问题:
GeometryReader
Package 在.overlay()
或.background()
中。这样做将减轻GeometryReader的布局更改效果。视图将正常布局,GeometryReader将扩展到视图的完整大小,并将geometry
发送到其内容构建器闭包中。例如,此示例通过将GeometryReader包裹在覆盖层中来呈现一个蓝色矩形,并在矩形高度的3/4处呈现一个“Hello world”文本(而不是矩形填充所有可用空间):
另一个通过在GeometryReader上设置帧来实现相同效果的示例:
但是,有一些注意事项/不太明显的行为
1
视图修改器应用于应用之前的任何内容,而不应用于之后的任何内容。在
.edgesIgnoringSafeArea(.all)
之后添加的覆盖/背景将遵守安全区域(不参与忽略安全区域)。这段代码在安全区域内呈现“Hello world”,而蓝色矩形忽略安全区域:
2
将
.edgesIgnoringSafeArea(.all)
应用于背景会使GeometryReader忽略安全区域:可以通过添加多个覆盖/背景来组合许多布局。
三
测量的几何体将可用于GeometryReader的内容。不适用于父视图或兄弟视图;即使值被提取到State或ObservableObject中。如果发生这种情况,SwiftUI将发出运行时警告:
ctehm74n2#
我尝试了previewLayout,我明白你的意思。然而,我认为行为符合预期。.sizeThatFits的定义是:
当提供运行预览的设备(C)的大小时,使容器(A)适合预览(B)的大小。
我插入了一些字母来定义每个部分,使其更加清晰:
A =预览的最终大小。
B =您正在使用.previewLayout()修改的内容的大小。在第一种情况下,它是VStack。但在第二种情况下,它是GeometryReader。
C =设备屏幕的大小。
这两种观点的行为是不同的,因为VStack并不贪婪,只取它需要的东西。而GeometryReader则试图拥有一切,因为它不知道它的孩子想要使用什么。如果孩子想少用,它可以做到,但它必须从提供一切开始。
也许如果你编辑你的问题来解释你想完成什么,我可以完善我的答案一点。
如果你想让GeometryReader报告VStack的大小,你可以把它放在一个.background修饰符中。但是,我不确定目标是什么,所以也许这是不可能的。
我写了一篇关于GeometryReader的不同用途的文章。这里的链接,如果它有帮助:https://swiftui-lab.com/geometryreader-to-the-rescue/
更新
好了,通过你的额外解释,这里你有一个工作解决方案。请注意,预览将不工作,因为safeInsets被报告为零。然而,在模拟器上,它工作正常:
正如你将看到的,我使用视图首选项。它们在任何地方都没有解释,但我目前正在写一篇关于它们的文章,我将很快发布。
它可能看起来太冗长了,但是如果您发现自己使用它太频繁了,您可以将它封装在一个自定义修饰符中。
d7v8vwbk3#
我设法解决了这个问题,将页面主视图 Package 在
GeometryReader
中,并将safeAreaInsets
传递给MyView
。由于它是我们想要整个屏幕的主页面视图,因此可以尽可能贪婪。q5lcpyga4#
我对我最终使用的解决方案并不完全满意,但它确实有效,所以我想我应该分享它。
此示例实现用于在聊天记录中呈现一个条目行的视图。它使用
GeometryReader
作为上下文视图中最上面的HStack
的背景-占据所述视图的整个宽度。当视图绘制时,
chatBalloonMaxWidth
状态由GeometryReader
Package 的Rectangle()
上的onAppear
回调设置。这会立即应用所需宽度(总宽度的三分之一)。为了处理窗口大小调整事件(这是一个macOS应用程序),另一个回调被添加到同一个
Rectangle()
-这次监听NSWindow
上的didResizeNotification
。因为SwiftUI的Window
是NSWindow
的扩展,我们仍然可以使用这个通知。在
setChatBalloonMaxWidth
函数中,一个条件检查新的更新宽度是否确实必要。这样做是为了避免不必要的状态更新-特别是那些由应用程序窗口调整大小事件触发的更新,而不是由当前窗口调度的。虽然我不喜欢这种方法,但它确实有效,而且响应流畅。它允许我使视图组件匿名,而不需要每次使用时都将
GeometryProxy
传入初始化器。如果一个组件的大小调整是通过其他元素完成的(例如,sidebar,draggable),你可以监听
NSView.boundsDidChangeNotification
,它也会被分派到NotificationCenter.default
上。你还可以通过创建一个自定义的NSViewRepresentable
并在你的onReceive
回调中检查该对象来将范围缩小到单个组件。