SwiftUI - 确定当前设备和方向

10

我试图检测设备是否为iPad并处于竖屏状态。

目前,我使用UIKit中的UIDevice API,并使用环境对象来监视变化。我使用此处找到的解决方案- 确定当前设备和方向

然而,orientationInfo.orientation最初始终等于.portrait,直到旋转为纵向,然后再旋转回横向。

因此,在执行以下操作以显示FABView时:

struct HomeView: View {

@EnvironmentObject var orientationInfo: OrientationInfo
let isPhone = UIDevice.current.userInterfaceIdiom == .phone

    var body: some View {
      ZStack(alignment: .bottom) {
          #if os(iOS)
          if isPhone == false && orientationInfo.orientation == .portrait {
            FABView()
          }
          #endif
      }
    }
}

当iPad最初处于横向模式时,视图被加载,但在更改为竖向模式然后再次更改为横向模式时,则被移除。为什么会发生这种情况,如何确保在第一次加载时不加载视图? 完整代码
struct HomeTab: View {
    
    var body: some View {
        NavigationView {
            HomeView()
                .environmentObject(OrientationInfo())
        }
    }
}

struct HomeView: View {

@EnvironmentObject var orientationInfo: OrientationInfo
let isPhone = UIDevice.current.userInterfaceIdiom == .phone

    var body: some View {
      ZStack(alignment: .bottom) {
          #if os(iOS)
          if isPhone == false && orientationInfo.orientation == .portrait {
            FABView()
          }
          #endif
      }
    }
}

final class OrientationInfo: ObservableObject {
    enum Orientation {
        case portrait
        case landscape
    }
    
    @Published var orientation: Orientation
    
    private var _observer: NSObjectProtocol?
    
    init() {
        // fairly arbitrary starting value for 'flat' orientations
        if UIDevice.current.orientation.isLandscape {
            self.orientation = .landscape
        }
        else {
            self.orientation = .portrait
        }
        
        // unowned self because we unregister before self becomes invalid
        _observer = NotificationCenter.default.addObserver(forName: UIDevice.orientationDidChangeNotification, object: nil, queue: nil) { [unowned self] note in
            guard let device = note.object as? UIDevice else {
                return
            }
            if device.orientation.isPortrait {
                self.orientation = .portrait
            }
            else if device.orientation.isLandscape {
                self.orientation = .landscape
            }
        }
    }
    
    deinit {
        if let observer = _observer {
            NotificationCenter.default.removeObserver(observer)
        }
    }
}
2个回答

14

您可以使用UIDevice.orientationDidChangeNotification来检测方向变化,但在应用程序启动时不应该依赖它。

UIDevice.current.orientation.isValidInterfaceOrientation在开始时将为false,因此两者

  • UIDevice.current.orientation.isLandscape

  • UIDevice.current.orientation.isPortrait

将会返回false

相反,您可以使用第一个窗口场景中的interfaceOrientation

struct ContentView: View {
    @State private var isPortrait = false
    
    var body: some View {
        Text("isPortrait: \(String(isPortrait))")
            .onReceive(NotificationCenter.default.publisher(for: UIDevice.orientationDidChangeNotification)) { _ in
                guard let scene = UIApplication.shared.windows.first?.windowScene else { return }
                self.isPortrait = scene.interfaceOrientation.isPortrait
            }
    }
}

请注意,设备方向不等于界面方向。 当设备在纵向模式下朝下时,设备方向为纵向,但界面方向也可以是横向

我认为在您的情况下最好依赖于界面方向。


优秀的解决方案。我只想补充一点,如果你想在启动时了解界面的方向,而不必等待旋转发生,可以将相同的代码添加到.onAppear中。 - undefined

1
我想再添加一些细节,这些细节在某些情况下可能会有所帮助:
您可以通过rawValue获取方向:
UIDevice.current.orientation.rawValue
  • Portrait = 1(纵向)
  • PortraitUpSideDown = 2(倒立)
  • LandscapeLeft = 3(设备顶部向左)
  • LandscapeRight = 4(设备顶部向右)

经常被忽略的是,还有一种平放的方向。当您在物理设备上测试应用程序时,这可能会成为一个问题。

  • Flat = 5(设备背面朝上)
  • Flat = 6(设备正面/屏幕朝上)

当您使用UIDevice.orientationDidChangeNotification的通知时,这很重要。

因为每次用户更改平放方向时,您都会收到通知,但您只希望在从纵向到横向或从横向到纵向时得到通知。

解决此问题的方法

  1. 您可以使用.isValidInterfaceOrientation,它仅返回纵向和横向方向。

UIDevice.current.orientation.isValidInterfaceOrientation

只有在 rawValue 为1到4时,你才能更改变量。
struct ContentView: View {
    @State private var isPortrait = false

    var body: some View {
        Text("isPortrait: \(String(isPortrait))")
            .onReceive(NotificationCenter.default.publisher(for: UIDevice.orientationDidChangeNotification)) { _ in

                if UIDevice.current.orientation.rawValue <= 4 { // This will run code only on Portrait and Landscape changes
                    guard let scene = UIApplication.shared.windows.first?.windowScene else { return }
                    self.isPortrait = scene.interfaceOrientation.isPortrait
                }
            }
    }
} 

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接