我正在尋找一種無需旋轉游戲視圖即可旋轉 iOS UI 的方法。
我正在開發的游戲支持多個方向,但游戲視圖負責旋轉和與該方向更改相關的所有影片,并且應該始終將其渲染為縱向。
目前,我將應用程式鎖定為僅支持縱向,并且當手機旋轉時,游戲也會旋轉。但是主欄和控制中心保持縱向,這還可以,但我希望該應用程式完全支持橫向。
就像現在一樣,我收聽方向通知
func ListenToRotationUpdates() {
UIDevice.current.beginGeneratingDeviceOrientationNotifications()
NotificationCenter.default.addObserver(
self,
selector: #selector(deviceOrientationDidChange),
name: UIDevice.orientationDidChangeNotification,
object: nil
)
}
這將在游戲中設定方向
@objc func deviceOrientationDidChange() {
let orientation = UIDevice.current.orientation
if orientation == .unknown || orientation == .faceUp || orientation == .faceDown {
return
}
self.gameScene.Set(orientation: orientation)
setNeedsStatusBarAppearanceUpdate()
}
有沒有辦法在應用程式正常旋轉作業系統時保持視圖肖像?
uj5u.com熱心網友回復:
那么這取決于如何旋轉游戲視圖。您是否使用一些自行處理的外部庫?您是否重新計算視圖框架和邊界?因此,您的想法的最終執行實際上取決于這些因素。通常,處理旋轉的最常見方法UIViewController是在viewWillTransition函式中,它處理所有旋轉影片對齊并同時發生。因此,如果您想以某種方式將視圖重新計算回縱向尺寸,我建議使用此功能。這是一個例子:
override func viewWillTransition(to size: CGSize,
with coordinator: UIViewControllerTransitionCoordinator)
{
coordinator.animate( alongsideTransition: { (UIViewControllerTransitionCoordinatorContext) -> Void in
// calculate your view e.g. self.rotateToCurrentPosition()
}, completion: { (UIViewControllerTransitionCoordinatorContext) -> Void in
// e.g. self.view.layoutSubviews()
})
super.viewWillTransition(to: size, with: coordinator)
}
uj5u.com熱心網友回復:
一種可能的解決方案是使用 windows 將您的應用程式拆分為“旋轉”部分和“非旋轉”部分。
這不是一個理想的選擇,但最近我們獲得的工具并沒有給我們太多選擇。使用此程序時您面臨的問題是在呈現新視圖控制器時可能會出現一些混亂。在您的情況下,這可能根本不是問題,但仍然......
簡而言之,您要做的是:
- 保持主視窗不變,但使您的應用程式可以旋轉到您需要的所有方向
- 創建一個僅支持一個方向(無論您喜歡哪個方向)的視圖控制器,并在您的主視窗上方但在狀態欄下方顯示一個新視窗(默認行為)
- 創建一個具有透明背景的視圖控制器,它可以將觸摸事件傳遞到它下面的視窗。還要在前一個視窗的新視窗中顯示它。此視窗還需要將觸摸事件傳遞到底部視窗。
您可以在代碼中或使用故事板創建所有這些。但是有一些組件需要管理。這些都是我在驗證這種方法時使用的:
class StandStillViewController: WindowViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
func setupManually() {
self.view = DelegatedTouchEventsView()
self.view.backgroundColor = UIColor.darkGray
let label = UILabel(frame: .zero)
label.text = "A part of this app that stands still"
label.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(label)
view.addConstraint(.init(item: label, attribute: .centerX, relatedBy: .equal, toItem: view, attribute: .centerX, multiplier: 1.0, constant: 0.0))
view.addConstraint(.init(item: label, attribute: .centerY, relatedBy: .equal, toItem: view, attribute: .centerY, multiplier: 1.0, constant: 0.0))
let button = UIButton(frame: .zero)
button.setTitle("Test button", for: .normal)
button.translatesAutoresizingMaskIntoConstraints = false
button.addTarget(self, action: #selector(testButton), for: .touchUpInside)
view.addSubview(button)
view.addConstraint(.init(item: button, attribute: .centerX, relatedBy: .equal, toItem: view, attribute: .centerX, multiplier: 1.0, constant: 0.0))
view.addConstraint(.init(item: button, attribute: .centerY, relatedBy: .equal, toItem: view, attribute: .centerY, multiplier: 1.0, constant: 50.0))
}
override var supportedInterfaceOrientations: UIInterfaceOrientationMask { return .portrait }
override var shouldAutorotate: Bool { false }
override var preferredInterfaceOrientationForPresentation: UIInterfaceOrientation { return .portrait }
@IBAction private func testButton() {
print("Button was pressed")
}
}
這是底部的控制器。我希望這個會主持你的游戲。你需要保存
override var supportedInterfaceOrientations: UIInterfaceOrientationMask { return .portrait }
override var shouldAutorotate: Bool { false }
override var preferredInterfaceOrientationForPresentation: UIInterfaceOrientation { return .portrait }
其余的可能會更改,洗掉。
class RotatingViewController: WindowViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
func setupManually() {
self.view = DelegatedTouchEventsView()
self.view.backgroundColor = UIColor.clear // Want to see through it
let label = UILabel(frame: .zero)
label.text = "A Rotating part of this app"
label.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(label)
view.addConstraint(.init(item: label, attribute: .centerX, relatedBy: .equal, toItem: view, attribute: .centerX, multiplier: 1.0, constant: 0.0))
view.addConstraint(.init(item: label, attribute: .centerY, relatedBy: .equal, toItem: view, attribute: .top, multiplier: 1.0, constant: 60.0))
let button = UIButton(frame: .zero)
button.setTitle("Test rotating button", for: .normal)
button.translatesAutoresizingMaskIntoConstraints = false
button.addTarget(self, action: #selector(testButton), for: .touchUpInside)
view.addSubview(button)
view.addConstraint(.init(item: button, attribute: .centerX, relatedBy: .equal, toItem: view, attribute: .centerX, multiplier: 1.0, constant: 0.0))
view.addConstraint(.init(item: button, attribute: .centerY, relatedBy: .equal, toItem: view, attribute: .centerY, multiplier: 1.0, constant: -50.0))
}
@IBAction private func testButton() {
print("Rotating button was pressed")
}
}
這個控制器將處理旋轉的東西。你需要保存
self.view = DelegatedTouchEventsView()
self.view.backgroundColor = UIColor.clear // Want to see through it
也可以在情節提要中設定。你放在這個控制器上的任何東西都會隨著你的設備旋轉。例如,放置一些 GUI 內容的好地方。
class WindowViewController: UIViewController {
private var window: UIWindow?
func shownInNewWindow(delegatesTouchEvents: Bool, baseWindow: UIWindow? = nil) {
let scene = baseWindow?.windowScene ?? UIApplication.shared.windows.first!.windowScene!
let newWindow: UIWindow
if delegatesTouchEvents {
newWindow = DelegatedTouchEventsWindow(windowScene: scene)
} else {
newWindow = UIWindow(windowScene: scene)
}
newWindow.rootViewController = self
newWindow.windowLevel = .normal
newWindow.makeKeyAndVisible()
self.window = newWindow
}
func dismissFromWindow(completion: (() -> Void)? = nil) {
removeFromWindow()
completion?()
}
func removeFromWindow() {
self.window?.isHidden = true
self.window = nil
}
}
This is what I used as base class for both view controllers above. It is not much but it allows view controllers to be shown in a new window. This code was pasted from one of my older projects and could use some minor improvements. But it does works so...
class DelegatedTouchEventsView: UIView {
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
let view = super.hitTest(point, with: event)
return view == self ? nil : view
}
}
class DelegatedTouchEventsWindow: UIWindow {
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
let view = super.hitTest(point, with: event)
return view == self ? nil : view
}
}
這是讓觸摸事件通過的兩個子類。關于其作業原理的快速解釋:當收到事件時,您的系統將首先通過您的視圖層次結構發送此事件,詢問“誰將處理此事件?”。回傳nil意味著“不是我”,默認為UIViewis self。所以在這段代碼中我們說:“如果我的任何子視圖想要處理這個事件(例如按鈕),那么它可能會處理這個事件。但如果他們都不想處理它們,那么我也不會。” 并且UIWindow是一個子類,UIView所以我們需要同時處理它們。
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// let standingStillController = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "StandStillViewController") as! StandStillViewController
// standingStillController.shownInNewWindow(delegatesTouchEvents: false, baseWindow: self.view.window)
//
// let rotatingViewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "RotatingViewController") as! RotatingViewController
// rotatingViewController.shownInNewWindow(delegatesTouchEvents: true, baseWindow: self.view.window)
let standingStillController = StandStillViewController()
standingStillController.setupManually()
standingStillController.shownInNewWindow(delegatesTouchEvents: false, baseWindow: self.view.window)
let rotatingViewController = RotatingViewController()
rotatingViewController.setupManually()
rotatingViewController.shownInNewWindow(delegatesTouchEvents: true, baseWindow: self.view.window)
}
}
這是一個如何一起使用它的示例。正如所承諾的,無論是使用故事板還是手動,兩者都應該作業。
看起來作業量很大,但您需要設定一次,然后再也不看它。
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/408272.html
標籤:
上一篇:Flutter-回到上一頁
