Apa sebenarnya yang dilakukan addChildViewController?

102

Saya baru saja mencelupkan kaki saya untuk pertama kalinya ke dalam pengembangan iOS, dan salah satu hal pertama yang harus saya lakukan adalah menerapkan pengontrol tampilan kontainer khusus - sebut saja SideBarViewController- yang menukar beberapa kemungkinan pengontrol tampilan anak itu menunjukkan, hampir persis seperti Tab Bar Controller standar . (Ini cukup banyak Tab Bar Controller tetapi dengan menu samping yang dapat disembunyikan, bukan bilah tab.)

Sesuai instruksi dalam dokumentasi Apple, saya menelepon addChildViewControllersetiap kali saya menambahkan ViewController anak ke penampung saya. Kode saya untuk menukar pengontrol tampilan anak saat ini yang ditunjukkan oleh SideBarViewControllertampilan seperti ini:

- (void)showViewController:(UIViewController *)newViewController {
    UIViewController* oldViewController = [self.childViewControllers 
                                           objectAtIndex:0];
    
    [oldViewController removeFromParentViewController];
    [oldViewController.view removeFromSuperview];
    
    newViewController.view.frame = CGRectMake(
        0, 0, self.view.frame.size.width, self.view.frame.size.height
    );
    [self addChildViewController: newViewController];
    [self.view addSubview: newViewController.view];
}

Kemudian saya mulai mencoba mencari tahu apa yang addChildViewControllerterjadi di sini, dan saya menyadari bahwa saya tidak tahu. Selain menempelkan yang baru ViewControllerdi .childViewControllersarray, tampaknya tidak berpengaruh pada apa pun. Tindakan dan jalan keluar dari pandangan pengontrol anak ke pengontrol anak yang telah saya setel di papan cerita masih berfungsi dengan baik bahkan jika saya tidak pernah menelepon addChildViewController, dan saya tidak bisa membayangkan apa lagi yang bisa terpengaruh.

Memang, jika saya menulis ulang kode saya untuk tidak menelepon addChildViewController, dan malah terlihat seperti ini ...

- (void)showViewController:(UIViewController *)newViewController {

    // Get the current child from a member variable of `SideBarViewController`
    UIViewController* oldViewController = currentChildViewController;

    [oldViewController.view removeFromSuperview];

    newViewController.view.frame = CGRectMake(
        0, 0, self.view.frame.size.width, self.view.frame.size.height
    );
    [self.view addSubview: newViewController.view];

    currentChildViewController = newViewController;
}

... maka aplikasi saya masih berfungsi dengan sempurna, sejauh yang saya tahu!

Dokumentasi Apple tidak menjelaskan banyak tentang apa yang addChildViewControllerdilakukannya, atau mengapa kami harus menyebutnya. Keseluruhan deskripsi yang relevan tentang apa yang dilakukan metode atau mengapa metode itu harus digunakan dalam bagiannya di UIViewControllerReferensi Kelas adalah, saat ini:

Menambahkan pengontrol tampilan yang diberikan sebagai anak. ... Metode ini hanya dimaksudkan untuk dipanggil dengan implementasi pengontrol tampilan penampung khusus. Jika Anda mengganti metode ini, Anda harus memanggil super dalam implementasi Anda.

Ada juga paragraf ini sebelumnya di halaman yang sama:

Pengontrol tampilan penampung Anda harus mengaitkan pengontrol tampilan anak dengan dirinya sendiri sebelum menambahkan tampilan root anak ke hierarki tampilan. Ini memungkinkan iOS untuk merutekan peristiwa dengan benar ke pengontrol tampilan anak dan tampilan yang dikelola pengontrol tersebut. Demikian pula, setelah menghapus tampilan root anak dari hierarki tampilannya, pengontrol tampilan anak tersebut harus diputuskan dari dirinya sendiri. Untuk membuat atau menghancurkan asosiasi ini, penampung Anda memanggil metode tertentu yang ditentukan oleh kelas dasar. Metode ini tidak dimaksudkan untuk dipanggil oleh klien kelas penampung Anda; mereka hanya akan digunakan oleh implementasi penampung Anda untuk memberikan perilaku penahanan yang diharapkan.

