【问题标题】:How to access safe area size in SwiftUI?如何在 SwiftUI 中访问安全区域大小?
【发布时间】:2019-11-28 16:48:15
【问题描述】:

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

【问题讨论】:

    标签: ios swift swiftui


    【解决方案1】:

    不确定为什么接受的答案使用顶部插图作为放置在底部下方的视图 - 这些不一样。 此外,如果您更正此“错字”,您会看到在 GeometryReader 上调用的 edgesIgnoringSafeArea 会将相应的值归零。看起来在 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)
    }
    

    【讨论】:

    • 我认为您在“...GeometryReader 将对应值归零”中遗漏了一个词?无论如何,我解决了; SwiftUI 是否有针对此类情况的“无操作”容器,或者即使您在技术上不需要,您是否只使用 VStack
    • @RobertAtkins 我不确定我在这里可能漏掉了什么词?我的意思是说,如果你在GeometryReader 之外禁用edgesIgnoringSafeArea,你就不能在里面获得geometry.safeAreaInsets(值将为零)。使用geometry,我们可以获得所需的尺寸,但为了将视图放在正确的位置,我使用VStackSpacer 占用了所有剩余空间并将我的视图向下推。
    • “……将 对应的值归零”可能吗?是的,我知道一个垫片会把事情搞砸,但我觉得使用只有一个东西的VStack 很奇怪。不过,这与最初的问题并没有真正的密切关系。
    • @RobertAtkins 现在我明白了,谢谢,我不是母语人士,所以我会犯这样的错误。这取决于您需要解决的问题,但通常以这种方式使用VStack 并不是一个坏习惯。绘制这样的视图不会是高负载操作。你可以设置一个父视图的对齐方式,但是如果你想让不同的子视图有不同的对齐方式,你可以使用这个方法。
    【解决方案2】:

    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
    

    【讨论】:

      【解决方案3】:

      如果您在 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)
          }
      }
      

      【讨论】:

      • 假设 windows[0] 将返回 UIWindow 是不安全的。根据 Padam Thapan (padamthapa.com/blog/how-to-get-deprecated-keywindow-in-swift) 的此博客中的信息,当键盘出现在屏幕上时,此数组中的第一个元素可能会变成 UITextEffectsWindow。我觉得更好的解决方案是将对 keyWindow 的引用包装在结构中并将其放入环境中。这将使我们能够方便地访问上述扩展。
      • @Shengchalover 感谢您的反馈,我更新了我的答案。我决定不捕获对 keyWindow 的引用,因为我们不知道它是否会改变。
      • 这段代码不能编译,你应该使用:static var defaultValue: EdgeInsets { UIApplication.shared.windows.first(where: { $0.isKeyWindow })?.safeAreaInsets.insets ?? EdgeInsets() }
      • @FarouK 现在应该没问题
      • 由于 home 指示器,使用它在横向中居中视图,“y”的偏移应该只在方向是横向时应用,不是吗?
      【解决方案4】:

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

      【讨论】:

      • 如果您在父视图上使用 .ignoresSafeArea,这将不起作用。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-08-19
      • 2012-07-18
      • 2012-07-27
      • 2020-06-25
      • 1970-01-01
      相关资源
      最近更新 更多