使用 .edgesIgnoringSafeArea() 时,SwiftUI 布局超出设备范围
Posted
技术标签:
【中文标题】使用 .edgesIgnoringSafeArea() 时,SwiftUI 布局超出设备范围【英文标题】:SwiftUI layout grows outside the bounds of the device when using .edgesIgnoringSafeArea() 【发布时间】:2020-04-28 01:40:44 【问题描述】:在使用 .edgesIgnoringSafeArea(.bottom) 时,在 SwiftUI 中存在一个问题,即某些视图在垂直方向上的增长大于设备的大小。在 812 像素高的 iPhone 11 Pro 上,我看到大小为 846 的视图。我正在使用 Debug View Hierarchy 来验证它。这已经在 Xcode 11.4.1 和 11.1 上进行了测试,并且存在于两个版本中,并且可能介于两者之间。
我在下面包含了示例代码。
我很确定这是一个 SwiftUI 错误,但想知道是否有人有解决方法。我需要edgesIgnoringSafeArea(.bottom) 代码来绘制TabBar,并在我隐藏自定义标签栏时让ProfileView() 扩展到屏幕底部。
struct ContentView: View
var body: some View
MainTabView()
struct MainTabView : View
enum Item : CaseIterable
case home
case resources
case profile
@State private var selected : Item = .home
var body: some View
VStack(spacing: 0.0)
ZStack
HomeView()
.zIndex(selected == .home ? 1 : 0)
ResourcesView()
.zIndex(selected == .resources ? 1 : 0)
ProfileView()
.zIndex(selected == .profile ? 1 : 0)
// Code here for building and showing/hiding a Toolbar
// Basically just a HStack with a few buttons in it
.edgesIgnoringSafeArea(.bottom) // <- This causes the screen to jump to 846
struct ProfileView : View
@State private var showQuestionnaireView = false
var body: some View
NavigationView
ZStack
NavigationLink(destination: QuestionnaireView( showQuestionnaireView:$showQuestionnaireView),
isActive: $showQuestionnaireView)
Text("Show Questionnaire View")
.navigationBarTitle("")
.navigationBarHidden(true)
struct QuestionnaireView : View
@Binding var showQuestionnaireView : Bool
var body: some View
GeometryReader screenGeometry in
ZStack
Color.orange
VStack
Text("Top")
Spacer()
Text("Bottom")
HomeView() 和 ResourcesView() 只是 ProfileView() 的副本,它们做自己的事情。
当你运行它时,你会看到一个按钮,按下按钮,一个隐藏的 Navigation Stack View 会推送到 QuestionnaireView 上,这个视图包含一个带有两个文本字段的 VStack,由于这个问题,你都看不到.可以理解的是,顶部位于缺口后面,但底部位于屏幕底部。在我的真实项目中,这个问题在运行时很少出现,但是在暗模式和亮模式之间切换会显示出来。在上面的代码中,不需要切换外观。
编辑:FB7677794 对于任何感兴趣的人,自 3 周前提交以来尚未收到 Apple 的任何更新。
EDIT2:向 MainTabBar 添加了更多代码
更新:这已在 Xcode 12 Beta 2 中修复
【问题讨论】:
【参考方案1】:阅读更新后的问题后,我做了一些更改并尝试制作一个小演示。在此,我使用与以前相同的方法,将NavigationView
放在您的主标签视图中,这样您就不必每次来或离开主标签视图时都隐藏和显示。
import SwiftUI
struct ContentView: View
var body: some View
MainTabView()
struct MainTabView : View
enum Item : CaseIterable
case home
case resources
case profile
@State private var selected : Item = .home
var body: some View
NavigationView
VStack(spacing: 0.0)
ZStack
Group
HomeView()
.zIndex(selected == .home ? 1 : 0)
ResourcesView()
.zIndex(selected == .resources ? 1 : 0)
ProfileView()
.zIndex(selected == .profile ? 1 : 0)
.frame(minWidth: .zero, maxWidth: .infinity, minHeight: .zero, maxHeight: .infinity)
.background(Color.white)
HStack
Group
Image(systemName: "house.fill")
.onTapGesture
self.selected = .home
Spacer()
Image(systemName: "plus.app.fill")
.onTapGesture
self.selected = .resources
Spacer()
Image(systemName: "questionmark.square.fill")
.onTapGesture
self.selected = .profile
.padding(.horizontal, 30)
.frame(height: 40)
.foregroundColor(Color.white)
.background(Color.gray)
// Code here for building and showing/hiding a Toolbar
// Basically just a HStack with a few buttons in it
.edgesIgnoringSafeArea(.bottom)
// <- This causes the screen to jump to 846
struct ProfileView : View
@State private var showQuestionnaireView = false
var body: some View
// NavigationView
ZStack
NavigationLink(destination: QuestionnaireView( showQuestionnaireView:$showQuestionnaireView),
isActive: $showQuestionnaireView)
Text("Show Questionnaire View")
.navigationBarTitle("")
.navigationBarHidden(true)
//
struct QuestionnaireView : View
@Binding var showQuestionnaireView : Bool
var body: some View
GeometryReader screenGeometry in
ZStack
Color.orange
VStack
Text("Top")
Spacer()
Text("Bottom")
.edgesIgnoringSafeArea(.bottom)
struct HomeView: View
var body: some View
NavigationLink(destination: SecondView())
Text("Home View")
struct ResourcesView: View
var body: some View
NavigationLink(destination: SecondView())
Text("Resources View")
struct SecondView: View
var body: some View
Text("Second view in navigation")
.background(Color.black)
.foregroundColor(.white)
struct ContentView_Previews: PreviewProvider
static var previews: some View
ContentView()
.previewDevice(PreviewDevice(rawValue: "iPhone 11"))
【讨论】:
我不能这样做,因为 MainTabView 比我发布的代码更复杂。它是一个包含 3 个主屏幕的 TabView,可以像普通的 Tab View 一样在它们之间切换(只有我的是自定义的,所以我可以隐藏工具栏)。这些主屏幕中的每一个都有自己的 NavigationView。在只发布您认为相关的代码和在问题中倾倒一大堆代码之间,这是一条很好的界限。我会尝试在问题中向 MainTabView 添加更多代码。 我对我的答案做了一些修改,我希望这就是你想要的。 谢谢 Nilay,这种方式确实有效,感谢您在我更新问题后花时间更新您的答案。我已经给你赏金了。 仅供参考,我发布的原始代码适用于 Xcode 12 Beta 2,Apple 表示他们已经解决了这个问题。但是现在顶部标签从顶部向下大约 44.0 像素 ? 我仍然在我的应用程序中使用你的解决方法。 很高兴能为您提供帮助。【参考方案2】:这是由于NavigationView
的大小未定义。当您添加自定义标签栏组件时,如下例所示,限制底部区域,NavigationView 将正确布局。
使用 Xcode 11.4 / ios 13.4 测试
struct MainTabView : View
var body: some View
VStack(spacing: 0.0)
ZStack
Color(.cyan)
ProfileView() // << this injects NavigationView
HStack // custom tab bar
Button(action: ) Image(systemName: "1.circle").padding()
Button(action: ) Image(systemName: "2.circle").padding()
Button(action: ) Image(systemName: "3.circle").padding()
.padding(.bottom)
.edgesIgnoringSafeArea(.bottom) // works !!
【讨论】:
感谢@Asperi 的快速回复。我希望这个问题比上一个问题(我删除了)更好地提出。所以我有非常相似的代码(我拿出来发布),它显示了 TabBar。我制作自己的标签栏的原因是因为 SwiftUI 无法隐藏。因此,当我在“个人资料”屏幕上并按下问卷按钮时,我会隐藏我的 TabBar,以便下一个屏幕具有全高。然后屏幕尺寸问题变得明显。你能详细说明为什么 NavigationView 在这里有一个未定义的大小吗?它不应该在屏幕尺寸上达到最大值吗?以上是关于使用 .edgesIgnoringSafeArea() 时,SwiftUI 布局超出设备范围的主要内容,如果未能解决你的问题,请参考以下文章
在我的 NavigationView '.edgesIgnoringSafeArea' 中不会将内容移动到安全区域之外
SwiftUI - edgesIgnoringSafeArea 在 iOS 13.4 中的行为不同