Berikut adalah metode penting yang mungkin perlu Anda panggil:

addChildViewController:
removeFromParentViewController
willMoveToParentViewController:
didMoveToParentViewController:

tetapi tidak menawarkan petunjuk apa pun tentang 'peristiwa' atau 'perilaku penahanan yang diharapkan' yang dibicarakannya, atau mengapa (atau bahkan ketika) menyebut metode ini 'penting'.

Contoh pengontrol tampilan penampung khusus di bagian "Pengontrol Tampilan Penampung Khusus" dari dokumentasi Apple semuanya memanggil metode ini, jadi saya berasumsi bahwa metode ini melayani beberapa tujuan penting lebih dari sekadar memunculkan ViewController anak ke dalam larik, tetapi saya tidak mengerti tahu apa tujuan itu. Apa fungsi metode ini, dan mengapa saya harus menyebutnya?

Mark Amery
sumber
3
Halaman video WWDC Apple 2011 memiliki sesi yang bagus ("Menerapkan UIViewController Containment") tentang topik ini.
Alladinian

Jawaban:

94

Saya juga bertanya-tanya tentang pertanyaan ini. Saya menonton video Sesi 102 dari WWDC 2011 dan Mr. View Controller, Bruce D. Nilo , mengatakan ini:

viewWillAppear:,, viewDidAppear:dll tidak ada hubungannya dengan addChildViewController:. Yang addChildViewController:dilakukan hanyalah mengatakan "Pengontrol tampilan ini adalah anak dari pengontrol itu" dan tidak ada hubungannya dengan tampilan tampilan. Ketika mereka dipanggil dikaitkan dengan saat tampilan masuk dan keluar dari hierarki jendela.

Jadi tampaknya panggilan untuk addChildViewController:tidak banyak membantu. Efek samping panggilan adalah bagian penting. Mereka berasal dari parentViewControllerdan childViewControllershubungan. Berikut beberapa efek samping yang saya ketahui:

  • Meneruskan metode tampilan ke pengontrol tampilan anak
  • Meneruskan metode rotasi
  • (Mungkin) meneruskan peringatan memori
  • Menghindari hierarki VC yang tidak konsisten, terutama di transitionFromViewController:toViewController:…mana kedua VC harus memiliki induk yang sama
  • Mengizinkan pengontrol tampilan kontainer khusus untuk mengambil bagian dalam Pelestarian dan Pemulihan Status
  • Mengambil bagian dalam rantai responden
  • Hooking up navigationController, tabBarController, dll sifat
raja nevan
sumber
Ini sesi 102 bukan 101
SeanChense
1 untuk rantai responden. addChildViewController diperlukan jika Anda ingin menerima peristiwa sentuh pada subview yang dimiliki oleh anak UIViewController
charlieb
108

Saya pikir sebuah contoh bernilai seribu kata.

Saya sedang mengerjakan aplikasi perpustakaan dan ingin menunjukkan tampilan notepad bagus yang muncul saat pengguna ingin menambahkan catatan.

masukkan deskripsi gambar di sini

Setelah mencoba beberapa solusi, saya akhirnya menemukan solusi khusus saya sendiri untuk menampilkan notepad. Jadi ketika saya ingin menampilkan notepad, saya membuat instance baru NotepadViewControllerdan menambahkan tampilan root-nya sebagai subview ke tampilan utama. Sejauh ini baik.

Kemudian saya perhatikan bahwa gambar notepad sebagian tersembunyi di bawah keyboard dalam mode lansekap.

masukkan deskripsi gambar di sini

