ios横竖屏旋转问题

267 阅读2分钟

这个的要求是到了某个页面,需要锁定用户通过手机重力旋转屏幕,只能通过点击“旋转到横屏”按钮或者“回退到竖屏”按钮来改变webview的方向,所以大致流程如下,要有两个桥接方法“rotateScreen”,"lockScreen"或者合成一个方法:"rotateAndLockScreen()";在退出该页面的时候,要“unlockRotate()”

image.png

存在三个不同层面的方向控制:

1. 手机硬件的实际方向

  • 这是物理设备的真实朝向(重力感应器检测)
  • 用户如何持握手机:竖直、左横、右横、倒置等

2. WebView的方向

  • 这是原生容器(iOS/Android App)强制设置的显示方向
  • 通过 rotateScreen 方法控制
  • iOS: UIInterfaceOrientationMask.landscape 或 .portrait
  • Android: 类似的原生旋转API

3. Layout的方向(H5页面布局)

  • 这是前端页面的布局状态,由 isLandscape 状态控制
  • 决定渲染哪个组件: LandscapePage 还是竖屏页面
  • 通过 setIsLandscape(true/false) 手动控制

问题的核心

理想状态 :三个方向应该协调工作

  • 用户点击"切换横屏批改" → setIsLandscape(true) → 调用原生 rotateScreen('horizontal') → 强制WebView横屏显示
  • 无论用户如何旋转手机,WebView都保持横屏,Layout也保持横屏布局
  override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
    if isOrientationLocked {
      return lockedOrientation
    }
    return .all
  }
  
  override var shouldAutorotate: Bool {
    return !isOrientationLocked
  }
private func rotateAndLockOrientation(_ orientation: UIInterfaceOrientationMask) {
    // 第一步:确保支持目标方向(临时解锁)
    isOrientationLocked = false
    log.info("rotateAndLockOrientation isOrientationLocked: \(isOrientationLocked)")
    log.info("rotateAndLockOrientation: \(orientation)")
    
    // 强制系统重新查询支持的方向
    UIViewController.attemptRotationToDeviceOrientation()
    
    // 添加短暂延迟,确保系统有时间处理方向变化
    DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
        // 第二步:旋转到目标方向
        if #available(iOS 16.0, *) {
            log.info("requestGeometryUpdate11")
            
            guard let window = self.view.window else {
                log.error("view.window is nil")
                return
            }
            
            guard let windowScene = window.windowScene else {
                log.error("windowScene is nil")
                return
            }
            
            log.info("window and windowScene are available")
            
            var callbackExecuted = false
            
            let preferences = UIWindowScene.GeometryPreferences.iOS(interfaceOrientations: orientation)
            
            // 修正:使用 Task 来处理异步调用
            Task { @MainActor in
                do {
                    try await windowScene.requestGeometryUpdate(preferences)
                    callbackExecuted = true
                    log.info("requestGeometryUpdate 成功完成")
                    log.info("旋转屏幕成功")
                    
                    // 延迟锁定方向
                    try await Task.sleep(nanoseconds: 100_000_000) // 0.1秒
                    self.isOrientationLocked = true
                    self.lockedOrientation = orientation
                    log.info("屏幕方向已锁定")
                    UIViewController.attemptRotationToDeviceOrientation() // 添加这行
                    
                } catch {
                    callbackExecuted = true
                    log.error("requestGeometryUpdate 出错: \(error)")
                    
                    // 即使出错也锁定方向
                    self.isOrientationLocked = true
                    self.lockedOrientation = orientation
                    log.info("屏幕方向已锁定(出错后)")
                }
            }
            
            // 添加超时机制
            DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
                if !callbackExecuted {
                    log.warning("requestGeometryUpdate 回调超时,强制执行锁定逻辑")
                    self.isOrientationLocked = true
                    self.lockedOrientation = orientation
                    log.info("屏幕方向已强制锁定")
                }
            }
            
        } else {
            log.info("requestGeometryUpdate22")
            // iOS 15 及以下版本的处理方式
            if let singleOrientation = orientation.toSingleOrientation() {
                UIDevice.current.setValue(singleOrientation.rawValue, forKey: "orientation")
            }
            UIViewController.attemptRotationToDeviceOrientation()
            
            DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
                self.isOrientationLocked = true
                self.lockedOrientation = orientation
                log.info("屏幕方向已锁定")
                UIViewController.attemptRotationToDeviceOrientation() // 添加这行
            }
        }
    }
}


    bridge.registerHandler("lockOrientation") {[weak self] data, callback in
      
      
      guard let orientationString = data as? String else {
        log.error("webviewBridge call lockOrientation with invalid data")
        callback?(nil)
        return
      }

      log.info("webviewBridge call lockOrientation: \(orientationString)")
      
      let orientation: UIInterfaceOrientationMask
      if orientationString == "horizontal" {
        orientation = .landscape
      } else if orientationString == "vertical" {
        orientation = .portrait
      } else {
        log.error("webviewBridge call lockOrientation with invalid orientation")
        callback?(nil)
        return
      }
      
      DispatchQueue.main.async {
        self?.rotateAndLockOrientation(orientation)
      }
      callback?(nil)
    }
    
    bridge.registerHandler("unlockOrientation") {[weak self] data, callback in
      log.info("webviewBridge call unlockOrientation")
      DispatchQueue.main.async {
        self?.unlockOrientation()
      }
      callback?(nil)
    }