Come forzare l'orientamento del controller di visualizzazione in iOS 8?


Answers

La rotazione dell'orientamento è un po 'più complicata se ci si trova all'interno di UINavigationController o UITabBarController . Il problema è che se un controller di visualizzazione è incorporato in uno di questi controller, il controller di navigazione o barra delle schede ha la precedenza e prende le decisioni su autorotazione e orientamenti supportati.

Io uso le seguenti 2 estensioni su UINavigationController e UITabBarController in modo che i controller di vista incorporati in uno di questi controller possano prendere le decisioni.

Dai al controller di vista il potere!

Swift 2.3

extension UINavigationController {
    public override func supportedInterfaceOrientations() -> Int {
        return visibleViewController.supportedInterfaceOrientations()
    }
    public override func shouldAutorotate() -> Bool {
        return visibleViewController.shouldAutorotate()
    }
}

extension UITabBarController {
    public override func supportedInterfaceOrientations() -> Int {
        if let selected = selectedViewController {
            return selected.supportedInterfaceOrientations()
        }
        return super.supportedInterfaceOrientations()
    }
    public override func shouldAutorotate() -> Bool {
        if let selected = selectedViewController {
            return selected.shouldAutorotate()
        }
        return super.shouldAutorotate()
    }
}

Swift 3

extension UINavigationController {
    open override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
        return visibleViewController?.supportedInterfaceOrientations ?? super.supportedInterfaceOrientations
    }

    open override var shouldAutorotate: Bool {
        return visibleViewController?.shouldAutorotate ?? super.shouldAutorotate
    }
}

extension UITabBarController {
    open override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
        if let selected = selectedViewController {
            return selected.supportedInterfaceOrientations
        }
        return super.supportedInterfaceOrientations
    }

    open override var shouldAutorotate: Bool {
        if let selected = selectedViewController {
            return selected.shouldAutorotate
        }
        return super.shouldAutorotate
    }
}

Ora puoi sostituire il metodo supportedInterfaceOrientations o puoi eseguire override di shouldAutoRotate nel controller di visualizzazione che vuoi bloccare altrimenti puoi escludere le sostituzioni in altri controller di visualizzazione che vuoi ereditare dal comportamento di orientamento predefinito specificato nella plist della tua app

Disabilita rotazione

class ViewController: UIViewController {
    override func shouldAutorotate() -> Bool {
        return false
    }
}

Blocca sull'orientamento specifico

class ViewController: UIViewController {
    override func supportedInterfaceOrientations() -> Int {
        return Int(UIInterfaceOrientationMask.Landscape.rawValue)
    }
}

In teoria questo dovrebbe funzionare per tutte le gerarchie di controller di vista complesse, ma ho notato un problema con UITabBarController. Per qualche motivo, vuole usare un valore di orientamento predefinito. Consulta il seguente post sul blog se sei interessato a scoprire come risolvere alcuni dei problemi:

Blocca la rotazione dello schermo

Question

Prima di iOS 8, abbiamo utilizzato il codice riportato di seguito insieme a SupportInterfaceOrientations e dovremmo eseguire i metodi di delega di AutoPost per forzare l'orientamento delle app su qualsiasi orientamento particolare. Ho utilizzato sotto lo snippet di codice per ruotare l'app a livello di programmazione all'orientamento desiderato. Innanzitutto, sto cambiando l'orientamento della barra di stato. E quindi solo presentando e immediatamente ignorando una vista modale, la vista ruota all'orientamento desiderato.

[[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationLandscapeRight animated:YES];
UIViewController *c = [[UIViewController alloc]init];
[self presentViewController:vc animated:NO completion:nil];
[self dismissViewControllerAnimated:NO completion:nil];

Ma questo non funziona in iOS 8. Inoltre, ho visto alcune risposte nello in cui le persone hanno suggerito di evitare sempre questo approccio da iOS 8 in poi.

Per essere più specifici, la mia applicazione è un tipo universale di applicazione. Ci sono tre controller in totale.

  1. Controller First View - Dovrebbe supportare tutti gli orientamenti in iPad e solo portrait (pulsante home down) in iPhone.

  2. Controller Vista Secondaria - Dovrebbe supportare solo l'orizzontale in tutte le condizioni

  3. Controller Third View - Dovrebbe supportare solo l'orizzontale in tutte le condizioni

Stiamo utilizzando il controller di navigazione per la navigazione della pagina. Dal primo controller di visualizzazione, in un'azione di clic del pulsante, stiamo spingendo il secondo in pila. Quindi, quando arriva il secondo controller di visualizzazione, indipendentemente dall'orientamento del dispositivo, l'app dovrebbe bloccarsi solo in orizzontale.

Di seguito sono riportati i metodi shouldAutorotate e supportedInterfaceOrientations nel secondo e terzo controller di visualizzazione.

-(NSUInteger)supportedInterfaceOrientations{
    return UIInterfaceOrientationMaskLandscapeRight;
}

-(BOOL)shouldAutorotate {
    return NO;
}

C'è qualche soluzione per questo o qualsiasi altro modo migliore di bloccare un controller di visualizzazione in particolare orientamento per iOS 8. Per favore aiutatemi !!




Ho provato molte soluzioni, ma quella per cui ho lavorato è la seguente:

Non è necessario modificare info.plist in info.plist 8 e 9.

- (BOOL) shouldAutorotate {
    return NO;
}   

- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
    return (UIInterfaceOrientationPortrait | UIInterfaceOrientationPortraitUpsideDown);
}