Jadi saya ingin mengubah gambar notepad dan menggesernya ke atas. Dan untuk melakukannya, saya menulis kode yang tepat dalam willAnimateRotationToInterfaceOrientation:duration:metode, tetapi ketika saya menjalankan aplikasi tidak ada yang terjadi! Dan setelah debugging saya perhatikan bahwa tidak ada UIViewControllermetode rotasi yang benar-benar dipanggil NotepadViewController. Hanya metode tersebut di pengontrol tampilan utama yang dipanggil.

Untuk mengatasi ini, saya perlu memanggil semua metode dari NotepadViewControllersecara manual ketika mereka dipanggil di pengontrol tampilan utama. Ini akan segera membuat segalanya menjadi rumit dan membuat ketergantungan ekstra antara komponen yang tidak terkait dalam aplikasi.

Itu terjadi di masa lalu, sebelum konsep pengontrol tampilan anak diperkenalkan. Tapi sekarang, Anda hanya perlu ke addChildViewControllerpengontrol tampilan utama dan semuanya akan berfungsi seperti yang diharapkan tanpa pekerjaan manual lagi.

Sunting: Ada dua kategori kejadian yang diteruskan ke pengontrol tampilan anak:

1- Metode Penampilan:

- viewWillAppear:
- viewDidAppear:
- viewWillDisappear:
- viewDidDisappear:

2- Metode Rotasi:

- willRotateToInterfaceOrientation:duration:
- willAnimateRotationToInterfaceOrientation:duration:
- didRotateFromInterfaceOrientation:

Anda juga dapat mengontrol kategori peristiwa apa yang ingin Anda teruskan secara otomatis dengan mengganti shouldAutomaticallyForwardRotationMethodsdan shouldAutomaticallyForwardAppearanceMethods.

Hejazi
sumber
Dari dokumentasi dan setelah melakukan quick test, saya rasa tidak ada peristiwa lain yang hanya diteruskan jika Anda addChildViewControllerke pengontrol induk.
Hejazi
berharap itu secara otomatis diteruskan viewWillLayoutSubviews
MobileMon
10

-[UIViewController addChildViewController:]hanya menambahkan pengontrol tampilan yang diteruskan dalam larik viewControllers yang ingin dijadikan referensi oleh viewController (induk). Anda sebenarnya harus menambahkan sendiri tampilan viewController tersebut di layar dengan menambahkannya sebagai subview dari tampilan lain (misalnya tampilan parentViewController). Ada juga objek praktis di Interface Builder untuk menggunakan childrenViewControllers di Storyboards.

Sebelumnya, untuk menyimpan referensi viewController lain yang tampilannya Anda gunakan, Anda harus menyimpan referensi manual di @properties. Memiliki properti bawaan seperti childViewControllersdan akibatnya parentViewControlleradalah cara mudah untuk mengelola interaksi tersebut dan membangun viewControllers yang tersusun seperti UISplitViewController yang Anda temukan di aplikasi iPad.

Selain itu, childrenViewControllers juga secara otomatis menerima semua kejadian sistem yang diterima induknya: -viewWillAppear, -viewWillDisappear, dll. Sebelumnya Anda harus memanggil metode ini secara manual pada "childrenViewControllers" Anda.

Itu dia.

Gianluca Tranchedone
sumber
Apa dasar Anda untuk berpikir bahwa hanya itu yang dilakukannya? Selain itu, dapatkah Anda memberikan daftar 'peristiwa sistem' yang diterima oleh anak? Pencarian Google untuk iOS "system events"tidak banyak muntah; Sepertinya bukan istilah yang digunakan Apple?
Mark Amery
Ini pada dasarnya adalah metode praktis yang memungkinkan Anda menambahkan tampilan View Controller B sebagai subview dari View Controller A, tetapi masih memiliki View Controller B yang mengelola tampilannya. Agar ini berfungsi dengan baik, Anda perlu memastikan bahwa View Controller B mendapatkan kejadian sistem (baca callback UIViewControllerDelegate). 'addChildViewController' mengaitkannya untuk Anda, untuk menghemat upaya meneruskan semuanya secara manual.
Sam Clewlow