Qual è il modo "giusto" per gestire i cambiamenti di orientamento in iOS 8?


104

Qualcuno può dirmi l'approccio "giusto" o "migliore" per lavorare con gli orientamenti dell'interfaccia verticale e orizzontale in iOS 8? Sembra che tutte le funzioni che voglio utilizzare a tale scopo siano deprecate in iOS 8 e la mia ricerca non ha trovato un'alternativa chiara ed elegante. Devo davvero guardare la larghezza e l'altezza per determinare da solo se siamo in modalità verticale o orizzontale?

Ad esempio, nel mio controller di visualizzazione, come devo implementare il seguente pseudocodice?

if we are rotating from portrait to landscape then
  do portrait things
else if we are rotating from landscape to portrait then
  do landscape things

3
Leggi i documenti per UIViewController. Vedi la sezione intitolata "Gestire le rotazioni della vista". Spiega cosa dovresti fare.
rmaddy

Che siano deprecati è un indizio. Devi usare qualcos'altro .... che qualcos'altro dovrebbe essere AutoLayout e Size Classes :-)
Aaron

Risposte:


262

Apple consiglia di utilizzare le classi di dimensioni come misura approssimativa della quantità di spazio disponibile sullo schermo, in modo che la tua interfaccia utente possa modificare in modo significativo il layout e l'aspetto. Considera che un iPad in verticale ha le stesse classi di dimensioni che ha in orizzontale (larghezza normale, altezza normale). Ciò significa che la tua interfaccia utente dovrebbe essere più o meno simile tra i due orientamenti.

Tuttavia, il passaggio da verticale a orizzontale in un iPad è abbastanza significativo che potrebbe essere necessario apportare alcune piccole modifiche all'interfaccia utente, anche se le classi di dimensioni non sono cambiate. Poiché i metodi relativi all'orientamento dell'interfaccia UIViewControllersono stati deprecati, Apple consiglia ora di implementare il seguente nuovo metodo in UIViewControllersostituzione:

- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id <UIViewControllerTransitionCoordinator>)coordinator
{
    [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];

    // Code here will execute before the rotation begins.
    // Equivalent to placing it in the deprecated method -[willRotateToInterfaceOrientation:duration:]

    [coordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> context) {

        // Place code here to perform animations during the rotation.
        // You can pass nil or leave this block empty if not necessary.

    } completion:^(id<UIViewControllerTransitionCoordinatorContext> context) {

        // Code here will execute after the rotation has finished.
        // Equivalent to placing it in the deprecated method -[didRotateFromInterfaceOrientation:]

    }];
}

Grande! Ora ricevi i callback subito prima dell'inizio della rotazione e dopo che è terminata. Ma che dire di sapere effettivamente se la rotazione è in verticale o in orizzontale?

Apple consiglia di considerare la rotazione come una semplice modifica delle dimensioni della vista principale. In altre parole, durante una rotazione dell'iPad da verticale a orizzontale, puoi pensarla come la vista a livello di radice semplicemente cambiando la sua bounds.sizeda {768, 1024}a {1024, 768}. Sapendo questo, dovresti usare il metodo sizepassato nel viewWillTransitionToSize:withTransitionCoordinator:metodo sopra per capire se stai ruotando in verticale o in orizzontale.

Se desideri un modo ancora più semplice per migrare il codice legacy al nuovo modo di fare le cose di iOS 8, considera l'utilizzo di questa semplice categoria su UIView, che può essere utilizzata per determinare se una vista è "verticale" o "orizzontale" in base alla sua taglia.

Ricapitolando:

  1. Dovresti utilizzare le classi di dimensioni per determinare quando mostrare interfacce utente fondamentalmente diverse (ad esempio un'interfaccia utente "simile a iPhone" rispetto a un'interfaccia utente "simile a iPad")
  2. Se è necessario apportare piccole modifiche all'interfaccia utente quando le classi di dimensioni non cambiano ma le dimensioni del contenitore (vista padre) viewWillTransitionToSize:withTransitionCoordinator:sì , ad esempio quando ruota un iPad, usa la richiamata in UIViewController.
  3. Ogni visualizzazione nella tua app dovrebbe prendere decisioni di layout solo in base allo spazio in cui è stata assegnata al layout. Lascia che la gerarchia naturale delle visualizzazioni ricolleghi queste informazioni a cascata.
  4. Allo stesso modo, non utilizzare statusBarOrientation- che è fondamentalmente una proprietà a livello di dispositivo - per determinare se impaginare una vista per "ritratto" rispetto a "paesaggio". L'orientamento della barra di stato dovrebbe essere utilizzato solo dal codice che si occupa di cose come UIWindowche in realtà vivono al livello principale dell'app.

5
Ottima risposta! A proposito, il tuo video di YouTube su AL è stato incredibilmente informativo. Grazie per la condivisione. Controlla! youtube.com/watch?v=taWaW2GzfCI
smileBot

2
L'unico problema che ho riscontrato è che a volte vuoi conoscere l'orientamento del dispositivo e questo non te lo fa sapere. Su iPad viene chiamato prima che la barra di stato o il valore di orientamento del dispositivo cambino. Non puoi dire se l'utente tiene il dispositivo in verticale o in verticale capovolto. È anche impossibile testare, perché prendere in giro UIViewControllerTransitionCoordinatorè un incubo :-(
Darrarski

@Darrarski hai trovato una soluzione elegante per far fronte a questi problemi?
Xvolks

