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

oiopk7p5  于 2023-02-15  发布在  Swift
关注(0)|答案(6)|浏览(219)

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

368yc8dk

368yc8dk1#

您可以使用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)
    }
}

不过顺便说一句:安全区不是一个尺寸,它是一个EdgeInsets

xghobddn

xghobddn2#

如果在parentView上使用edgesIgnoringSafeArea,并且希望访问设备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)
    }
}
    • 用法**
struct MyView: View {
    
    @Environment(\.safeAreaInsets) private var safeAreaInsets
    
    var body: some View {
        Text("Ciao")
            .padding(safeAreaInsets)
    }
}
9nvpjoqh

9nvpjoqh3#

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)
    }
}

然后使用View中的Environment属性获取安全区域插图:

@Environment(\.safeAreaInsets) private var safeAreaInsets
nlejzf6q

nlejzf6q4#

不知道为什么接受的答案使用顶部插入的一个视图放在下面的一个-这是不一样的。另外,如果你纠正这个“错别字”,你会看到edgesIgnoringSafeArea调用了一个GeometryReader零相应的值。看起来不是这样的iOS 13回来,但现在它是,所以你需要调用一个GeometryReader孩子edgesIgnoringSafeArea代替,这段代码在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)
}
frebpwbc

frebpwbc5#

您也可以创建一个自定义的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 }
    }
}

设置

我们的想法是在任何潜在的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)
            }
        }
    }
}

用法

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

    ...
}
ff29svar

ff29svar6#

extension UIScreen {
    static var topSafeArea: CGFloat {
        let keyWindow = UIApplication.shared.connectedScenes
            .filter({$0.activationState == .foregroundActive})
            .map({$0 as? UIWindowScene})
            .compactMap({$0})
            .first?.windows
            .filter({$0.isKeyWindow}).first
        
        return (keyWindow?.safeAreaInsets.top) ?? 0
    } 
}

用法:

UIScreen.topSafeArea

相关问题