Ponieważ moja aplikacja obsługuje wszystkie orientacje. Chciałbym zablokować tylko tryb portretowy do określonego UIViewController.
np. załóżmy, że była to aplikacja z zakładkami, a gdy widok logowania pojawia się modalnie, chcę, aby widok logowania był tylko w trybie pionowym, bez względu na to, jak użytkownik obraca urządzenie lub jak będzie aktualna orientacja urządzenia
swift
uiviewcontroller
portrait
device-orientation
Thiha Aung
źródło
źródło
Odpowiedzi:
Sytuacja może być dość skomplikowana, gdy masz skomplikowaną hierarchię widoków, na przykład wiele kontrolerów nawigacji i / lub kontrolerów widoku kart.
Ta implementacja umieszcza je na poszczególnych kontrolerach widoku, aby ustawić, kiedy chcą zablokować orientacje, zamiast polegać na delegacie aplikacji, aby znaleźć je przez iterację przez podglądy.
Swift 3, 4, 5
W AppDelegate:
/// set orientations you want to be allowed in this property by default var orientationLock = UIInterfaceOrientationMask.all func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask { return self.orientationLock }
W innej globalnej klasie strukturalnej lub pomocniczej utworzyłem tutaj AppUtility:
struct AppUtility { static func lockOrientation(_ orientation: UIInterfaceOrientationMask) { if let delegate = UIApplication.shared.delegate as? AppDelegate { delegate.orientationLock = orientation } } /// OPTIONAL Added method to adjust lock and rotate to the desired orientation static func lockOrientation(_ orientation: UIInterfaceOrientationMask, andRotateTo rotateOrientation:UIInterfaceOrientation) { self.lockOrientation(orientation) UIDevice.current.setValue(rotateOrientation.rawValue, forKey: "orientation") UINavigationController.attemptRotationToDeviceOrientation() } }
Następnie w żądanym kontrolerze ViewController, który chcesz zablokować orientacje:
override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) AppUtility.lockOrientation(.portrait) // Or to rotate and lock // AppUtility.lockOrientation(.portrait, andRotateTo: .portrait) } override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) // Don't forget to reset when view is being removed AppUtility.lockOrientation(.all) }
Jeśli iPad lub aplikacja uniwersalna
Upewnij się, że opcja „Wymaga pełnego ekranu” jest zaznaczona w Ustawieniach celu -> Ogólne -> Informacje o wdrożeniu.
supportedInterfaceOrientationsFor
delegat nie zostanie wywołany, jeśli nie jest to zaznaczone.źródło
Requires full screen
zaznaczysz, uniemożliwi to dostęp do aplikacji do przesuwania i dzielenia widoku. Zobacz: Stosowanie ulepszeń wielozadaniowości na iPadzie autorstwa Apple. Mam odpowiedź, która nie wymaga włączeniaRequires full screen
Szybki 4
AppDelegate
var orientationLock = UIInterfaceOrientationMask.all func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask { return self.orientationLock } struct AppUtility { static func lockOrientation(_ orientation: UIInterfaceOrientationMask) { if let delegate = UIApplication.shared.delegate as? AppDelegate { delegate.orientationLock = orientation } } static func lockOrientation(_ orientation: UIInterfaceOrientationMask, andRotateTo rotateOrientation:UIInterfaceOrientation) { self.lockOrientation(orientation) UIDevice.current.setValue(rotateOrientation.rawValue, forKey: "orientation") } }
Twój ViewController Dodaj następującą linię, jeśli potrzebujesz tylko orientacji pionowej. musisz zastosować to do wszystkich ViewController, które muszą wyświetlać tryb pionowy.
override func viewWillAppear(_ animated: Bool) { AppDelegate.AppUtility.lockOrientation(UIInterfaceOrientationMask.portrait, andRotateTo: UIInterfaceOrientation.portrait) }
a to spowoduje orientację ekranu dla innych Viewcontroller zgodnie z fizyczną orientacją urządzenia.
override func viewWillDisappear(_ animated: Bool) { AppDelegate.AppUtility.lockOrientation(UIInterfaceOrientationMask.all) }
źródło
func application
w aplikacji delegata tą funkcjąfunc application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask { return self.orientationLock ?
Swift 3 i 4
Ustaw
supportedInterfaceOrientations
właściwość określonych UIViewControllers w następujący sposób:class MyViewController: UIViewController { var orientations = UIInterfaceOrientationMask.portrait //or what orientation you want override var supportedInterfaceOrientations : UIInterfaceOrientationMask { get { return self.orientations } set { self.orientations = newValue } } override func viewDidLoad() { super.viewDidLoad() } //... }
AKTUALIZACJA
To rozwiązanie działa tylko wtedy, gdy nie
viewController
jest osadzone w , ponieważ orientacja dziedziczy z elementu nadrzędnego viewController. W tym przypadku można utworzyć podklasę i ustawić w niej te właściwości.UINavigationController
UINavigationViewController
źródło
Dodaj ten kod, aby wymusić portret i zablokować go:
override func viewDidLoad() { super.viewDidLoad() // Force the device in portrait mode when the view controller gets loaded UIDevice.currentDevice().setValue(UIInterfaceOrientation.Portrait.rawValue, forKey: "orientation") } override func shouldAutorotate() -> Bool { // Lock autorotate return false } override func supportedInterfaceOrientations() -> Int { // Only allow Portrait return Int(UIInterfaceOrientationMask.Portrait.rawValue) } override func preferredInterfaceOrientationForPresentation() -> UIInterfaceOrientation { // Only allow Portrait return UIInterfaceOrientation.Portrait }
W AppDelegate - ustaw supportedInterfaceOrientationsForWindow na dowolne orientacje, które ma obsługiwać cała aplikacja:
func application(application: UIApplication, supportedInterfaceOrientationsForWindow window: UIWindow?) -> UIInterfaceOrientationMask { return UIInterfaceOrientationMask.All }
źródło
To jest ogólne rozwiązanie Twojego problemu i innych powiązanych problemów.
1. Utwórz pomocniczą klasę UIHelper i użyj następujących metod:
/**This method returns top view controller in application */ class func topViewController() -> UIViewController? { let helper = UIHelper() return helper.topViewControllerWithRootViewController(rootViewController: UIApplication.shared.keyWindow?.rootViewController) } /**This is a recursive method to select the top View Controller in a app, either with TabBarController or not */ private func topViewControllerWithRootViewController(rootViewController:UIViewController?) -> UIViewController? { if(rootViewController != nil) { // UITabBarController if let tabBarController = rootViewController as? UITabBarController, let selectedViewController = tabBarController.selectedViewController { return self.topViewControllerWithRootViewController(rootViewController: selectedViewController) } // UINavigationController if let navigationController = rootViewController as? UINavigationController ,let visibleViewController = navigationController.visibleViewController { return self.topViewControllerWithRootViewController(rootViewController: visibleViewController) } if ((rootViewController!.presentedViewController) != nil) { let presentedViewController = rootViewController!.presentedViewController; return self.topViewControllerWithRootViewController(rootViewController: presentedViewController!); }else { return rootViewController } } return nil }
2. Stwórz protokół ze swoim zachowaniem pożądania, dla twojego konkretnego przypadku będzie portret.
protokół orientacjiIsOnlyPortrait {}
Uwaga: jeśli chcesz, dodaj go na początku klasy UIHelper.
3. Rozszerz swój kontroler widoku
W Twoim przypadku:
class Any_ViewController: UIViewController,orientationIsOnlyPortrait { .... }
4. W klasie delegatów aplikacji dodaj tę metodę:
func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask { let presentedViewController = UIHelper.topViewController() if presentedViewController is orientationIsOnlyPortrait { return .portrait } return .all }
Uwagi końcowe:
źródło
W przypadku nowej wersji Swift spróbuj tego
override var shouldAutorotate: Bool { return false } override var supportedInterfaceOrientations: UIInterfaceOrientationMask { return UIInterfaceOrientationMask.portrait } override var preferredInterfaceOrientationForPresentation: UIInterfaceOrientation { return UIInterfaceOrientation.portrait }
źródło
Kilka świetnych odpowiedzi w tym wątku, ale żadna nie odpowiadała moim potrzebom. Mam aplikację z zakładkami z kontrolerami nawigacyjnymi na każdej karcie i jeden widok wymagał obrócenia, podczas gdy inne musiały być zablokowane w pionie. Z jakiegoś powodu kontroler nawigacji nie zmienił poprawnie rozmiaru podglądów podrzędnych. Znalazłem rozwiązanie (w Swift 3), łącząc się z tą odpowiedzią, a problemy z układem zniknęły. Utwórz strukturę zgodnie z sugestią @bmjohns:
import UIKit struct OrientationLock { static func lock(to orientation: UIInterfaceOrientationMask) { if let delegate = UIApplication.shared.delegate as? AppDelegate { delegate.orientationLock = orientation } } static func lock(to orientation: UIInterfaceOrientationMask, andRotateTo rotateOrientation: UIInterfaceOrientation) { self.lock(to: orientation) UIDevice.current.setValue(rotateOrientation.rawValue, forKey: "orientation") } }
Następnie podklasa UITabBarController:
import UIKit class TabBarController: UITabBarController, UITabBarControllerDelegate { required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) self.delegate = self } func tabBarControllerSupportedInterfaceOrientations(_ tabBarController: UITabBarController) -> UIInterfaceOrientationMask { if tabBarController.selectedViewController is MyViewControllerNotInANavigationControllerThatShouldRotate { return .allButUpsideDown } else if let navController = tabBarController.selectedViewController as? UINavigationController, navController.topViewController is MyViewControllerInANavControllerThatShouldRotate { return .allButUpsideDown } else { //Lock view that should not be able to rotate return .portrait } } func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool { if viewController is MyViewControllerNotInANavigationControllerThatShouldRotate { OrientationLock.lock(to: .allButUpsideDown) } else if let navController = viewController as? UINavigationController, navController.topViewController is MyViewControllerInANavigationControllerThatShouldRotate { OrientationLock.lock(to: .allButUpsideDown) } else { //Lock orientation and rotate to desired orientation OrientationLock.lock(to: .portrait, andRotateTo: .portrait) } return true } }
Nie zapomnij zmienić klasy TabBarController w scenorysie na nowo utworzoną podklasę.
źródło
Oto prosty sposób, który działa dla mnie ze Swift 4.2 (iOS 12.2), umieść to w,
UIViewController
dla którego chcesz wyłączyć shouldAutorotate:override var supportedInterfaceOrientations: UIInterfaceOrientationMask { return .portrait }
.portrait
Część informuje go, w którym orientacja (s) pozostanie, można zmienić to jak chcesz. Do wyboru są:.portrait
,.all
,.allButUpsideDown
,.landscape
,.landscapeLeft
,.landscapeRight
,.portraitUpsideDown
.źródło
Aby ustawić orientację poziomą dla wszystkich widoków aplikacji i zezwolić tylko na jeden widok dla wszystkich orientacji (na przykład w celu dodania rolki z aparatu):
W AppDelegate.swift:
var adaptOrientation = false
W: didFinishLaunchingWithOptions
NSNotificationCenter.defaultCenter().addObserver(self, selector: "adaptOrientationAction:", name:"adaptOrientationAction", object: nil)
W innym miejscu w AppDelegate.swift:
func application(application: UIApplication, supportedInterfaceOrientationsForWindow window: UIWindow?) -> Int { return checkOrientation(self.window?.rootViewController) } func checkOrientation(viewController:UIViewController?)-> Int{ if (adaptOrientation == false){ return Int(UIInterfaceOrientationMask.Landscape.rawValue) }else { return Int(UIInterfaceOrientationMask.All.rawValue) } } func adaptOrientationAction(notification: NSNotification){ if adaptOrientation == false { adaptOrientation = true }else { adaptOrientation = false } }
Następnie w widoku, który idzie do tego, który chcesz mieć wszystkie orientacje:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) { if (segue.identifier == "YOURSEGUE") { NSNotificationCenter.defaultCenter().postNotificationName("adaptOrientationAction", object: nil) } } override func viewWillAppear(animated: Bool) { if adaptOrientation == true { NSNotificationCenter.defaultCenter().postNotificationName("adaptOrientationAction", object: nil) } }
Ostatnią rzeczą jest zaznaczenie Orientacja urządzenia: - Pionowa - Pozioma lewa - Pozioma prawa
źródło
Utwórz nowe rozszerzenie za pomocą
import UIKit extension UINavigationController { override open var supportedInterfaceOrientations: UIInterfaceOrientationMask { return .portrait } } extension UITabBarController { override open var supportedInterfaceOrientations: UIInterfaceOrientationMask { return .portrait } }
źródło
bmjohns -> Jesteś moim zbawcą życia. To jedyne działające rozwiązanie (ze strukturą AppUtility)
Stworzyłem tę klasę:
class Helper{ struct AppUtility { static func lockOrientation(_ orientation: UIInterfaceOrientationMask) { if let delegate = UIApplication.shared.delegate as? AppDelegate { delegate.orientationLock = orientation } } /// OPTIONAL Added method to adjust lock and rotate to the desired orientation static func lockOrientation(_ orientation: UIInterfaceOrientationMask, andRotateTo rotateOrientation:UIInterfaceOrientation) { self.lockOrientation(orientation) UIDevice.current.setValue(rotateOrientation.rawValue, forKey: "orientation") } } }
i postępuj zgodnie z instrukcjami, a wszystko działa idealnie dla Swift 3 -> xcode w wersji 8.2.1
źródło
Od iOS 10 i 11 iPad obsługuje Slide Over i Split View. Aby włączyć aplikację w widoku Slide Over i Split View,
Requires full screen
należy usunąć zaznaczenie. Oznacza to, że zaakceptowanej odpowiedzi nie można użyć, jeśli aplikacja chce obsługiwać Slide Over i Split View. Zobacz więcej na temat wdrażania ulepszeń wielozadaniowości firmy Apple na iPadzie tutaj .Mam rozwiązanie, które umożliwia (1) odznaczenie
Requires full screen
, (2) tylko jedną funkcję do zaimplementowaniaappDelegate
(szczególnie jeśli nie chcesz / nie możesz modyfikować kontrolerów widoku docelowego) i (3) unikanie wywołań rekurencyjnych. Nie potrzeba klasy pomocniczej ani rozszerzeń.appDelegate.swift (Swift 4)
func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask { // Search for the visible view controller var vc = window?.rootViewController // Dig through tab bar and navigation, regardless their order while (vc is UITabBarController) || (vc is UINavigationController) { if let c = vc as? UINavigationController { vc = c.topViewController } else if let c = vc as? UITabBarController { vc = c.selectedViewController } } // Look for model view controller while (vc?.presentedViewController) != nil { vc = vc!.presentedViewController } print("vc = " + (vc != nil ? String(describing: type(of: vc!)) : "nil")) // Final check if it's our target class. Also make sure it isn't exiting. // Otherwise, system will mistakenly rotate the presentingViewController. if (vc is TargetViewController) && !(vc!.isBeingDismissed) { return [.portrait] } return [.all] }
Edytować
@bmjohns zwrócił uwagę, że ta funkcja nie jest wywoływana na iPadzie. Zweryfikowałem i tak, nie został wywołany. Zrobiłem więc trochę więcej testów i odkryłem kilka faktów:
Requires full screen
ponieważ chcę włączyć Slide Over i Slide View na iPadzie. Wymaga aplikację do obsługi wszystkich orientacji 4 dla iPada, w Info.plist:Supported interface orientations (iPad)
.Moja aplikacja działa tak samo jak Facebook: na iPhonie przez większość czasu jest zablokowana do portretu. Podczas oglądania obrazu na pełnym ekranie, umożliwia obracanie obrazu w poziomie dla lepszego widoku. Na iPadzie użytkownicy mogą obracać się do dowolnej orientacji w dowolnych kontrolerach widoku. Tak więc aplikacja wygląda ładnie, gdy iPad stoi na Smart Cover (poziomo po lewej).
Aby dzwonić z iPada
application(_:supportedInterfaceOrientationsFor)
, w Info.plist zachowaj portret tylko dla iPada. Aplikacja utraci funkcję Slide Over + Split View. Ale możesz zablokować lub odblokować orientację dla dowolnego kontrolera widoku, w jednym miejscu i bez konieczności modyfikowania klasy ViewController.Wreszcie ta funkcja jest wywoływana w cyklu życia kontrolera widoku, gdy widok jest wyświetlany / usuwany. Jeśli Twoja aplikacja będzie musiała zablokować / odblokować / zmienić orientację w innym czasie, może nie działać
źródło
Rzeczywiste przetestowane rozwiązanie do tego.W moim przykładzie potrzebuję, aby cała moja aplikacja była w trybie pionowym, ale orientacja tylko jednego ekranu powinna być w trybie poziomym.
Kod w AppDelegate, jak opisano powyżej.
var orientationLock = UIInterfaceOrientationMask.all func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask { return self.orientationLock } struct AppUtility { static func lockOrientation(_ orientation: UIInterfaceOrientationMask) { if let delegate = UIApplication.shared.delegate as? AppDelegate { delegate.orientationLock = orientation } } static func lockOrientation(_ orientation: UIInterfaceOrientationMask, andRotateTo rotateOrientation:UIInterfaceOrientation) { self.lockOrientation(orientation) UIDevice.current.setValue(rotateOrientation.rawValue, forKey: "orientation") } }
Następnie zapisz ten kod, zanim Twój kontroler widoku w orientacji poziomej zostanie przedstawiony / push.
override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) AppDelegate.AppUtility.lockOrientation(UIInterfaceOrientationMask.portrait, andRotateTo: UIInterfaceOrientation.portrait) }
Następnie zapisz ten kod w rzeczywistym kontrolerze widoku (dla widoku poziomego)
override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) AppDelegate.AppUtility.lockOrientation(UIInterfaceOrientationMask.landscape, andRotateTo: UIInterfaceOrientation.landscape) }
źródło
Dzięki powyższej odpowiedzi @ bmjohn. Oto działająca wersja Xamarin / C # kodu tej odpowiedzi, aby zaoszczędzić innym czas transkrypcji:
AppDelegate.cs
public UIInterfaceOrientationMask OrientationLock = UIInterfaceOrientationMask.All; public override UIInterfaceOrientationMask GetSupportedInterfaceOrientations(UIApplication application, UIWindow forWindow) { return this.OrientationLock; }
Klasa Static OrientationUtility.cs:
public static class OrientationUtility { public static void LockOrientation(UIInterfaceOrientationMask orientation) { var appdelegate = (AppDelegate) UIApplication.SharedApplication.Delegate; if(appdelegate != null) { appdelegate.OrientationLock = orientation; } } public static void LockOrientation(UIInterfaceOrientationMask orientation, UIInterfaceOrientation RotateToOrientation) { LockOrientation(orientation); UIDevice.CurrentDevice.SetValueForKey(new NSNumber((int)RotateToOrientation), new NSString("orientation")); } }
Wyświetl kontroler:
public override void ViewDidAppear(bool animated) { base.ViewWillAppear(animated); OrientationUtility.LockOrientation(UIInterfaceOrientationMask.Portrait, UIInterfaceOrientation.Portrait); } public override void ViewWillDisappear(bool animated) { base.ViewWillDisappear(animated); OrientationUtility.LockOrientation(UIInterfaceOrientationMask.All); }
źródło
Trochę poeksperymentowałem i udało mi się znaleźć czyste rozwiązanie tego problemu. Podejście opiera się na oznaczaniu widoku za pomocą znacznika view->
W docelowym ViewController po prostu przypisz tag do widoku głównego, jak w poniższym przykładzie kodu:
class MyViewController: BaseViewController { // declare unique view tag identifier static let ViewTag = 2105981; override func viewDidLoad() { super.viewDidLoad(); // assign the value to the current root view self.view.tag = MyViewController.ViewTag; }
I na koniec w AppDelegate.swift sprawdź, czy aktualnie wyświetlany widok jest tym, który oznaczyliśmy:
func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask { if (window?.viewWithTag(DesignerController.ViewTag)) != nil { return .portrait; } return .all; }
To podejście zostało przetestowane z moim symulatorem i wydaje się, że działa dobrze.
Uwaga: zaznaczony widok zostanie również znaleziony, jeśli bieżący MVC nakłada się na jakiś element podrzędny ViewController w stosie nawigacji.
źródło
Najlepsze rozwiązanie do blokowania i zmiany orientacji w orientacji pionowej i poziomej:
Obejrzyj ten film na YouTube:
https://m.youtube.com/watch?v=4vRrHdBowyo
Ten samouczek jest najlepszy i prosty.
lub użyj poniższego kodu:
Zobacz to zdjęcie
// 1- w drugim viewcontroller ustawiamy landscapeleft, aw pierwszym viewcontroller ustawiamy portrat:
// 2- jeśli używasz NavigationController, powinieneś dodać rozszerzenie
import UIKit class SecondViewController: UIViewController { override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) UIDevice.current.setValue(UIInterfaceOrientation.landscapeLeft.rawValue, forKey: "orientation") } override open var shouldAutorotate: Bool { return false } override open var supportedInterfaceOrientations: UIInterfaceOrientationMask { return .landscapeLeft } override var preferredInterfaceOrientationForPresentation: UIInterfaceOrientation { return .landscapeLeft } override func viewDidLoad() { super.viewDidLoad() } //write The rest of your code in here } //if you use NavigationController, you should add this extension extension UINavigationController { override open var supportedInterfaceOrientations: UIInterfaceOrientationMask { return topViewController?.supportedInterfaceOrientations ?? .allButUpsideDown } }
źródło