Possibili orientamenti della documentazione Apple :

UIInterfaceOrientationUnknown

L'orientamento del dispositivo non può essere determinato.

UIInterfaceOrientationPortrait

Il dispositivo è in modalità verticale, con il dispositivo in posizione verticale e il tasto home in basso.

UIInterfaceOrientationPortraitUpsideDown

Il dispositivo è in modalità verticale ma capovolto, con il dispositivo tenuto in posizione verticale e il tasto home in alto.

UIInterfaceOrientationLandscapeLeft

Il dispositivo è in modalità orizzontale, con il dispositivo tenuto in posizione verticale e il tasto home sul lato sinistro.

UIInterfaceOrientationLandscapeRight

Il dispositivo è in modalità orizzontale, con il dispositivo tenuto in posizione verticale e il tasto home sul lato destro.




Questo dovrebbe funzionare da iOS 6 in su, ma l'ho provato solo su iOS 8. Sottoclassi UINavigationController e sovrascrivi i seguenti metodi:

- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
    return UIInterfaceOrientationLandscapeRight;
}

- (BOOL)shouldAutorotate {
    return NO;
}

Oppure chiedi al controller della vista visibile

- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
    return self.visibleViewController.preferredInterfaceOrientationForPresentation;
}

- (BOOL)shouldAutorotate {
    return self.visibleViewController.shouldAutorotate;
}

e implementare i metodi lì.




La soluzione migliore sopra:

let value = UIInterfaceOrientation.LandscapeLeft.rawValue
UIDevice.currentDevice().setValue(value, forKey: "orientation")

non ha funzionato per me quando l'ho chiamato in viewDidAppear del controller di visualizzazione presentato. Tuttavia ha funzionato quando l'ho chiamato in preparForSegue nel controller della vista di presentazione.

(Scusa, non abbastanza punti di reputazione per commentare questa soluzione, quindi ho dovuto aggiungerla in questo modo)




La mia soluzione

In AppDelegate :

func application(application: UIApplication, supportedInterfaceOrientationsForWindow window: UIWindow?) -> UIInterfaceOrientationMask {
    if let topController = UIViewController.topMostViewController() {
        if topController is XXViewController {
            return [.Portrait, .LandscapeLeft]
        }
    }
    return [.Portrait]
}

XXViewController è il ViewController che si desidera supportare in modalità Landscape.

Quindi la soluzione di Sunny Shah funzionerebbe nel tuo XXViewController su qualsiasi versione iOS:

let value = UIInterfaceOrientation.LandscapeLeft.rawValue
UIDevice.currentDevice().setValue(value, forKey: "orientation")

Questa è la funzione di utilità per trovare il ViewController più in alto.

extension UIViewController {

    /// Returns the current application's top most view controller.
    public class func topMostViewController() -> UIViewController? {
        let rootViewController = UIApplication.sharedApplication().windows.first?.rootViewController
        return self.topMostViewControllerOfViewController(rootViewController)
    }



    /// Returns the top most view controller from given view controller's stack.
    class func topMostViewControllerOfViewController(viewController: UIViewController?) -> UIViewController? {
        // UITabBarController
        if let tabBarController = viewController as? UITabBarController,
           let selectedViewController = tabBarController.selectedViewController {
            return self.topMostViewControllerOfViewController(selectedViewController)
        }

        // UINavigationController
        if let navigationController = viewController as? UINavigationController,
           let visibleViewController = navigationController.visibleViewController {
            return self.topMostViewControllerOfViewController(visibleViewController)
        }

        // presented view controller
        if let presentedViewController = viewController?.presentedViewController {
            return self.topMostViewControllerOfViewController(presentedViewController)
        }

        // child view controller
        for subview in viewController?.view?.subviews ?? [] {
            if let childViewController = subview.nextResponder() as? UIViewController {
                return self.topMostViewControllerOfViewController(childViewController)
            }
        }

        return viewController
    }

}



Secondo la risposta di Korey Hinton

Swift 2.2:

