如何在SwiftUI中访问安全区域大小?

M. *_*oot 1 swiftui

我想在SwiftUI中手动将视图的框架高度设置为屏幕安全区域的大小。获取屏幕边界(UIScreen.main.bounds)很容易,但是我找不到找到安全区域大小的方法。

Mir*_*rko 49

UIApplication.shared.windows 已弃用,您现在可以使用connectedScenes:

import SwiftUI

extension UIApplication {
    var keyWindow: UIWindow? {
        connectedScenes
            .compactMap {
                $0 as? UIWindowScene
            }
            .flatMap {
                $0.windows
            }
            .first {
                $0.isKeyWindow
            }
    }
}

private struct SafeAreaInsetsKey: EnvironmentKey {
    static var defaultValue: EdgeInsets {
        UIApplication.shared.keyWindow?.safeAreaInsets.swiftUiInsets ?? EdgeInsets()
    }
}

extension EnvironmentValues {
    var safeAreaInsets: EdgeInsets {
        self[SafeAreaInsetsKey.self]
    }
}

private extension UIEdgeInsets {
    var swiftUiInsets: EdgeInsets {
        EdgeInsets(top: top, leading: left, bottom: bottom, trailing: right)
    }
}
Run Code Online (Sandbox Code Playgroud)

然后在视图中使用环境属性来获取安全区域插图:

@Environment(\.safeAreaInsets) private var safeAreaInsets
Run Code Online (Sandbox Code Playgroud)

  • 这是动态的,所以会更新吗? (2认同)

Phi*_*hov 13

不知道为什么接受的答案使用顶部插图放置在底部视图下方 - 这些是不一样的。此外,如果您更正此“拼写错误”,您将看到edgesIgnoringSafeArea调用GeometryReader的相应值为零。看起来 iOS 13 上的情况并非如此,但现在是这样,因此您需要调用edgesIgnoringSafeAreaGeometryReader进程,并且此代码在 iOS 13 上仍然按预期工作:

GeometryReader { geometry in
    VStack {
        Spacer()
        Color.red
            .frame(
                width: geometry.size.width,
                height: geometry.safeAreaInsets.bottom,
                alignment: .center
            )
            .aspectRatio(contentMode: ContentMode.fit)
    }
    .edgesIgnoringSafeArea(.bottom)
}
Run Code Online (Sandbox Code Playgroud)


Lor*_*ngo 11

如果您edgesIgnoringSafeArea在 parentView 上使用并且想要访问该设备,UISafeAreaInsets您可以执行以下操作:

代码

private struct SafeAreaInsetsKey: EnvironmentKey {
    static var defaultValue: EdgeInsets {
        (UIApplication.shared.windows.first(where: { $0.isKeyWindow })?.safeAreaInsets ?? .zero).insets
    }
}

extension EnvironmentValues {
    
    var safeAreaInsets: EdgeInsets {
        self[SafeAreaInsetsKey.self]
    }
}

private extension UIEdgeInsets {
    
    var insets: EdgeInsets {
        EdgeInsets(top: top, leading: left, bottom: bottom, trailing: right)
    }
}
Run Code Online (Sandbox Code Playgroud)

用法

struct MyView: View {
    
    @Environment(\.safeAreaInsets) private var safeAreaInsets
    
    var body: some View {
        Text("Ciao")
            .padding(safeAreaInsets)
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 假设 windows[0] 将返回 UIWindow 是不安全的。根据 Padam Thapan 的博客 (https://padamthapa.com/blog/how-to-get-deprecated-keywindow-in-swift/) 提供的信息,当存在键盘时,此数组中的第一个元素可能会成为 UITextEffectsWindow在屏幕上。我觉得更好的解决方案是将 keyWindow 的引用包装在一个结构中并将其放入环境中。这将使我们能够同样方便地访问上述扩展。 (13认同)

Mof*_*waw 8

您还可以创建自定义EnvironmentValue并从“初始”传递安全区域插图View。这对我来说非常有效!

创造EnvironmentValue

private struct SafeAreaInsetsEnvironmentKey: EnvironmentKey {
    static let defaultValue: (top: CGFloat, bottom: CGFloat) = (0, 0)
}

extension EnvironmentValues {
    var safeAreaInsets: (top: CGFloat, bottom: CGFloat) {
        get { self[SafeAreaInsetsEnvironmentKey.self] }
        set { self[SafeAreaInsetsEnvironmentKey.self] = newValue }
    }
}
Run Code Online (Sandbox Code Playgroud)

环境

View我们的想法是在任何潜在的父母使用之前执行此操作.edgesIgnoringSafeArea,这是它发挥作用所必需的。例如:

@main
struct YourApp: App {
    @State private var safeAreaInsets: (top: CGFloat, bottom: CGFloat) = (0, 0)
    
    var body: some Scene {
        WindowGroup {
            ZStack {
                GeometryReader { proxy in
                    Color.clear.onAppear {
                        safeAreaInsets = (proxy.safeAreaInsets.top, proxy.safeAreaInsets.bottom)
                    }
                }
                
                ContentView()
                    .environment(\.safeAreaInsets, safeAreaInsets)
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

用法

struct SomeChildView: View {
    @Environment(\.safeAreaInsets) var safeAreaInsets

    ...
}
Run Code Online (Sandbox Code Playgroud)


Ugo*_*ino 7

您可以使用GeometryReader访问安全区域。
请参阅:https : //developer.apple.com/documentation/swiftui/geometryreader

struct ContentView : View {

    var body: some View {
        GeometryReader { geometry in
            VStack {
                Spacer()
                Color.red
                    .frame(
                        width: geometry.size.width,
                        height: geometry.safeAreaInsets.top,
                        alignment: .center
                )
                    .aspectRatio(contentMode: ContentMode.fit)
            }
        }
        .edgesIgnoringSafeArea(.bottom)
    }
}
Run Code Online (Sandbox Code Playgroud)

但仅供参考:她的安全区域不是大小。这是一个EdgeInsets

  • 如果您在父视图上使用 .ignoresSafeArea,则这不起作用。 (2认同)