Поскольку мое приложение поддерживает любую ориентацию. Я хотел бы заблокировать только портретный режим для определенного UIViewController.
например, предположим, что это было приложение с вкладками, и когда представление входа в систему отображается модально, я хочу, чтобы этот вид входа был только в портретном режиме, независимо от того, как пользователь поворачивает устройство или как будет текущая ориентация устройства
swift
uiviewcontroller
portrait
device-orientation
Тиха Аунг
источник
источник
Ответы:
Все может стать довольно запутанным, когда у вас сложная иерархия представлений, например, наличие нескольких контроллеров навигации и / или контроллеров представления вкладок.
Эта реализация помещает его в отдельные контроллеры представления, чтобы установить, когда они хотели бы заблокировать ориентацию, вместо того, чтобы полагаться на делегата приложения, чтобы найти их путем итерации по вложенным представлениям.
Свифт 3, 4, 5
В 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 }
В какой-то другой глобальной структуре или вспомогательном классе я создал 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() } }
Затем в желаемом ViewController вы хотите заблокировать ориентацию:
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) }
Если iPad или универсальное приложение
Убедитесь, что "Требуется полноэкранный режим" отмечен в Target Settings -> General -> Deployment Info.
supportedInterfaceOrientationsFor
делегат не будет вызван, если он не отмечен.источник
Requires full screen
, приложение будет недоступно для скользящего и разделенного просмотра. См. Раздел « Внедрение улучшений многозадачности на iPad от Apple». У меня ответ не требует включенияRequires full screen
Swift 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") } }
Ваш ViewController Добавьте следующую строку, если вам нужна только портретная ориентация. вы должны применить это ко всем ViewController, который должен отображать портретный режим.
override func viewWillAppear(_ animated: Bool) { AppDelegate.AppUtility.lockOrientation(UIInterfaceOrientationMask.portrait, andRotateTo: UIInterfaceOrientation.portrait) }
и это сделает ориентацию экрана для других Viewcontroller в соответствии с физической ориентацией устройства.
override func viewWillDisappear(_ animated: Bool) { AppDelegate.AppUtility.lockOrientation(UIInterfaceOrientationMask.all) }
источник
func application
в делегате приложения этой функциейfunc application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask { return self.orientationLock ?
Swift 3 и 4
Установите
supportedInterfaceOrientations
свойство определенных UIViewControllers следующим образом: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() } //... }
ОБНОВИТЬ
Это решение работает только тогда , когда ваш
viewController
будет не встроен вUINavigationController
, поскольку ориентация наследуется от родительского ViewController.В этом случае вы можете создать подкласс
UINavigationViewController
и установить для него эти свойства.источник
Добавьте этот код, чтобы заставить портрет и заблокировать его:
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 }
В вашем AppDelegate установите для supportedInterfaceOrientationsForWindow любую ориентацию, которую вы хотите, чтобы все приложение поддерживало:
func application(application: UIApplication, supportedInterfaceOrientationsForWindow window: UIWindow?) -> UIInterfaceOrientationMask { return UIInterfaceOrientationMask.All }
источник
Это общее решение вашей и других проблем.
1. Создайте дополнительный класс UIHelper и включите следующие методы:
/**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. Создайте протокол с вашим желанием поведения, для вашего конкретного случая будет портрет.
ориентация протоколаIsOnlyPortrait {}
Примечание: если хотите, добавьте его в начало класса UIHelper.
3. Расширьте свой View Controller
В твоем случае:
class Any_ViewController: UIViewController,orientationIsOnlyPortrait { .... }
4. В классе делегата приложения добавьте этот метод:
func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask { let presentedViewController = UIHelper.topViewController() if presentedViewController is orientationIsOnlyPortrait { return .portrait } return .all }
Заключительные примечания:
источник
Для новой версии Swift попробуйте это
override var shouldAutorotate: Bool { return false } override var supportedInterfaceOrientations: UIInterfaceOrientationMask { return UIInterfaceOrientationMask.portrait } override var preferredInterfaceOrientationForPresentation: UIInterfaceOrientation { return UIInterfaceOrientation.portrait }
источник
Куча отличных ответов в этой теме, но ни один из них не соответствовал моим потребностям. У меня есть приложение с вкладками с контроллерами навигации на каждой вкладке, и одно представление необходимо вращать, а другие необходимо заблокировать в портретной ориентации. Контроллер навигации по какой-то причине не менял размер своих подвидов должным образом. Нашел решение (в Swift 3), объединив с этим ответом, и проблемы с макетом исчезли. Создайте структуру, как предлагает @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") } }
Затем подкласс 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 } }
Не забудьте изменить класс TabBarController в раскадровке на только что созданный подкласс.
источник
Вот простой способ, который у меня работает со Swift 4.2 (iOS 12.2), поместите его в,
UIViewController
для которого вы хотите отключить shouldAutorotate:override var supportedInterfaceOrientations: UIInterfaceOrientationMask { return .portrait }
.portrait
Часть рассказывает , в которой ориентация (s) , чтобы остаться, вы можете изменить это , как вам нравится. Варианты:.portrait
,.all
,.allButUpsideDown
,.landscape
,.landscapeLeft
,.landscapeRight
,.portraitUpsideDown
.источник
Чтобы установить альбомную ориентацию для всех представлений вашего приложения и разрешить только одно представление для всех ориентаций (например, чтобы иметь возможность добавить камеру):
В AppDelegate.swift:
var adaptOrientation = false
В: didFinishLaunchingWithOptions
NSNotificationCenter.defaultCenter().addObserver(self, selector: "adaptOrientationAction:", name:"adaptOrientationAction", object: nil)
В другом месте в 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 } }
Затем в представлении, которое переходит к тому, которое вы хотите иметь для всех ориентаций:
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) } }
Последнее, что нужно сделать, это отметить ориентацию устройства: - Книжная - Альбомная слева - Альбомная справа
источник
Создать новое расширение с
import UIKit extension UINavigationController { override open var supportedInterfaceOrientations: UIInterfaceOrientationMask { return .portrait } } extension UITabBarController { override open var supportedInterfaceOrientations: UIInterfaceOrientationMask { return .portrait } }
источник
bmjohns -> Ты спаситель моей жизни. Это единственное рабочее решение (со структурой AppUtility)
Я создал этот класс:
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") } } }
и следовал вашим инструкциям, и все отлично работает для Swift 3 -> xcode версии 8.2.1
источник
Начиная с iOS 10 и 11 iPad поддерживает Slide Over и Split View. Чтобы включить приложение в режиме Slide Over и Split View,
Requires full screen
необходимо снять флажок. Это означает, что принятый ответ нельзя использовать, если приложение хочет поддерживать Slide Over и Split View. Дополнительные сведения об улучшении многозадачности Apple на iPad можно найти здесь .У меня есть решение, которое позволяет (1) снять отметку
Requires full screen
, (2) реализовать только одну функциюappDelegate
(особенно если вы не хотите / не можете изменять целевые контроллеры представления) и (3) избегать рекурсивных вызовов. Нет необходимости во вспомогательном классе или расширениях.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] }
редактировать
@bmjohns указал, что эта функция не вызывается на iPad. Я проверил, и да, это не называлось. Итак, я провел еще немного тестов и выяснил некоторые факты:
Requires full screen
потому что хочу включить на iPad режим «Скольжение» и «Просмотр слайдов». Это требует приложение , чтобы поддержать все 4 ориентации для IPad, в Info.plist:Supported interface orientations (iPad)
.Мое приложение работает так же, как Facebook: на iPhone большую часть времени оно заблокировано в портретной ориентации. При просмотре изображения в полноэкранном режиме пользователи могут поворачивать альбомную ориентацию для лучшего просмотра. На iPad пользователи могут поворачивать в любую ориентацию в любых контроллерах представления. Итак, приложение хорошо выглядит, когда iPad установлен на Smart Cover (альбомная ориентация слева).
Чтобы с iPad звонить
application(_:supportedInterfaceOrientationsFor)
, в Info.plist оставьте только вертикальную ориентацию для iPad. Приложение потеряет возможность скольжения и разделения просмотра. Но вы можете заблокировать или разблокировать ориентацию для любого контроллера представления всего в одном месте и не нужно изменять класс ViewController.Наконец, эта функция вызывается в жизненном цикле контроллера представления, когда представление отображается / удаляется. Если вашему приложению нужно заблокировать / разблокировать / изменить ориентацию в другое время, оно может не работать
источник
Фактическое протестированное решение для этого. В моем примере мне нужно, чтобы все мое приложение было в портретном режиме, но только одна ориентация экрана должна быть в ландшафтном режиме.
Код в 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") } }
Затем запишите этот код до того, как ваш контроллер просмотра с альбомной ориентацией будет представлен / push.
override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) AppDelegate.AppUtility.lockOrientation(UIInterfaceOrientationMask.portrait, andRotateTo: UIInterfaceOrientation.portrait) }
Затем запишите этот код в реальном контроллере просмотра (для ландшафтного просмотра)
override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) AppDelegate.AppUtility.lockOrientation(UIInterfaceOrientationMask.landscape, andRotateTo: UIInterfaceOrientation.landscape) }
источник
Благодаря ответу @bmjohn выше. Вот рабочая версия Xamarin / C # этого кода ответа, чтобы сэкономить время транскрипции другим:
AppDelegate.cs
public UIInterfaceOrientationMask OrientationLock = UIInterfaceOrientationMask.All; public override UIInterfaceOrientationMask GetSupportedInterfaceOrientations(UIApplication application, UIWindow forWindow) { return this.OrientationLock; }
Статический класс 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")); } }
Просмотр контроллера:
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); }
источник
Я немного поэкспериментировал и мне удалось найти чистое решение этой проблемы. Подход основан на тегировании представления с помощью тега view->.
В целевом ViewController просто назначьте тег корневому представлению, как в следующем примере кода:
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; }
И, наконец, в AppDelegate.swift проверьте, является ли текущее отображаемое представление тем, которое мы пометили:
func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask { if (window?.viewWithTag(DesignerController.ViewTag)) != nil { return .portrait; } return .all; }
Этот подход был протестирован на моем симуляторе и, кажется, работает нормально.
Примечание: отмеченное представление также будет найдено, если текущий MVC перекрывается некоторым дочерним ViewController в стеке навигации.
источник
Лучшее решение для блокировки и изменения ориентации в портретной и альбомной ориентации:
Посмотрите это видео на YouTube:
https://m.youtube.com/watch?v=4vRrHdBowyo
Этот учебник самый лучший и простой.
или используйте код ниже:
Смотрите эту картинку
// 1- во втором контроллере просмотра мы устанавливаем ландшафт слева, а в первом контроллере просмотра устанавливаем portrat:
// 2- если вы используете NavigationController, вы должны добавить расширение
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 } }
источник