extension UINavigationController {
    public override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
        return visibleViewController!.supportedInterfaceOrientations()
    }
    public override func shouldAutorotate() -> Bool {
        return visibleViewController!.shouldAutorotate()
    }
}


extension UITabBarController {
    public override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
        if let selected = selectedViewController {
            return selected.supportedInterfaceOrientations()
        }
        return super.supportedInterfaceOrientations()
    }
    public override func shouldAutorotate() -> Bool {
        if let selected = selectedViewController {
            return selected.shouldAutorotate()
        }
        return super.shouldAutorotate()
    }
}

Disabilita rotazione

override func shouldAutorotate() -> Bool {
    return false
}

Blocca sull'orientamento specifico

override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
    return UIInterfaceOrientationMask.Portrait
}



Sembra che ci sia ancora qualche discussione su come meglio portare a termine questo compito, quindi ho pensato di condividere il mio approccio (di lavoro). Aggiungi il seguente codice nell'implementazione di UIViewController :

- (void) viewWillAppear:(BOOL)animated
{
    [UIViewController attemptRotationToDeviceOrientation];
}

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

-(BOOL)shouldAutorotate
{
    return NO;
}

- (UIInterfaceOrientationMask)supportedInterfaceOrientations
{
    return UIInterfaceOrientationMaskLandscapeLeft;
}

Per questo esempio, dovrai anche impostare gli orientamenti dei dispositivi consentiti su "Paesaggio a sinistra" nelle impostazioni del progetto (o direttamente in info.plist ). Basta modificare l'orientamento specifico che si desidera forzare se si desidera qualcosa di diverso da LandscapeLeft.

La chiave per me era la chiamata attemptRotationToDeviceOrientation in viewWillAppear - senza che la vista non ruotasse correttamente senza ruotare fisicamente il dispositivo.




Prima di tutto - questa è una cattiva idea, in generale, c'è qualcosa di sbagliato nell'architettura della tua app, ma, sh..t happens , se è così, puoi provare a fare qualcosa di simile qui sotto:

final class OrientationController {

    static private (set) var allowedOrientation:UIInterfaceOrientationMask = [.all]

    // MARK: - Public

    class func lockOrientation(_ orientationIdiom: UIInterfaceOrientationMask) {
        OrientationController.allowedOrientation = [orientationIdiom]
    }

    class func forceLockOrientation(_ orientation: UIInterfaceOrientation) {
        var mask:UIInterfaceOrientationMask = []
        switch orientation {
            case .unknown:
                mask = [.all]
            case .portrait:
                mask = [.portrait]
            case .portraitUpsideDown:
                mask = [.portraitUpsideDown]
            case .landscapeLeft:
                mask = [.landscapeLeft]
            case .landscapeRight:
                mask = [.landscapeRight]
        }

        OrientationController.lockOrientation(mask)

        UIDevice.current.setValue(orientation.rawValue, forKey: "orientation")
    }
}

Di, in AppDelegate

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
    // do stuff
    OrientationController.lockOrientation(.portrait)
    return true
}

// MARK: - Orientation

func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
    return OrientationController.allowedOrientation
}

E ogni volta che vuoi cambiare orientamento fai come:

OrientationController.forceLockOrientation(.landscapeRight)

Nota: a volte il dispositivo potrebbe non aggiornarsi da tale chiamata, pertanto potrebbe essere necessario eseguire le operazioni seguenti

OrientationController.forceLockOrientation(.portrait)
OrientationController.forceLockOrientation(.landscapeRight)

È tutto




[iOS9 +] Se qualcuno ha trascinato fino in fondo, visto che nessuna delle soluzioni precedenti funzionava, e se si presenta la vista che si desidera cambiare orientamento in seguito, si potrebbe voler controllare questo.

Controlla il tipo di presentazione del tuo seguito. Il mio era "contesto attuale". Quando l'ho modificato su Schermo intero, ha funzionato.

Grazie a @GabLeRoux, ho trovato questa soluzione.

  • Questo cambiamento funziona solo se combinato con le soluzioni di cui sopra.



Per me, il VC di livello superiore doveva implementare gli override di orientamento. L'uso di VC nello stack non avrà alcun effetto se il VC principale non sta implementando.

VC-main
    |
    -> VC 2
        |
        -> VC 3

Solo VC-Main viene ascoltato, essenzialmente nei miei test.




Su Xcode 8 i metodi sono convertiti in proprietà, quindi il seguente funziona con Swift :

override public var supportedInterfaceOrientations: UIInterfaceOrientationMask {
    return UIInterfaceOrientationMask.portrait
}

override public var preferredInterfaceOrientationForPresentation: UIInterfaceOrientation {
    return UIInterfaceOrientation.portrait
}

override public var shouldAutorotate: Bool {
    return true
}






Related