allora dov'è viewdidlayoutsubviews? Ho pensato che viewdidlayoutsubviewssia usato per catturare le modifiche del limite dovute alla rotazione. Puoi approfondire questo?
Honey

1
Come distinguere tra landscape left e landscape right se non usiamo l'orientamento della barra di stato? Ho bisogno di quella distinzione. Inoltre, ci sono altri problemi come descritto qui - stackoverflow.com/questions/53364498/…
Deepak Sharma

17

Basato sulla risposta molto ben dettagliata (e accettata) di smileyborg, ecco un adattamento che utilizza swift 3:

override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
    super.viewWillTransition(to: size, with: coordinator)
    coordinator.animate(alongsideTransition: nil, completion: {
        _ in
        self.collectionView.collectionViewLayout.invalidateLayout()
    })        
}

E UICollectionViewDelegateFlowLayoutnell'implementazione,

public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
    // retrieve the updated bounds
    let itemWidth = collectionView.bounds.width
    let itemHeight = collectionView.bounds.height
    // do whatever you need to do to adapt to the new size
}

11

Uso semplicemente il Centro notifiche:

Aggiungi una variabile di orientamento (spiegherà alla fine)

//Above viewdidload
var orientations:UIInterfaceOrientation = UIApplication.sharedApplication().statusBarOrientation

Aggiungi notifica quando viene visualizzata la vista

override func viewDidAppear(animated: Bool) {
    NSNotificationCenter.defaultCenter().addObserver(self, selector: "orientationChanged:", name: UIDeviceOrientationDidChangeNotification, object: nil)
}

Rimuovi notifica quando la visualizzazione scompare

override func viewWillDisappear(animated: Bool) {
        NSNotificationCenter.defaultCenter().removeObserver(self, name: UIDeviceOrientationDidChangeNotification, object: nil) 
}

Ottiene l'orientamento corrente quando viene attivata la notifica

func orientationChanged (notification: NSNotification) {
    adjustViewsForOrientation(UIApplication.sharedApplication().statusBarOrientation)
}

Controlla l'orientamento (verticale / orizzontale) e gestisce gli eventi

func adjustViewsForOrientation(orientation: UIInterfaceOrientation) {
    if (orientation == UIInterfaceOrientation.Portrait || orientation == UIInterfaceOrientation.PortraitUpsideDown)
    {
        if(orientation != orientations) {
            println("Portrait")
            //Do Rotation stuff here
            orientations = orientation
        }
    }
    else if (orientation == UIInterfaceOrientation.LandscapeLeft || orientation == UIInterfaceOrientation.LandscapeRight)
    {
       if(orientation != orientations) {
            println("Landscape")
            //Do Rotation stuff here
            orientations = orientation
        }
    }
}

Il motivo per cui aggiungo una variabile di orientamento è perché quando si esegue il test su un dispositivo fisico, la notifica di orientamento viene richiamata ad ogni piccola mossa nel dispositivo, e non solo quando ruota. L'aggiunta delle istruzioni var e if chiama il codice solo se è passato all'orientamento opposto.


11
Questo non è l'approccio consigliato da Apple perché significa che ogni visualizzazione nella tua app sta decidendo come visualizzare in base all'orientamento del dispositivo, invece di considerare lo spazio che è stato dato.
smileyborg

2
Anche Apple non prende in considerazione l'hardware con 8.0. Dì a un livello AVPreview che non deve preoccuparsi dell'orientamento se viene presentato su un controller di visualizzazione. Non funziona, ma è stato risolto in 8.2
Nick Turner

superdi viewDidAppeare viewWillDisappeardovrebbe essere chiamato
Andrew Bogaevskyi

Nota: UIDeviceOrientation! = UIInterfaceOrientation. Nei miei esperimenti, statusBarOrientation non è affidabile.
nnrales

2

Dal punto di vista dell'interfaccia utente, credo che l'utilizzo delle classi di dimensioni sia l'approccio consigliato da Apple per la gestione delle interfacce in diversi orientamenti, dimensioni e scale.

Vedi la sezione: I tratti descrivono la classe di dimensioni e la scala di un'interfaccia qui: https://developer.apple.com/library/ios/releasenotes/General/WhatsNewIniOS/Articles/iOS8.html

"iOS 8 aggiunge nuove funzionalità che rendono la gestione delle dimensioni e dell'orientamento dello schermo molto più versatile."

Anche questo è un buon articolo: https://carpeaqua.com/thinking-in-terms-of-ios-8-size-classes/

EDIT Link aggiornato: https://carpeaqua.com/2014/06/14/thinking-in-terms-of-ios-8-size-classes/ (Credit: Koen)


Sì, sembra che il suo intero sito non sia attivo al momento.
Aaron

L'articolo merita davvero di essere letto ed ecco l'indirizzo corretto: carpeaqua.com/thinking-in-terms-of-ios-8-size-classes
uem

Ma cosa succede se non stiamo parlando di classi di dimensioni e interfaccia utente, ma di AVFoundation, ad esempio. Quando si registra un video, in alcuni casi è necessario conoscere l'orientamento dei viewController per impostare i metadati corretti. Questo è semplice impossibile. "versatile".
Julian F. Weinert

Non è ciò di cui l'OP stava chiedendo / parlando.
Aaron

1
Link aggiornato: carpeaqua.com/2014/06/14/…
koen
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.