Jaki jest „właściwy” sposób obsługi zmian orientacji w iOS 8?


104

Czy ktoś może mi powiedzieć „właściwe” lub „najlepsze” podejście do pracy z orientacją pionową i poziomą interfejsu w iOS 8? Wygląda na to, że wszystkie funkcje, których chcę używać do tego celu, są przestarzałe w iOS 8, a moje badania nie wykazały jasnej, eleganckiej alternatywy. Czy naprawdę mam spojrzeć na szerokość i wysokość, aby samodzielnie określić, czy jesteśmy w trybie pionowym czy poziomym?

Na przykład w moim kontrolerze widoku, jak zaimplementować następujący pseudokod?

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
Przeczytaj dokumentację dotyczącą UIViewController. Zobacz sekcję zatytułowaną „Obsługa
obrotów

To, że są przestarzałe, jest wskazówką. Musisz użyć czegoś innego ... to coś innego powinno być AutoLayout and Size Classes :-)
Aaron

Odpowiedzi:


263

Apple zaleca używanie klas rozmiaru jako przybliżonej miary dostępnej przestrzeni na ekranie, aby interfejs użytkownika mógł znacząco zmienić jego układ i wygląd. Weź pod uwagę, że iPad w orientacji pionowej ma te same klasy rozmiarów, co w orientacji poziomej (standardowa szerokość, standardowa wysokość). Oznacza to, że Twój interfejs użytkownika powinien być mniej więcej podobny między dwiema orientacjami.

Jednak zmiana z pionowej na poziomą w iPadzie jest na tyle znacząca, że ​​może być konieczne wprowadzenie mniejszych zmian w interfejsie użytkownika, nawet jeśli klasy rozmiarów nie uległy zmianie. Ponieważ metody związane z orientacją interfejsu UIViewControllerzostały wycofane, firma Apple zaleca teraz zaimplementowanie następującej nowej metody UIViewControllerjako zamiennika:

- (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:]

    }];
}

Wspaniały! Teraz otrzymujesz wywołania zwrotne tuż przed rozpoczęciem rotacji i po jej zakończeniu. Ale co z faktyczną wiedzą, czy obrót ma charakter pionowy, czy poziomy?

Apple zaleca myślenie o obracaniu jako po prostu zmianie rozmiaru widoku rodzica. Innymi słowy, podczas obracania iPada z orientacji pionowej do poziomej, możesz myśleć o nim jako o widoku na poziomie głównym, po prostu zmieniając go bounds.sizez {768, 1024}na {1024, 768}. Wiedząc o tym, powinieneś użyć metody sizeprzekazanej do viewWillTransitionToSize:withTransitionCoordinator:powyższej metody, aby dowiedzieć się, czy obracasz do pionu, czy do krajobrazu.

Jeśli chcesz jeszcze bardziej bezproblemowo migrować stary kod do nowego sposobu wykonywania zadań w iOS 8, rozważ skorzystanie z tej prostej kategorii w UIView, której można użyć do określenia, czy widok jest „pionowy” czy „poziomy” na podstawie rozmiar.

Przypomnę:

  1. Należy użyć klas wielkości, aby określić, kiedy wyświetlać zasadniczo różne interfejsy użytkownika (np. Interfejs „podobny do iPhone'a” a „podobny do iPada”).
  2. Jeśli musisz wprowadzić mniejsze zmiany w interfejsie użytkownika, gdy klasy rozmiaru nie zmieniają się, ale rozmiar kontenera (widok nadrzędny) tak się dzieje, na przykład gdy obraca się iPad, użyj viewWillTransitionToSize:withTransitionCoordinator:wywołania zwrotnego w UIViewController.
  3. Każdy widok w aplikacji powinien podejmować decyzje dotyczące układu wyłącznie na podstawie przestrzeni, w której został przydzielony układ. Pozwól, aby naturalna hierarchia widoków powodowała kaskadowanie tych informacji.
  4. Podobnie nie używaj właściwości statusBarOrientation- która jest w zasadzie właściwością na poziomie urządzenia - do określania, czy układ widoku ma być „pionowy” czy „poziomy”. Orientacja paska stanu powinna być używana tylko przez kod zajmujący się rzeczami, UIWindowktóre faktycznie znajdują się na samym poziomie głównym aplikacji.

5
Doskonała odpowiedź! Swoją drogą, twój film z YouTube na AL był niesamowicie pouczający. Dzięki za udostępnienie. Sprawdź to! youtube.com/watch?v=taWaW2GzfCI
smileBot

2
Jedyny problem, jaki znalazłem, to to, że czasami chcesz poznać orientację urządzenia, a to nie da ci tego znać. Na iPadzie jest wywoływana przed zmianą wartości paska stanu lub orientacji urządzenia. Nie możesz stwierdzić, czy użytkownik trzyma urządzenie w pozycji pionowej, czy pionowej do góry nogami. UIViewControllerTransitionCoordinator
Tego

@Darrarski Czy znalazłeś eleganckie rozwiązanie, aby poradzić sobie z tymi problemami?
Xvolks

więc gdzie jest viewdidlayoutsubviews? Myślę, że viewdidlayoutsubviewsjest używany do rejestrowania zmian związanych z obrotem. Czy możesz to rozwinąć?
Honey

1
Jak odróżnić krajobraz lewy od poziomego prawego, jeśli nie używamy orientacji paska stanu? Potrzebuję tego rozróżnienia. Poza tym istnieją inne problemy opisane tutaj - stackoverflow.com/questions/53364498/…
Deepak Sharma

17

W oparciu o bardzo szczegółową (i akceptowaną) odpowiedź Smileyborga, oto adaptacja wykorzystująca 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()
    })        
}

