Apa cara yang "benar" untuk menangani perubahan orientasi di iOS 8?

104

Bisakah seseorang memberi tahu saya pendekatan "benar" atau "terbaik" untuk bekerja dengan orientasi antarmuka potret dan lanskap di iOS 8? Tampaknya semua fungsi yang ingin saya gunakan untuk tujuan itu sudah tidak digunakan lagi di iOS 8, dan penelitian saya tidak menemukan alternatif yang jelas dan elegan. Apakah saya benar-benar harus melihat lebar dan tinggi untuk menentukan sendiri apakah kita dalam mode potret atau lanskap?

Misalnya, dalam pengontrol tampilan saya, bagaimana cara mengimplementasikan kodesemu berikut?

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
rmp251
sumber
3
Baca dokumen untuk UIViewController. Lihat bagian berjudul "Menangani Rotasi Tampilan. Ini menjelaskan apa yang harus Anda lakukan.
rmaddy
Bahwa mereka tidak digunakan lagi adalah petunjuk. Anda harus menggunakan sesuatu yang lain .... bahwa sesuatu yang lain harus AutoLayout dan Kelas Ukuran :-)
Aaron

Jawaban:

262

Apple merekomendasikan penggunaan kelas ukuran sebagai ukuran kasar dari seberapa banyak ruang layar yang tersedia, sehingga UI Anda dapat mengubah tata letak dan tampilannya secara signifikan. Pertimbangkan bahwa iPad dalam potret memiliki kelas ukuran yang sama seperti pada lanskap (Lebar reguler, tinggi reguler). Artinya, UI Anda harus lebih atau kurang mirip di antara kedua orientasi.

Namun, perubahan dari potret ke lanskap di iPad cukup signifikan sehingga Anda mungkin perlu melakukan beberapa penyesuaian yang lebih kecil pada UI, meskipun kelas ukurannya tidak berubah. Karena metode yang terkait dengan orientasi antarmuka UIViewControllersudah tidak digunakan lagi, Apple sekarang merekomendasikan penerapan metode baru berikut UIViewControllersebagai penggantinya:

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

    }];
}

Bagus! Sekarang Anda mendapatkan callback tepat sebelum rotasi dimulai, dan setelah selesai. Tapi bagaimana dengan mengetahui sebenarnya apakah rotasi untuk potret atau lanskap?

Apple merekomendasikan pemikiran tentang rotasi hanya sebagai perubahan ukuran tampilan induk. Dengan kata lain, selama rotasi iPad dari potret ke lanskap, Anda dapat menganggapnya sebagai tampilan tingkat akar yang hanya mengubah bounds.sizedari {768, 1024}menjadi {1024, 768}. Mengetahui hal ini kemudian, Anda harus menggunakan sizepassing ke viewWillTransitionToSize:withTransitionCoordinator:metode di atas untuk mengetahui apakah Anda memutar ke potret atau lanskap.

Jika Anda menginginkan cara yang lebih mulus untuk memigrasi kode lama ke cara baru iOS 8 dalam melakukan sesuatu, pertimbangkan untuk menggunakan kategori sederhana ini di UIView, yang dapat digunakan untuk menentukan apakah tampilan adalah "potret" atau "lanskap" berdasarkan ukuran.

Rangkuman:

  1. Anda harus menggunakan kelas ukuran untuk menentukan kapan harus menampilkan UI yang sangat berbeda (mis. UI "seperti iPhone" vs. UI "seperti iPad")
  2. Jika Anda perlu membuat penyesuaian yang lebih kecil pada UI Anda saat kelas ukuran tidak berubah tetapi ukuran container (tampilan orang tua) Anda berubah, seperti saat iPad berputar, gunakan viewWillTransitionToSize:withTransitionCoordinator:callback di UIViewController.
  3. Setiap tampilan di aplikasi Anda seharusnya hanya membuat keputusan tata letak berdasarkan ruang yang telah diberikan untuk tata letak. Biarkan hierarki tampilan alami menurunkan informasi ini.
  4. Demikian pula, jangan gunakan statusBarOrientation- yang pada dasarnya adalah properti tingkat perangkat - untuk menentukan apakah akan menata tampilan untuk "potret" vs "lanskap". Orientasi bilah status sebaiknya hanya digunakan oleh kode yang menangani hal-hal seperti UIWindowyang sebenarnya ada di tingkat paling dasar aplikasi.
