Depuis que mon application a reçu un soutien pour toutes les orientations. Je voudrais verrouiller uniquement le mode portrait sur UIViewController spécifique.
(Par exemple, supposons qu'il s'agisse d'une application à onglets et que lorsque SignIn View apparaisse de manière modale, je souhaite uniquement que SignIn View soit en mode portrait uniquement no quelle que soit la rotation de l'utilisateur ou l'orientation actuelle du périphérique )
Lorsque vous avez une hiérarchie de vues complexe, les choses peuvent devenir compliquées, comme avoir plusieurs contrôleurs de navigation et/ou des contrôleurs de vue par onglet.
Cette implémentation impose aux contrôleurs de vue individuels de définir quand ils souhaitent verrouiller les orientations, au lieu de compter sur le délégué de l'application pour les rechercher en effectuant une itération dans des sous-vues.
Swift 3 & 4
Dans 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
}
Dans une autre classe globale de struct ou helper, j'ai créé ici 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()
}
}
Ensuite, dans le ViewController souhaité, vous souhaitez verrouiller les orientations:
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)
}
Si iPad ou application universelle
Assurez-vous que l'option "Nécessite le plein écran" est cochée dans Paramètres de la cible -> Général -> Informations de déploiement. supportedInterfaceOrientationsFor
délégué ne sera pas appelé si cela n'est pas coché .
Swift 4
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")
}
}
Votre ViewController Ajouter la ligne suivante si vous n’avez besoin que d’une orientation portrait. vous devez appliquer cela à tous les ViewController nécessaires pour afficher le mode portrait.
override func viewWillAppear(_ animated: Bool) {
AppDelegate.AppUtility.lockOrientation(UIInterfaceOrientationMask.portrait, andRotateTo: UIInterfaceOrientation.portrait)
}
et qui orientera d’autres écrans Viewcontroller en fonction de l’orientation physique du périphérique.
override func viewWillDisappear(_ animated: Bool) {
AppDelegate.AppUtility.lockOrientation(UIInterfaceOrientationMask.all)
}
Ajoutez ce code pour forcer le portrait et le verrouiller:
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
}
Dans votre AppDelegate - définissez supportedInterfaceOrientationsForWindow sur les orientations que vous souhaitez que l'application entière prenne en charge:
func application(application: UIApplication, supportedInterfaceOrientationsForWindow window: UIWindow?) -> UIInterfaceOrientationMask {
return UIInterfaceOrientationMask.All
}
Ceci est une solution générique pour votre problème et d'autres liés.
1. Créez la classe auxiliar UIHelper et appliquez les méthodes suivantes:
/**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. Créez un protocole avec votre comportement souhaité, pour votre cas spécifique sera portrait.
protocole orientationIsOnlyPortrait {}
Note: Si vous voulez, ajoutez-le en haut de UIHelper Class.
3. Étendez votre contrôleur de vue
Dans ton cas:
class Any_ViewController: UIViewController,orientationIsOnlyPortrait {
....
}
4. Dans la classe de délégué d'application, ajoutez cette méthode:
func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
let presentedViewController = UIHelper.topViewController()
if presentedViewController is orientationIsOnlyPortrait {
return .portrait
}
return .all
}
Notes finales:
Un tas de bonnes réponses dans ce fil, mais aucune ne correspondait vraiment à mes besoins. J'ai une application à onglets avec des contrôleurs de navigation dans chaque onglet, et une vue doit pivoter, tandis que les autres doivent être verrouillées en mode portrait. Le contrôleur de navigation ne redimensionnait pas correctement ses sous-vues, pour une raison quelconque. Nous avons trouvé une solution (dans Swift 3) en combinant avec ceci réponse, et les problèmes de mise en page ont disparu. Créez la structure comme suggéré par @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")
}
}
Ensuite, sous-classe 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
}
}
N'oubliez pas de changer la classe de TabBarController dans le storyboard pour la nouvelle sous-classe.
Pour définir l'orientation Paysage sur toutes les vues de votre application et n'autoriser qu'une seule vue sur Toutes les orientations (pour pouvoir ajouter une pellicule, par exemple):
Dans AppDelegate.Swift:
var adaptOrientation = false
Dans: didFinishLaunchingWithOptions
NSNotificationCenter.defaultCenter().addObserver(self, selector: "adaptOrientationAction:", name:"adaptOrientationAction", object: nil)
Ailleurs dans 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
}
}
Puis dans la vue qui se sépare de celle que vous voulez pouvoir avoir Toutes les orientations:
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)
}
}
La dernière chose à faire est de cocher Orientation de l'appareil: - Portrait - Paysage gauche - Paysage droite
bmjohns -> Tu es mon sauveur de vie. C’est la seule solution qui fonctionne (avec la structure AppUtility)
J'ai créé cette classe:
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")
}
}
}
et suivi vos instructions, et tout fonctionne parfaitement pour Swift 3 -> xcode version 8.2.1
Créer une nouvelle extension avec
import UIKit
extension UINavigationController {
override open var supportedInterfaceOrientations: UIInterfaceOrientationMask {
return .portrait
}
}
extension UITabBarController {
override open var supportedInterfaceOrientations: UIInterfaceOrientationMask {
return .portrait
}
}
À partir d'iOS 10 et 11, l'iPad prend en charge les fonctions glisser sur et fractionnées. Pour activer une application en mode Diapositive et fractionnée, désélectionnez Requires full screen
doit être. Cela signifie que la réponse acceptée ne peut pas être utilisée if l'application veut prendre en charge le mode de glissement et d'affichage fractionné. En savoir plus sur Apple Adopter des améliorations du multitâche sur iPadici .
J'ai une solution qui permet (1) de décocher Requires full screen
, (2) une seule fonction à implémenter dans appDelegate
(surtout si vous ne voulez pas/ne pouvez pas modifier les contrôleurs de vue cibles) et (3) éviter les appels récursifs. . Pas besoin de classe d'assistance ni d'extensions.
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]
}
Modifier
@bmjohns a souligné que cette fonction n'est pas appelée sur iPad. J'ai vérifié et oui il n'a pas été appelé. J'ai donc fait un peu plus de tests et découvert quelques faits:
Requires full screen
parce que je veux activer le mode glisser sur et le mode diapositive sur iPad. Cela nécessite que l'application prenne en charge les 4 orientations pour iPad, dans Info.plist: Supported interface orientations (iPad)
.Mon application fonctionne de la même manière que Facebook: sur iPhone, elle est généralement verrouillée en mode portrait. Lors de l'affichage d'images en plein écran, permet aux utilisateurs de faire pivoter le paysage pour une meilleure vue. Sur iPad, les utilisateurs peuvent faire pivoter n’importe quelle orientation dans n’importe quel contrôleur de vue. Ainsi, l'application a l'air agréable lorsque l'iPad est installé sur Smart Cover (paysage à gauche).
Pour que l'iPad appelle application(_:supportedInterfaceOrientationsFor)
, dans Info.plist, conservez uniquement portrait pour iPad. L'application perdra la capacité Slide Over + Split View. Mais vous pouvez verrouiller ou déverrouiller l'orientation de n'importe quel contrôleur de vue, en un seul endroit et sans avoir à modifier la classe ViewController.
Enfin, cette fonction est appelée sur le cycle de vie du contrôleur de vue, lorsque la vue est affichée/supprimée. Si votre application doit verrouiller/déverrouiller/changer d'orientation ultérieurement, cela pourrait ne pas fonctionner.
Swift 3 & 4
Définissez la propriété supportedInterfaceOrientations
de UIViewControllers spécifique comme ceci:
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()
}
//...
}
Voici un moyen simple qui fonctionne pour moi avec Swift 4.2 (iOS 12.2), mettez ceci dans une UIViewController
pour laquelle vous souhaitez désactiver devraitAutorotate:
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
return .portrait
}
La partie .portrait
indique dans quelle orientation rester, vous pouvez changer cela à votre guise. Les choix sont les suivants: .portrait
, .all
, .allButUpsideDown
, .landscape
, .landscapeLeft
, .landscapeRight
, .portraitUpsideDown
.
Merci à la réponse de @ bmjohn ci-dessus. Voici une version Xamarin/C # de travail du code de cette réponse, permettant aux autres de gagner du temps lors de la transcription:
AppDelegate.cs
public UIInterfaceOrientationMask OrientationLock = UIInterfaceOrientationMask.All;
public override UIInterfaceOrientationMask GetSupportedInterfaceOrientations(UIApplication application, UIWindow forWindow)
{
return this.OrientationLock;
}
Classe statique 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"));
}
}
Contrôleur de vue:
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);
}
Solution actuellement testée pour ce problème .Dans mon exemple, j'ai besoin que toute mon application soit en mode portrait, mais l'orientation d'un seul écran doit être en mode paysage .
Code dans AppDelegate comme décrit ci-dessus.
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")
}
}
Ensuite, écrivez ce code avant que votre contrôleur de vue d'orientation paysage ne soit présenté/Appuyez.
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
AppDelegate.AppUtility.lockOrientation(UIInterfaceOrientationMask.portrait, andRotateTo: UIInterfaceOrientation.portrait)
}
Puis écrivez ce code dans viewcontroller réel (Pour une vue paysage)
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
AppDelegate.AppUtility.lockOrientation(UIInterfaceOrientationMask.landscape, andRotateTo: UIInterfaceOrientation.landscape)
}