SwiftUI 以编程方式强制设备旋转

问题描述 投票:0回答:2

我的应用程序具有根据设备设置的某些方向。 对于 iPad,它设置为横向;对于 iPhone,它设置为纵向。 这工作得很好,除了我想允许 iPhone 横向显示一个视图。 我需要视图自动旋转为横向并在离开该视图后旋转回纵向。 我有以下代码可以锁定该视图的方向,但必须手动来回旋转设备。有没有办法自动旋转设备?

.onAppear {
            if(deviceType == .phone){
                UIDevice.current.setValue(UIInterfaceOrientation.portrait.rawValue, forKey: "orientation")
                AppDelegate.orientationLock = .landscape

            }
        }
.onDisappear {
            if(deviceType == .phone){
                AppDelegate.orientationLock = .portrait
            }
        }
swiftui orientation
2个回答
10
投票

enter image description here

这可以使用

View extension
来实现,它将更新视图的方向。让我们制作自己的扩展,它将在给定方向上来回
rotate
view

extension View {
    @ViewBuilder
    func forceRotation(orientation: UIInterfaceOrientationMask) -> some View {
        if UIDevice.current.userInterfaceIdiom == .phone {
            self.onAppear() {
                AppDelegate.orientationLock = orientation
            }
            // Reset orientation to previous setting
            let currentOrientation = AppDelegate.orientationLock
            self.onDisappear() {
                AppDelegate.orientationLock = currentOrientation
            }
        } else {
            self
        }
    }
}

在AppDelegate中,添加:

class AppDelegate: NSObject, UIApplicationDelegate {

    static var orientationLock = UIInterfaceOrientationMask.portrait {
        didSet {
            if #available(iOS 16.0, *) {
                UIApplication.shared.connectedScenes.forEach { scene in
                    if let windowScene = scene as? UIWindowScene {
                        windowScene.requestGeometryUpdate(.iOS(interfaceOrientations: orientationLock))
                    }
                }
                UIViewController.attemptRotationToDeviceOrientation()
            } else {
                if orientationLock == .landscape {
                    UIDevice.current.setValue(UIInterfaceOrientation.landscapeRight.rawValue, forKey: "orientation")
                } else {
                    UIDevice.current.setValue(UIInterfaceOrientation.portrait.rawValue, forKey: "orientation")
                }
            }
        }
    }
    
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
        return true
    }
    
    func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
        return AppDelegate.orientationLock
    }
}

这个小例子演示了如何使用此扩展:

struct ContentView: View {
        
    var body: some View {
        NavigationView {
            List(Orientation.allCases, id: \.self) { orientation in
                       NavigationLink(orientation.title) {
                           ZStack {
                               Text(orientation.title)
                           }
                           .forceRotation(orientation: orientation.mask)  // << Update the required orientation here.. 
                       }
                   }
               }
        }
}

enum Orientation: Int CaseIterable {
    case landscapeLeft
    case landscapeRight
    
    var title: String {
        switch self {
        case .landscapeLeft:
            return "LandscapeLeft"
        case .landscapeRight:
            return "LandscapeRight"
        }
    }
    
    var mask: UIInterfaceOrientationMask {
        switch self {
        case .landscapeLeft:
            return .landscapeLeft
        case .landscapeRight:
            return .landscapeRight
        }
    }
}

0
投票

Kush Bhavsar 的答案几乎对我有用,但当视图消失时它无法恢复到之前的方向。这是一个确实按预期恢复到以前的方向的解决方案(并且我改进了一些内容的命名和组织以使其更加清晰)。

创建视图扩展

supportedInterfaceOrientations
:

extension View {
  @ViewBuilder func supportedInterfaceOrientations(
    _ orientations: UIInterfaceOrientationMask
  ) -> some View {
    modifier(SupportedInterfaceOrientationsModifier(orientations: orientations))
  }
}

private struct SupportedInterfaceOrientationsModifier: ViewModifier {
  let orientations: UIInterfaceOrientationMask

  @State private var previousOrientations = UIInterfaceOrientationMask.portrait

  func body(content: Content) -> some View {
    content
      .onAppear() {
        previousOrientations = AppDelegate.supportedInterfaceOrientations
        AppDelegate.supportedInterfaceOrientations = orientations
      }
      .onDisappear() {
        AppDelegate.supportedInterfaceOrientations = previousOrientations
      }
  }
}

在AppDelegate中,添加静态变量:

  static var supportedInterfaceOrientations = UIInterfaceOrientationMask.portrait {
    didSet { updateSupportedInterfaceOrientationsInUI() }
  }

并添加 AppDelegate 扩展:

extension AppDelegate {
  private static func updateSupportedInterfaceOrientationsInUI() {
    if #available(iOS 16.0, *) {
      UIApplication.shared.connectedScenes.forEach { scene in
        if let windowScene = scene as? UIWindowScene {
          windowScene.requestGeometryUpdate(
            .iOS(interfaceOrientations: supportedInterfaceOrientations)
          )
        }
      }
      UIViewController.attemptRotationToDeviceOrientation()
    } else {
      if supportedInterfaceOrientations == .landscape {
        UIDevice.current.setValue(
          UIInterfaceOrientation.landscapeRight.rawValue, forKey: "orientation"
        )
      } else {
        UIDevice.current.setValue(UIInterfaceOrientation.portrait.rawValue, forKey: "orientation")
      }
    }
  }

  func application(
    _ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?
  ) -> UIInterfaceOrientationMask {
    Self.supportedInterfaceOrientations
  }
}
© www.soinside.com 2019 - 2024. All rights reserved.