web-dev-qa-db-fra.com

Comment permettre à un seul UIViewController de pivoter dans les directions Paysage et Portrait?

Mon application est destinée uniquement au périphérique iphone (iphone 4 et 5) et conçue pour prendre en charge uniquement le système ios 6.

Toute mon application ne supporte que le mode portrait. Mais il existe une vue appelée "ChatView", que je souhaite prendre en charge à la fois avec les modes landscape et portrait.

J'ai défini les rotations de périphérique requises comme suit -

enter image description here

J'ai aussi essayé de suivre le code pour supporter la rotation dans "ChatView" -

-(BOOL)shouldAutorotate
{
    return YES;
}

-(NSUInteger)supportedInterfaceOrientations
{
    return UIInterfaceOrientationMaskLandscape;
}

Mais il ne pouvait pas faire pivoter cette vue.

J'ai beaucoup cherché, mais je ne pouvais pas trouver la solution à mon problème.

Et aussi dans "ChatView", il y a des objets comme des boutons, des champs de texte dont les cadres sont définis par programme. Donc, je veux savoir si je dois aussi définir des cadres de tous ces objets pour le mode paysage?

Aidez-moi, s'il vous plaît.

Merci.....

60
Rohan

Je pense que si vous voulez prendre en charge une seule rotation de contrôleur de vue, ce n’est pas possible car l’application suivra les orientations que vous avez définies dans .plist fichier. Une autre solution consiste à prendre en charge votre application pour les paysages et les portraits, et geler toutes les rotations des contrôleurs de vues en portrait, à l'exception de l'affichage Chat.

EDIT

Pour sous-classer UINavigationController, créez un nouveau fichier avec le nom, par exemple. CustomNavigationController et en faire une sous-classe de UINavigationController.

fichier. h

#import <UIKit/UIKit.h>

@interface CustomNavigationController : UINavigationController

@end

fichier .m

#import "CustomNavigationController.h"

@interface CustomNavigationController ()

@end


@implementation CustomNavigationController

-(BOOL)shouldAutorotate
{
    return NO;
}

-(UIInterfaceOrientationMask)supportedInterfaceOrientations
{
    return UIInterfaceOrientationMaskAll;
}


- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    return UIInterfaceOrientationIsPortrait(interfaceOrientation);
}

@end

Définissez la classe de votre UINavigationController dans votre classe principale xib comme CustomNavigationController. J'espère que ça vous aide ..

28
NightFury

Simple mais ça marche très bien. IOS 7.1 et 8

AppDelegate.h

@property () BOOL restrictRotation;

AppDelegate.m

