![](/img/trans.png)
[英]make only video player Landscape in uiviewcontroller on rotation ios swift
[英]iOS16+ Present UIViewController in landscape only for single screen not working [Swift 5.7]
在 iOS 之前 16 橫向呈現單個屏幕適合縱向應用。 工作代碼如下。
備注:整個應用程序僅處於縱向模式。
override public var shouldAutorotate: Bool {
return false
}
override public var supportedInterfaceOrientations: UIInterfaceOrientationMask {
return .landscapeLeft
}
override public var preferredInterfaceOrientationForPresentation: UIInterfaceOrientation {
return .landscapeLeft
}
我找到了解決方案,但它適用於UIWindowScene ,但我需要 UIWindow 中的解決方案。 我需要幫助在 iOS 16 中修復它。
Xcode - 14.0, iOS - 16.0, 模擬器 - 14 Pro
如果有人需要,我可以准備演示。
我在 iOS 16 Release Notes 中找到了一些相關的東西。
https://developer.apple.com/documentation/ios-ipados-release-notes/ios-16-release-notes?changes=lat__8_1
UIKit 中有一些棄用:
棄用
[UIViewController shouldAutorotate] 已棄用,不再支持。 [UIViewController attemptRotationToDeviceOrientation] 已被棄用並替換為 [UIViewController setNeedsUpdateOfSupportedInterfaceOrientations]。
解決方法:依賴 shouldAutorotate 的應用程序應該使用視圖控制器supportedInterfaceOrientations 反映他們的偏好。 如果支持的方向發生變化,請使用 `-[UIViewController setNeedsUpdateOfSupportedInterface
我認為您可能必須使用setNeedsUpdateOfSupportedInterface 。
這可以通過兩種方式完成。
1.我個人更喜歡這種方式
1.1 在 AppDelegate 中保留這個 function 來處理方向(這是必須的)
func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
return .all
}
1.2 您希望在哪個 ViewController 中強制方向,go 到該視圖 controller 並將這些行添加到變量聲明部分
var forceLandscape: Bool = false
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
forceLandscape ? .landscape : .portrait
}
我們將更新forceLandscape使其得到更新,然后supportedInterfaceOrientations也將得到更新
1.3 這里我們設置了更新forceLandscape的觸發器(我們可以在按鈕動作中添加這幾行代碼來處理IOS 16 force roatation)
if #available(iOS 16.0, *) {
self.forceLandscape = true
guard let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene else { return }
self.setNeedsUpdateOfSupportedInterfaceOrientations()
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2, execute: {
windowScene.requestGeometryUpdate(.iOS(interfaceOrientations: .landscapeRight)){
error in
print(error)
print(windowScene.effectiveGeometry)
}
})
這將更新forceLandscape ,因此它將檢查方向並根據它進行更新
上面的代碼行是一種方式,下面給出另一種方式
2. 另一種方法是在 AppDelegate class 中更新方向:
2.1 保留這個 function 和 AppDelegate 中的屬性來處理方向(這是必須的)
var orientation : UIInterfaceOrientationMask = .portrait
func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
return orientation
}
2.2 在 viewcontroller 按鈕動作中我們可以更新屬性
@IBAction func buttonAction(_ sender: Any) {
let appDel = UIApplication.shared.delegate as! AppDelegate
appDel.orientation = .landscape
if #available(iOS 16.0, *) {
DispatchQueue.main.async {
let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene
self.setNeedsUpdateOfSupportedInterfaceOrientations()
self.navigationController?.setNeedsUpdateOfSupportedInterfaceOrientations()
windowScene?.requestGeometryUpdate(.iOS(interfaceOrientations: .landscape)) { error in
print(error)
print(windowScene?.effectiveGeometry ?? "")
}
}
}else{
UIDevice.current.setValue(UIInterfaceOrientation.landscapeLeft.rawValue, forKey: "orientation")
}
我設法從橫向中縱向呈現模態視圖 controller 的最佳解決方法是setNeedsUpdateOfSupportedInterfaceOrientations()
, requestGeometryUpdate(.iOS(interfaceOrientations: .landscape))
和新 Z05B8C74CBD96FBF2DE4C1A352F4 上 AppDelegate 上允許的界面方向的組合
var allowedOrientation: UIInterfaceOrientationMask = .allButUpsideDown
func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
return allowedOrientation
}
var overlayWindow: UIWindow? // New window to present the controller in
…
func presentModalInLandscape(vc: ViewController) {
if #available(iOS 16.0, *) {
let appdelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.allowedOrientation = .landscapeRight
if let currentWindowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene {
overlayWindow = UIWindow(windowScene: currentWindowScene)
}
overlayWindow?.windowLevel = UIWindow.Level.alert
overlayWindow?.rootViewController = livevc
overlayWindow?.makeKeyAndVisible()
// It's important to do it after the interface has enough time to rotate
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
self.overlayWindow?.windowScene?.requestGeometryUpdate(.iOS(interfaceOrientations: .landscapeRight))
vc.setNeedsUpdateOfSupportedInterfaceOrientations()
}
} else {
// For iOS 15 it's enough to present it modally
self.present(vc, animated: true, completion: nil)
}
}
然后當你想解雇它時,你需要
if #available(iOS 16.0, *) {
self.overlayWindow?.isHidden = true // Destroy the window
self.overlayWindow?.windowScene = nil
self.overlayWindow = nil
appDelegate().allowedOrientation = .allButUpsideDown // Reset allowed orientation
self.setNeedsUpdateOfSupportedInterfaceOrientations() // Set the controller back
} else {
self.presentedViewController?.dismiss(animated: true)
}
它仍然不是 100%,因為視圖 controller 以橫向呈現,然后輕彈回縱向,然后在一秒鍾后再次旋轉到橫向。 但如果沒有 UIWindow,它有時會在鎖定橫向模式之前執行 2 倍。
經過多次嘗試,我想出了一個簡單的解決方案。 正如我在問題中提到的,我的整個應用程序僅處於縱向模式,並且我只想在橫向模式下顯示一個屏幕。
此代碼不需要任何外部 window 即可成為makeKeyAndVisible
。 如果您使用額外的 window 出示,則您需要單獨寫信解散 iOS 16。
在以前版本的 iOS 16 中工作的舊代碼將保持不變,沒有任何變化。
魔術線如下。
func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
if let _ = window?.rootViewController?.presentedViewController as? LandscapeChartVC {
if #available(iOS 16.0, *) {
return .landscapeLeft
} else {
return .portrait
}
} else {
return .portrait
}
}
我在 appDelegate 的supportedInterfaceOrientationsFor
中確定了我的橫向視圖 controller。
好吧,您可以更改 word presentedViewController
以獲取您的 controller。僅此而已。
使用 iPad 添加對所有 3 或 4 方向的支持:
func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
if UIDevice.IS_IPAD {
return .allButUpsideDown
} else {
if let _ = window?.rootViewController?.presentedViewController as? LandscapeChartVC {
if #available(iOS 16.0, *) {
return .landscapeLeft
} else {
return .portrait
}
} else {
return .portrait
}
}
如果要求 iPad 應用程序鎖定方向,您可以按照 iPhone/上面的代碼。
這個想法提出了答案,並感謝所有有興趣的人。 如果有人仍然得到更多改進的解決方案,我會很樂意更新。
我要鎖定的ViewController
位於UINaviationController
內。 對於這種情況,這是我的工作解決方案
我有這個struct
,它有一個lock
和unlock
方法。
struct AppOrientation {
// statusBarOrientation
// 0 - unknown
// 1 - portrait
// 2 - portraitUpsideDown
// 3 - landscapeLeft
// 4 - landscapeRight
static let ORIENTATION_KEY: String = "orientation"
private static func lockInterfaceOrientation(_ orientation: UIInterfaceOrientationMask) {
if let delegate = UIApplication.shared.delegate as? AppDelegate {
delegate.orientationLock = orientation
}
}
// Important
// Notice that UIDeviceOrientation.landscapeRight is assigned to UIInterfaceOrientation.landscapeLeft
// and UIDeviceOrientation.landscapeLeft is assigned to UIInterfaceOrientation.landscapeRight.
// The reason for this is that rotating the device requires rotating the content in the opposite direction.
// Note that UIInterfaceOrientationLandscapeLeft is equal to UIDeviceOrientationLandscapeRight (and vice versa).
// This is because rotating the device to the left requires rotating the content to the right.
// DevieOrientation
// 0 - unknown
// 1 - portrait
// 2 - portraitUpsideDown
// 3 - landscapeLeft
// 4 - landscapeRight
// 5 - faceUp
// 6 - faceDown
// UIInterfaceOrientation
// - landscapeLeft:
// -- Home button on the left
// - landscapeRight:
// -- Home button on the right
// UIDevice orientation
// - landscapeLeft:
// -- home button on the right
// - landscapeRight:
// -- home button on the left
static func lockDeviceToLandscapeLeft() {
if #available(iOS 16.0, *) {
let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene
if let controller = windowScene?.keyWindow?.rootViewController as? RMNavigationController {
controller.lockLandscape = true
}
} else {
UIDevice.current.setValue(UIDeviceOrientation.landscapeLeft.rawValue, forKey: ORIENTATION_KEY)
lockInterfaceOrientation(.landscapeRight)
}
// the docs say you should call 'attemptRorationToDeviceOrientation()
// lots of StackOverflow answers don't use it,
// but a couple say you _must_ call it.
// for me, not calling it seems to work...
// UINavigationController.attemptRotationToDeviceOrientation()
}
static func unlockOrientation() {
if #available(iOS 16.0, *) {
let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene
if let controller = windowScene?.keyWindow?.rootViewController as? RMNavigationController {
controller.lockLandscape = false
}
} else {
lockInterfaceOrientation(.all)
}
}
}
在 ios16 之前你只需要調用這兩個方法。
從 ios16 開始,您現在還需要調用setNeedsUpdateOfSupportedInterfaceOrientations()
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
AppOrientation.lockDeviceToLandscapeLeft()
if #available(iOS 16.0, *) {
self.setNeedsUpdateOfSupportedInterfaceOrientations()
} else {
// Fallback on earlier versions
}
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
AppOrientation.unlockOrientation()
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.