smileyborg
sumber
5
Jawaban yang sangat bagus! BTW, video youtube Anda di AL sangat informatif. Terima kasih untuk berbagi. Saksikan berikut ini! youtube.com/watch?v=taWaW2GzfCI
smileBot
2
Satu-satunya masalah yang saya temukan adalah terkadang Anda ingin mengetahui orientasi perangkat, dan ini tidak akan memberi tahu Anda. Di iPad, ini dipanggil sebelum bilah status atau nilai orientasi perangkat berubah. Anda tidak dapat mengetahui apakah pengguna memegang perangkat dalam potret atau potret-terbalik. Ini juga tidak mungkin untuk diuji, karena mengejek UIViewControllerTransitionCoordinatoradalah mimpi buruk :-(
Darrarski
@Darrarski sudahkah Anda menemukan solusi elegan untuk mengatasi masalah tersebut?
Xvolks
jadi dimana viewdidlayoutsubviewskemudian? Saya pikir viewdidlayoutsubviewsdigunakan untuk menangkap perubahan terikat karena rotasi. Bisakah Anda menjelaskan lebih lanjut tentang itu?
Madu
1
Bagaimana membedakan antara lanskap kiri dan lanskap kanan jika kita tidak menggunakan statusbarorientasi? Saya membutuhkan perbedaan itu. Selain itu, ada masalah lain seperti yang dijelaskan di sini - stackoverflow.com/questions/53364498/…
Deepak Sharma
17

Berdasarkan jawaban smileyborg yang sangat detail (dan diterima), berikut ini adaptasi menggunakan 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()
    })        
}

Dan dalam UICollectionViewDelegateFlowLayoutimplementasinya,

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
}
CMont
sumber
11

Saya hanya menggunakan Pusat Pemberitahuan:

Tambahkan variabel orientasi (akan dijelaskan di bagian akhir)

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

Tambahkan Pemberitahuan saat tampilan muncul

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

Hapus Pemberitahuan saat tampilan hilang

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

Mendapat orientasi saat ini ketika notifikasi dipicu

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

Memeriksa orientasi (potret / lanskap) dan menangani acara

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

Alasan saya menambahkan variabel orientasi adalah karena saat menguji pada perangkat fisik, notifikasi orientasi dipanggil pada setiap gerakan kecil di perangkat, dan tidak hanya saat itu berputar. Menambahkan pernyataan var dan if hanya memanggil kode jika dialihkan ke orientasi yang berlawanan.

inVINCEable
sumber
11
Ini bukan pendekatan yang direkomendasikan Apple karena ini berarti setiap tampilan di aplikasi Anda memutuskan cara menampilkan berdasarkan orientasi perangkat, alih-alih mempertimbangkan ruang yang telah diberikan.
smileyborg
2
Apple juga tidak mempertimbangkan perangkat keras dengan 8.0. Beri tahu lapisan AVPreview bahwa ia tidak perlu mengkhawatirkan orientasi jika ditampilkan melalui pengontrol tampilan. Tidak berfungsi, tapi sudah diperbaiki di 8.2
Nick Turner
superdari viewDidAppeardan viewWillDisappearharus dipanggil
Andrew Bogaevskyi
Catatan: UIDeviceOrientation! = UIInterfaceOrientation. Dalam percobaan saya, statusBarOrientation tidak dapat diandalkan.
nnrales
2

Dari perspektif UI, saya percaya bahwa menggunakan Kelas Ukuran adalah pendekatan yang direkomendasikan Apple untuk menangani antarmuka dalam orientasi, ukuran, dan skala yang berbeda.

Lihat bagian: Sifat Jelaskan Kelas Ukuran dan Skala Antarmuka di sini: https://developer.apple.com/library/ios/releasenotes/General/WhatsNewIniOS/Articles/iOS8.html

"iOS 8 menambahkan fitur baru yang membuat pengaturan ukuran dan orientasi layar jauh lebih serbaguna."

Yang ini artikel yang bagus juga: https://carpeaqua.com/thinking-in-terms-of-ios-8-size-classes/

EDIT Tautan yang Diperbarui: https://carpeaqua.com/2014/06/14/thinking-in-terms-of-ios-8-size-classes/ (Sumber: Koen)

Aaron
sumber
Ya, sepertinya seluruh situsnya sedang tidak aktif.
Aaron
Artikel ini memang layak dibaca dan inilah alamat yang benar: carpeaqua.com/thinking-in-terms-of-ios-8-size-classes
uem
Tetapi bagaimana jika kita tidak berbicara tentang kelas ukuran dan UI, tetapi tentang AVFoundation, misalnya. Saat merekam video, dalam beberapa kasus Anda harus mengetahui orientasi viewControllers untuk menyetel metadata yang benar. Ini tidak mungkin sederhana. "serba guna".
Julian F. Weinert
Bukan itu yang ditanyakan / dibicarakan OP.
Aaron
1
Tautan diperbarui: carpeaqua.com/2014/06/14/…
koen