-(NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window
{
if(self.restrictRotation)
    return UIInterfaceOrientationMaskPortrait;
else
    return UIInterfaceOrientationMaskAll;
}

ViewController

-(void) restrictRotation:(BOOL) restriction
{
    AppDelegate* appDelegate = (AppDelegate*)[UIApplication sharedApplication].delegate;
    appDelegate.restrictRotation = restriction;
}

viewDidLoad

[self restrictRotation:YES]; or NO
63
Alan10977

Votre contrôleur de vue ne pivotera jamais vers une position non prise en charge par l'application elle-même. Vous devez activer toutes les rotations possibles, puis, dans la vue, les contrôleurs qui ne sont pas censés faire la rotation mettent les lignes suivantes

- (UIInterfaceOrientationMask)supportedInterfaceOrientations
{
    return UIInterfaceOrientationMaskPortrait;
}

Dans ChatView, cela devrait être:

- (UIInterfaceOrientationMask)supportedInterfaceOrientations
{
    return UIInterfaceOrientationMaskAll;
}

Si vous devez modifier votre mise en page après une rotation, vous devez implémenter les modifications appropriées dans vos sous-vues.

- (void)viewWillLayoutSubviews

Utilisation self.view.bounds pour vérifier la taille actuelle du view, puisque self.view.frame ne change pas après les rotations.

21
johnyu

pour le viewcontroller.m spécifique que vous voulez faire pivoter

ajoutez cette méthode:

- (BOOL)canAutoRotate
{
    return YES;
}

puis dans votre AppDelegate.m

- (NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window
{
    UIViewController *currentViewController = [self topViewController];

    if ([currentViewController respondsToSelector:@selector(canAutoRotate)]) {
        NSMethodSignature *signature = [currentViewController methodSignatureForSelector:@selector(canAutoRotate)];

        NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];

        [invocation setSelector:@selector(canAutoRotate)];
        [invocation setTarget:currentViewController];

        [invocation invoke];

        BOOL canAutorotate = NO;
        [invocation getReturnValue:&canAutorotate];

        if (canAutorotate) {
            return UIInterfaceOrientationMaskAll;
        }
    }

    return UIInterfaceOrientationMaskPortrait;
}

- (UIViewController *)topViewController
{
    return [self topViewControllerWithRootViewController:[UIApplication sharedApplication].keyWindow.rootViewController];
}

- (UIViewController *)topViewControllerWithRootViewController:(UIViewController *)rootViewController
{
    if ([rootViewController isKindOfClass:[UITabBarController class]]) {
        UITabBarController* tabBarController = (UITabBarController*)rootViewController;
        return [self topViewControllerWithRootViewController:tabBarController.selectedViewController];
    } else if ([rootViewController isKindOfClass:[UINavigationController class]]) {
        UINavigationController* navigationController = (UINavigationController*)rootViewController;
        return [self topViewControllerWithRootViewController:navigationController.visibleViewController];
    } else if (rootViewController.presentedViewController) {
        UIViewController* presentedViewController = rootViewController.presentedViewController;
        return [self topViewControllerWithRootViewController:presentedViewController];
    } else {
        return rootViewController;
    }
}
16
Ted

La réponse de Ted fonctionne bien avec le problème mentionné par Alexander de Norvège. Mais je me suis dit que le problème ne se produisait pas comme l'expliquait Alexander

Lorsque ViewController B qui est actuellement en mode paysage (toutes les orientations activées) revient à ViewController A (portrait uniquement) après que l'utilisateur a cliqué sur le bouton Précédent, supportedInterfaceOrientationsForWindow n'est pas appelé et ViewController A se termine en paysage

En fait, lorsque ViewController B qui est actuellement en mode paysage (toutes les orientations activées) revient à ViewController A (Portrait uniquement) après que l'utilisateur a cliqué sur le bouton Précédent, Appdelegate

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation;

se fait appeler. Cependant, le contrôleur de vue racine est toujours ViewController B (ce contrôleur de vue activé par rotation), ViewController A ne revient pas à l'orientation portrait, puisque ViewController B renvoie toujours

-(BOOL)shouldAutorotate{

    return YES;
}

Ainsi, lorsque vous appuierez sur le bouton Retour pour revenir à l'affichage, "shouldAutorotate -> NO" dans ViewController B. Ensuite, ViewController A passera en orientation Portrait. C'est ce que j'ai fait

@property (nonatomic, assign) BOOL canAutoRotate;

#pragma mark - Public methods
- (BOOL)canAutoRotate
{
    return _canAutoRotate;
}

#pragma mark - Button actions
- (void)backButtonPressed:(UIButton *)sender {
    _canAutoRotate = NO;
   (...)
}

#pragma mark - Init
- (id)init{
    if(self=[super init]) {
        _canAutoRotate = YES;
    }
    return self;
}
4
Charitha Basnayake

Swift 3 Version cachère

J'ai laissé cela ici juste pour un cas, quelqu'un a le problème.

documentation d'Apple pour supportedInterfaceOrientations dit:

Lorsque l'utilisateur modifie l'orientation du périphérique, le système appelle cette méthode sur le contrôleur de vue racine ou sur le contrôleur de vue présenté supérieur remplissant la fenêtre. Si le contrôleur de vue prend en charge la nouvelle orientation, la fenêtre et le contrôleur de vue sont tournés vers la nouvelle orientation. Cette méthode n'est appelée que si la méthode shouldAutorotate du contrôleur de vue renvoie la valeur true.

En quelques mots, vous devez remplacer supportedInterfaceOrientations dans le contrôleur de vue racine afin qu'il renvoie la valeur de son contrôleur de vue enfant supérieur et la valeur par défaut sinon.

Ce que vous devez faire est de vérifier si l'application prend en charge tous les modes (voir Informations de déploiement dans les paramètres généraux des cibles ou Info.plist), recherchez la classe de votre contrôleur de vue racine. Cela peut être générique UIViewController, UINavigationController, UITabBarController ou une classe personnalisée. Vous pouvez le vérifier de cette façon:

dump(UIApplication.shared.keyWindow?.rootViewController)

Ou de toute autre manière que vous aimez.

Soit CustomNavigationController. Donc, vous devriez écraser supportedInterfaceOrientations comme ceci:

class CustomNavigationController: UINavigationController {

    override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
        return topViewController?.supportedInterfaceOrientations ?? .allButUpsideDown
    }
}