A w UICollectionViewDelegateFlowLayoutrealizacji

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

Po prostu korzystam z Centrum powiadomień:

Dodaj zmienną orientacji (wyjaśnię na końcu)

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

Dodaj powiadomienie, gdy pojawi się widok

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

Usuń powiadomienie, gdy widok zniknie

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

Pobiera aktualną orientację po wyzwoleniu powiadomienia

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

Sprawdza orientację (pionową / poziomą) i obsługuje zdarzenia

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
        }
    }
}

Powodem dodania zmiennej orientacji jest to, że podczas testowania na urządzeniu fizycznym powiadomienie o orientacji jest wywoływane przy każdym drobnym ruchu w urządzeniu, a nie tylko wtedy, gdy się obraca. Dodanie instrukcji var i if wywołuje kod tylko wtedy, gdy został przełączony na przeciwną orientację.


11
Nie jest to zalecane podejście firmy Apple, ponieważ oznacza to, że każdy widok w aplikacji decyduje o sposobie wyświetlania w oparciu o orientację urządzenia, zamiast brać pod uwagę przydzieloną przestrzeń.
Smileyborg

2
Apple również nie bierze pod uwagę sprzętu w wersji 8.0. Poinformuj warstwę AVPreview, że nie musi martwić się o orientację, jeśli jest prezentowana przez kontroler widoku. Nie działa, ale naprawiono to w wersji 8.2
Nick Turner,

superod viewDidAppeari viewWillDisappearpowinna nazywać
Andrew Bogaevskyi

Uwaga: UIDeviceOrientation! = UIInterfaceOrientation. W moich eksperymentach statusBarOrientation nie jest wiarygodna.
nnrales

2

Z perspektywy interfejsu użytkownika uważam, że używanie klas rozmiaru jest zalecanym przez Apple podejściem do obsługi interfejsów w różnych orientacjach, rozmiarach i skalach.

Zobacz sekcję: Cechy opisujące klasę rozmiaru i skalę interfejsu tutaj: https://developer.apple.com/library/ios/releasenotes/General/WhatsNewIniOS/Articles/iOS8.html

„iOS 8 dodaje nowe funkcje, dzięki którym radzenie sobie z rozmiarem i orientacją ekranu jest znacznie bardziej wszechstronne”.

Ten też jest dobrym artykułem: https://carpeaqua.com/thinking-in-terms-of-ios-8-size-classes/

EDYTUJ Zaktualizowany link: https://carpeaqua.com/2014/06/14/thinking-in-terms-of-ios-8-size-classes/ (Źródło: Koen)


Tak, wygląda na to, że cała jego witryna nie działa w tej chwili.
Aaron

Artykuł jest rzeczywiście wart przeczytania i tutaj jest poprawny adres: carpeaqua.com/thinking-in-terms-of-ios-8-size-classes
uem

Ale co, jeśli nie mówimy o klasach wielkości i interfejsie użytkownika, ale na przykład o AVFoundation. Podczas nagrywania wideo w niektórych przypadkach musisz znać orientację viewControllers, aby ustawić prawidłowe metadane. To jest po prostu niemożliwe. "wszechstronny".
Julian F. Weinert

Nie o to pytał / mówił OP.
Aaron

1
Zaktualizowany link: carpeaqua.com/2014/06/14/…
koen
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.