使用 UIKit 进行多年开发后,我决定开始使用 SwiftUI。我发现了一件很奇怪的事情,这让我花了至少 6 个小时的时间来拔头发。
目标:我有一个可以在纵向和横向模式下运行的应用程序。
肖像模式:一切都很好,UI 的渲染完全符合我的预期。
横向模式:当我将手机旋转到横向模式时,所有 UI 似乎都有超出屏幕边界的偏移。虽然我看到我的元素正确水平对齐,但我看不到屏幕的顶部。没关系,如果我启动手机已经处于横向模式的应用程序,或者如果我以纵向模式启动应用程序然后旋转 - 效果是完全相同的。
这是我的 ContentView - 根视图,这是我的 UI 的开始
struct ContentView: View {
@State var isActive: Bool = false
private let container: DIContainer
init(container: DIContainer) {
self.container = container
self.isActive = isActive
}
var body: some View {
ZStack {
if self.isActive {
RootView().inject(container)
} else {
LinearGradient(colors: [.blue, Color("light_blue")],
startPoint: .top,
endPoint: .center)
.edgesIgnoringSafeArea(/*@START_MENU_TOKEN@*/.all/*@END_MENU_TOKEN@*/)
Text("HELLO")
.font(.system(size:36))
.foregroundColor(Color.white)
}
}.onAppear {
DispatchQueue.main.asyncAfter(deadline: .now() + 2.5) {
withAnimation {
self.isActive = true
}
}
}
}
}
因此,为了调试这个令人难以置信的恼人问题,我决定将 ContentView 包装在 GeometryReader 中,看看它有什么偏移量等。所以我这样做了
struct ContentView: View {
@State var isActive: Bool = false
private let container: DIContainer
init(container: DIContainer) {
self.container = container
self.isActive = isActive
}
var body: some View {
GeometryReader { geometry in
ZStack {
if self.isActive {
RootView().inject(container)
} else {
LinearGradient(colors: [.blue, Color("light_blue")],
startPoint: .top,
endPoint: .center)
.edgesIgnoringSafeArea(/*@START_MENU_TOKEN@*/.all/*@END_MENU_TOKEN@*/)
Text("HELLO")
.font(.system(size:36))
.foregroundColor(Color.white)
}
}.onAppear {
DispatchQueue.main.asyncAfter(deadline: .now() + 2.5) {
withAnimation {
self.isActive = true
}
}
}
}
}
}
您相信吗 - 它解决了一个问题!一旦我将 ContentView 包装在 GeometryReader 中,应用程序就开始在所有模式下正确渲染 - 纵向和横向。所以基本上 - 纯粹出于运气,我已经解决了这个问题。
我的问题是 - 为什么我需要将 ContentView 包装在 Geometry Reader 中,以便 SwiftUI 能够正确渲染?我问的原因是 - 我搜索了所有 Google、Youtube 等,但没有在任何地方找到此推荐!所以我的假设是我的 SwiftUI 代码中存在严重错误,这就是我需要实现此解决方法的原因。因为我不相信每个拥有支持景观的应用程序的人都会默默地处理同样的问题并且没有分享这个重要信息。
UPD:如果我仅将应用程序锁定在横向模式下,它仍然无法正确渲染,并且与顶部有巨大的偏移。如果我将 ContentView 包装在 GeometryReader 中,它会呈现良好
A
GeometryReader
是贪婪的,会使用所有可用空间,而 ZStack
本身无法做到这一点。但是,如果将 Color.clear
放入 ZStack
内,则会使其膨胀到最大尺寸。
然后,为了模拟
GeometryReader
内部的外观,您可能需要对 .topLeading
使用 ZStack
对齐:
ZStack(alignment: .topLeading) { // alignment added
Color.clear // added
if self.isActive {
RootView().inject(container)
} else {
// other content as before
}
}
将
.ignoresSafeArea()
添加到 ZStack
和/或 RootView
也可能有所帮助。
添加对齐方式将导致初始文本消息出现在左上角,因此要解决此问题,您可以对文本应用最大宽度和高度:
Text("HELLO")
.font(.system(size:36))
.foregroundColor(Color.white)
.frame(maxWidth: .infinity, maxHeight: .infinity) // added