Dans tout contrôleur de vue qui ne devrait supporter que l'orientation portrait, par exemple, remplacez supportedInterfaceOrientations de cette façon:

class ChildViewController: UIViewController {

    override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
        return .portrait
    }
}

Ensuite, n'oubliez pas de vérifier si shouldAutorotate de votre contrôleur de vue racine et du contrôleur de vue présenté supérieur renvoie déjà true. Sinon, ajoutez ceci aux définitions de classes:

override var shouldAutorotate: Bool {
    return true
}

Sinon, supportedInterfaceOrientations ne sera jamais appelé.

Voici!

Si vous devez résoudre le problème inverse lorsqu'un seul contrôleur de vue doit prendre en charge un ensemble d'orientations et que d'autres ne le font pas, modifiez cette modification pour tous les contrôleurs de vue, sauf celui-ci.

J'espère que cela aidera.

2
Artem Kirillov

Je ne suis pas sûr de l'historique de ce problème (maintenant = échéancier iOS 10), mais la solution la plus simple manquait car j'ai posté ceci en octobre 2016.

En supposant que vous vouliez ceci:

  1. Prise en charge iOS 7 et plus récent uniquement (y compris iOS 10)
  2. Certains contrôleurs de vue doivent prendre en charge toutes les orientations, d'autres doivent prendre en charge un sous-ensemble d'orientations. Exemple de ce que je veux dire: un contrôleur de vue ne devrait supporter que le portrait, tandis que tous les autres devraient supporter toutes les orientations
  3. Tous les contrôleurs de vue doivent effectuer une rotation automatique s'ils prennent en charge la rotation (ce qui signifie que vous ne voulez pas que le code corrige ce problème dans vos contrôleurs de vue).
  4. Supporte l'ajout de UINavigationControllers dans les XIBs/NIBs/Storyboards sans rien leur faire

... alors (IMO) la solution la plus simple consiste à créer un UINavigationControllerDelegate, PAS la sous-classe UINavigationController (qui viole l'hypothèse 4 ci-dessus).

Quand j'ai résolu ceci, j'ai décidé de faire mon premier ViewController un UINavigationControllerDelegate. Ce contrôleur de vue se définit lui-même comme délégué du contrôleur de navigation et renvoie les orientations autorisées. Dans mon cas, la valeur par défaut est que toutes les orientations sont autorisées, avec portrait préféré, mais dans un cas particulier, seul le portrait est autorisé. Le code ci-dessous provient de Swift 3/XCode 8:

    class iPhoneStartViewController: UIViewController {

        var navInterfaceOrientationMask: UIInterfaceOrientationMask?
        var navInterfaceOrientationPreferred: UIInterfaceOrientation! = .portrait

        override func viewDidLoad() {
            super.viewDidLoad()
            self.navigationController?.delegate = self
        }

        @IBAction func cameraButtonPressed(_ sender: AnyObject) {
            if PermissionsHelper.singleton().photosPermissionGranted() == false {
                self.navInterfaceOrientationMask = nil   // default is: all orientations supported
                self.performSegue(withIdentifier: "segueToPhotoAccess", sender: self)
            } else {
                self.navInterfaceOrientationMask = .portrait // this stops the next view controller from being to rotate away from portrait
                self.performSegue(withIdentifier: "segueToCamera", sender: self)
            }
        }
     }

     // lock orientation to portrait in certain cases only. Default is: all orientations supported
    extension iPhoneStartViewController : UINavigationControllerDelegate {
        public func navigationControllerSupportedInterfaceOrientations(_ navigationController: UINavigationController) -> UIInterfaceOrientationMask {
            if let mask = self.navInterfaceOrientationMask {
                return mask
            } else {
                return .all
            }
        }

        public func navigationControllerPreferredInterfaceOrientationForPresentation(_ navigationController: UINavigationController) -> UIInterfaceOrientation {
            return self.navInterfaceOrientationPreferred
        }
    }
2
xaphod

faire une sous-classe de UINavigationController comme ceci:

MyNavigationController.h

#import <UIKit/UIKit.h>

@interface MyNavigationController : UINavigationController

@end

MyNavigationController.m

#import "MyNavigationController.h"
#import "ServicesVC.h"

@implementation MyNavigationController

-(BOOL)shouldAutorotate{

    return YES;
}

-(NSUInteger)supportedInterfaceOrientations{

    if ([[self.viewControllers lastObject] isKindOfClass:[ServicesVC class]]) {
        return UIInterfaceOrientationMaskLandscapeLeft | UIInterfaceOrientationMaskLandscapeRight;
    }

    return UIInterfaceOrientationMaskAll;
}

@end

en supposant que votre contrôleur de vue s'appelle: ServicesVC

1
Jesus

Sur la base de la réponse de @iAnum, j'ai activé la détection de classe autorotate et UIViewController.

En effet, sinon, la transition vers le "contrôleur de vue spécial" et hors de celui-ci ne sera pas corrigée en orientation portrait et vous serez bloqué dans une orientation non prise en charge.

Une seule vue prenant en charge le paysage, je l'ai simplement codée en dur dans le contrôleur de vue de navigation personnalisé:

-(BOOL)shouldAutorotate
{
    return YES;
}

-(NSUInteger)supportedInterfaceOrientations
{
    //Access the current top object.
    UIViewController *viewController = [self.viewControllers lastObject];
    //Is it one of the landscape supported ones?
    if ([viewController isMemberOfClass:[SpecialViewController class]]) {
        return UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskLandscapeLeft | UIInterfaceOrientationMaskLandscapeRight;
    } else
        return UIInterfaceOrientationMaskPortrait;
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    //Access the current top object.
    UIViewController *viewController = [self.viewControllers lastObject];
    //Is it one of the landscape supported ones?
    if ([viewController isMemberOfClass:[SpecialViewController class]]) {
        return interfaceOrientation;
    } else
        return UIInterfaceOrientationIsPortrait(interfaceOrientation);
}

Il y a un problème avec les VCs dont il est question ici https://stackoverflow.com/a/15057537/127735 où le fait d'appuyer en arrière dans le paysage n'appelle même pas les méthodes d'orientation, vous devez donc le pirater un peu en montrant et en écartant une vue modale.

Et rappelez-vous simplement que si vous voulez que willShowViewController se déclenche, vous devez définir self.delegate = self et ajouter UINavigationControllerDelegate à votre contrôleur de navigation personnalisé avec le code ci-dessous.

- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
    return UIInterfaceOrientationPortrait;
}

- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated
{
    UIApplication* application = [UIApplication sharedApplication];
    if (application.statusBarOrientation != UIInterfaceOrientationPortrait)
    {
        UIViewController *c = [[UIViewController alloc]init];
        [c.view setBackgroundColor:[UIColor clearColor]];
        [navigationController presentViewController:c animated:NO completion:^{
            [self dismissViewControllerAnimated:YES completion:^{
            }];
        }];
    }
}
1
LordParsley

Voici la réponse d'Alexandre ( https://stackoverflow.com/posts/25507963/revisions ) dans Swift:

func application(application: UIApplication, supportedInterfaceOrientationsForWindow window: UIWindow?) -> Int {

    var currentViewController: UIViewController? = self.topViewController()
    if currentViewController != nil && currentViewController!.canAutoRotate() {
        return Int(UIInterfaceOrientationMask.All.rawValue)
    }
    return Int(UIInterfaceOrientationMask.Portrait.rawValue)


}

func topViewController() -> UIViewController? {
    if UIApplication.sharedApplication().keyWindow != nil
    {
        return self.topViewControllerWithRootViewController(UIApplication.sharedApplication().keyWindow!.rootViewController!)
    }
    return nil
}

func topViewControllerWithRootViewController(rootViewController: UIViewController?) -> UIViewController? {
    if rootViewController == nil {
        return nil
    }
    if rootViewController!.isKindOfClass(UITabBarController) {
        var tabBarController: UITabBarController = (rootViewController as? UITabBarController)!
        return self.topViewControllerWithRootViewController(tabBarController.selectedViewController)
    }
    else {
        if rootViewController!.isKindOfClass(UINavigationController) {
            var navigationController: UINavigationController = (rootViewController as? UINavigationController)!
            return self.topViewControllerWithRootViewController(navigationController.visibleViewController)
        }
        else {
            if (rootViewController!.presentedViewController != nil) {
                var presentedViewController: UIViewController = rootViewController!.presentedViewController!
                return self.topViewControllerWithRootViewController(presentedViewController)
            }
            else {
                return rootViewController
            }
        }
    }
}

En outre, vous devrez ajouter l'extrait suivant dans AppDelegate.Swift:

extension UIViewController {
func canAutoRotate() -> Bool {
    return false
}}

Et pour les ViewControllers pour lesquels vous voulez autoriser toutes les rotations, ajoutez cette fonction:

override func canAutoRotate() -> Bool {
    return true
}
1
egsemsem

// colle cette méthode dans la classe app Deligate

- (UIInterfaceOrientationMask)application:(UIApplication )application supportedInterfaceOrientationsForWindow:(UIWindow )window
{
 if ([self.window.rootViewController.presentedViewController isKindOfClass: [_moviePlayerController class]])
  {
   if (self.window.rootViewController.presentedViewController)
        return UIInterfaceOrientationMaskAll;
    else return UIInterfaceOrientationMaskPortrait;
  }
 else return UIInterfaceOrientationMaskPortrait;
}   
0
dheerendra

Si l'application prend en charge de IOS7 à IOS9, utilisez ce code pour l'orientation:

#if __IPHONE_OS_VERSION_MAX_ALLOWED < 90000
- (NSUInteger)supportedInterfaceOrientations
#else
- (UIInterfaceOrientationMask)supportedInterfaceOrientations
#endif
{
    if([AppDelegate isPad]) return UIInterfaceOrientationMaskAll;
    else return UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskPortraitUpsideDown;
}
0
Mihir Oza

Je sais que cette question est très ancienne mais qu’elle nécessite une réponse à jour. La manière la plus simple et la plus correcte d’atteindre ce résultat consiste à activer Portrait et Paysage dans les paramètres de votre application. Ajoutez ensuite ce code au délégué de votre application:

 func application(application: UIApplication, supportedInterfaceOrientationsForWindow window: UIWindow?) -> UIInterfaceOrientationMask {

    if let navigationController = self.window?.rootViewController as? UINavigationController {

        if navigationController.visibleViewController is INSERTYOURVIEWCONTROLLERHERE  {
            return UIInterfaceOrientationMask.All
        }

        else {
            return UIInterfaceOrientationMask.Portrait
        }
    }

    return UIInterfaceOrientationMask.Portrait
}

N'oubliez pas de remplacer "INSERTYOURVIEWCONTROLLERHERE" par votre contrôleur de vue.

0
Paul Lehn

J'ai eu la même situation. J'ai donc subdivisé UINavigationController en CustomNavigationController, et à l'intérieur de ce CustomNavigationController, j'ai écrit:

#define IOS_OLDER_THAN_6 ( [ [ [ UIDevice currentDevice ] systemVersion ] floatValue ] < 6.0 )
#define IOS_NEWER_OR_EQUAL_TO_6 ( [ [ [ UIDevice currentDevice ] systemVersion ] floatValue ] >= 6.0 )


#pragma mark - Rotation

#ifdef IOS_OLDER_THAN_6
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation{
      return (toInterfaceOrientation == UIInterfaceOrientationPortrait);
}
#endif
#ifdef IOS_NEWER_OR_EQUAL_TO_6
-(BOOL)shouldAutorotate {
    return YES;
}
- (NSUInteger)supportedInterfaceOrientations {
    return UIInterfaceOrientationMaskPortrait;;
}
#endif

J'ai utilisé ce CustomNavigationController au lieu de NavigationController existant.

Ensuite, dans le contrôleur de vue que vous devez afficher dans LandScape Orientation, dites LandScapeView, j’ai écrit

#pragma mark - Rotation

#ifdef IOS_OLDER_THAN_6

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation{
    return (toInterfaceOrientation == UIInterfaceOrientationLandscapeRight | toInterfaceOrientation == UIInterfaceOrientationLandscapeLeft);
}

#endif

#ifdef IOS_NEWER_OR_EQUAL_TO_6

-(BOOL)shouldAutorotate {
    return YES;
}
- (NSUInteger)supportedInterfaceOrientations {
    return UIInterfaceOrientationMaskLandscapeRight | UIInterfaceOrientationMaskLandscapeLeft;
}

#endif

À l'intérieur de CustomNavigationController, j'ai présenté ce contrôleur de vue, non poussé dans Navigation Stack. Ainsi, LandScapeView est apparu dans LandScape Orientation.

LandScapeView *graph = [[LandScapeView alloc]init....];
[self presentViewController:graph animated:YES completion:nil];

Je n'ai rien changé dans le Orientation d'interface prise en charge dans les paramètres du projet.

0
rakeshNS