addChildViewController ทำอะไรได้จริง?


102

ฉันเพิ่งจุ่มเท้าของฉันเป็นครั้งแรกในการพัฒนา iOS และหนึ่งในสิ่งแรกที่ฉันต้องทำคือใช้ตัวควบคุมมุมมองคอนเทนเนอร์แบบกำหนดเอง - เรียกได้SideBarViewControllerว่าเปลี่ยนจากตัวควบคุมมุมมองเด็กที่เป็นไปได้หลายตัว แสดงเกือบจะเหมือนกับTab Bar Controllerมาตรฐาน (มันค่อนข้างเป็นTab Bar Controllerแต่มีเมนูด้านข้างที่ซ่อนได้แทนที่จะเป็นแถบแท็บ)

ตามคำแนะนำในเอกสารของ Apple ฉันโทรหาaddChildViewControllerทุกครั้งที่ฉันเพิ่ม ViewController ลูกลงในคอนเทนเนอร์ของฉัน รหัสของฉันสำหรับการสลับตัวควบคุมมุมมองเด็กปัจจุบันที่แสดงโดยSideBarViewControllerมีลักษณะดังนี้:

- (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];
}

จากนั้นฉันก็เริ่มพยายามหาว่าaddChildViewControllerที่นี่ทำอะไรและฉันก็รู้ว่าฉันไม่รู้ นอกจากการติดใหม่ViewControllerใน.childViewControllersอาร์เรย์แล้วดูเหมือนว่าจะไม่มีผลกระทบใด ๆ การดำเนินการและช่องทางจากมุมมองของผู้ควบคุมเด็กไปยังตัวควบคุมเด็กที่ฉันตั้งไว้บนกระดานเรื่องราวยังคงทำงานได้ดีแม้ว่าฉันจะไม่เคยโทรเลยaddChildViewControllerก็ตามและฉันนึกไม่ออกว่าจะมีผลกระทบอะไรอีก

อันที่จริงถ้าฉันเขียนรหัสของฉันใหม่เพื่อไม่เรียกaddChildViewControllerและมีลักษณะเช่นนี้แทน ...

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

... จากนั้นแอพของฉันก็ยังทำงานได้อย่างสมบูรณ์เท่าที่ฉันบอกได้!

เอกสารของ Apple ไม่ได้ให้ความกระจ่างเกี่ยวกับสิ่งที่addChildViewControllerทำหรือทำไมเราถึงควรเรียกมันว่า ขอบเขตทั้งหมดของคำอธิบายที่เกี่ยวข้องว่าวิธีนี้ทำอะไรหรือทำไมจึงควรใช้ในส่วนของการUIViewControllerอ้างอิงคลาสคือในปัจจุบัน:

เพิ่มตัวควบคุมมุมมองที่กำหนดเป็นลูก ... วิธีนี้มีไว้เพื่อเรียกใช้โดยการใช้งานตัวควบคุมมุมมองคอนเทนเนอร์ที่กำหนดเองเท่านั้น หากคุณลบล้างวิธีนี้คุณต้องเรียกใช้ super ในการใช้งานของคุณ

นอกจากนี้ยังมีย่อหน้านี้ก่อนหน้านี้ในหน้าเดียวกัน:

ตัวควบคุมมุมมองคอนเทนเนอร์ของคุณต้องเชื่อมโยงตัวควบคุมมุมมองเด็กกับตัวเองก่อนที่จะเพิ่มมุมมองรากของเด็กไปยังลำดับชั้นของมุมมอง สิ่งนี้ช่วยให้ iOS สามารถกำหนดเส้นทางเหตุการณ์ไปยังตัวควบคุมมุมมองเด็กได้อย่างถูกต้องและมุมมองที่ตัวควบคุมเหล่านั้นจัดการ ในทำนองเดียวกันหลังจากลบมุมมองรูทของเด็กออกจากลำดับชั้นมุมมองแล้วควรตัดการเชื่อมต่อตัวควบคุมมุมมองเด็กนั้นออกจากตัวเอง ในการสร้างหรือทำลายการเชื่อมโยงเหล่านี้คอนเทนเนอร์ของคุณจะเรียกใช้เมธอดเฉพาะที่กำหนดโดยคลาสพื้นฐาน เมธอดเหล่านี้ไม่ได้มีไว้สำหรับลูกค้าของคลาสคอนเทนเนอร์ของคุณ โดยจะใช้เฉพาะในการใช้งานคอนเทนเนอร์ของคุณเพื่อให้เป็นไปตามพฤติกรรมการกักกันที่คาดหวัง

นี่คือวิธีการสำคัญที่คุณอาจต้องโทร:

addChildViewController:
removeFromParentViewController
willMoveToParentViewController:
didMoveToParentViewController:

แต่ไม่ได้ให้เบาะแสใด ๆ ว่า 'เหตุการณ์' หรือ 'พฤติกรรมการกักกันที่คาดหวัง' ที่กำลังพูดถึงคืออะไรหรือทำไม (หรือแม้กระทั่งเมื่อ) การเรียกวิธีการเหล่านี้ว่า 'จำเป็น'

ตัวอย่างของตัวควบคุมมุมมองคอนเทนเนอร์แบบกำหนดเองในส่วน "Custom Container View Controllers" ของเอกสาร Apple ทั้งหมดเรียกวิธีนี้ดังนั้นฉันคิดว่ามันมีจุดประสงค์ที่สำคัญบางอย่างนอกเหนือไปจากการเปิด ViewController ลูกลงบนอาร์เรย์ แต่ฉันคิดไม่ออก จุดประสงค์นั้นคืออะไร วิธีนี้ใช้ทำอะไรและทำไมฉันถึงเรียกมันว่า?


3
หน้าวิดีโอWWDC ปี 2011ของ Apple มีเซสชันที่ยอดเยี่ยม ("การใช้งาน UIViewController Containment") ในหัวข้อนี้
Alladinian

คำตอบ:


94

ฉันก็สงสัยเกี่ยวกับคำถามนี้เหมือนกัน ฉันดูช่วงที่102 ของวิดีโอWWDC 2011และ Mr. View Controller, Bruce D. Niloกล่าวว่า:

viewWillAppear:, viewDidAppear:ฯลฯ addChildViewController:มีอะไรจะทำอย่างไรกับ สิ่งที่addChildViewController:กล่าวมาก็คือการพูดว่า "ตัวควบคุมมุมมองนี้เป็นลูกของตัวควบคุมมุมมองนั้น" และไม่มีส่วนเกี่ยวข้องกับลักษณะที่ปรากฏ เมื่อได้รับการเรียกจะเชื่อมโยงกับเมื่อมุมมองย้ายเข้าและออกจากลำดับชั้นของหน้าต่าง

จึงดูเหมือนว่าการเรียกร้องให้addChildViewController:ทำน้อยมาก ผลข้างเคียงของการโทรเป็นส่วนสำคัญ พวกเขามาจากparentViewControllerและchildViewControllersความสัมพันธ์ นี่คือผลข้างเคียงบางส่วนที่ฉันรู้:

  • วิธีการส่งต่อลักษณะที่ปรากฏไปยังตัวควบคุมมุมมองเด็ก
  • วิธีการหมุนไปข้างหน้า
  • (อาจ) ส่งต่อคำเตือนหน่วยความจำ
  • การหลีกเลี่ยงลำดับชั้น VC ที่ไม่สอดคล้องกันโดยเฉพาะอย่างยิ่งในtransitionFromViewController:toViewController:…กรณีที่VC ทั้งสองต้องมีพาเรนต์เดียวกัน
  • อนุญาตให้ตัวควบคุมมุมมองคอนเทนเนอร์แบบกำหนดเองมีส่วนร่วมในการสงวนรักษาและการฟื้นฟูสถานะ
  • มีส่วนร่วมในห่วงโซ่การตอบสนอง
  • Hooking ขึ้นnavigationController, tabBarControllerคุณสมบัติอื่น ๆ

เป็นเซสชั่น 102 ไม่ใช่ 101
SeanChense

+1 สำหรับห่วงโซ่การตอบกลับ addChildViewController เป็นสิ่งจำเป็นหากคุณต้องการรับเหตุการณ์สัมผัสในมุมมองย่อยที่เป็นของเด็ก UIViewController
charlieb

108

ฉันคิดว่าตัวอย่างมีค่าหนึ่งพันคำ

ฉันกำลังทำงานกับแอปไลบรารีและต้องการแสดงมุมมองแผ่นจดบันทึกที่สวยงามซึ่งจะปรากฏขึ้นเมื่อผู้ใช้ต้องการเพิ่มบันทึก

ป้อนคำอธิบายภาพที่นี่

หลังจากลองใช้วิธีแก้ปัญหาบางอย่างแล้วฉันก็ประดิษฐ์โซลูชันที่กำหนดเองของตัวเองเพื่อแสดงแผ่นจดบันทึก ดังนั้นเมื่อฉันต้องการแสดง notepad ฉันจึงสร้างอินสแตนซ์ใหม่NotepadViewControllerและเพิ่มมุมมองรูทเป็นมุมมองย่อยในมุมมองหลัก จนถึงตอนนี้ดีมาก

จากนั้นฉันสังเกตเห็นว่าภาพ notepad บางส่วนซ่อนอยู่ใต้แป้นพิมพ์ในโหมดแนวนอน

ป้อนคำอธิบายภาพที่นี่

เลยอยากจะเปลี่ยนรูป notepad แล้วเลื่อนขึ้น และในการทำเช่นนั้นฉันเขียนโค้ดที่ถูกต้องในwillAnimateRotationToInterfaceOrientation:duration:วิธีการ แต่เมื่อฉันรันแอพไม่มีอะไรเกิดขึ้น! และหลังจากการแก้จุดบกพร่องที่ผมสังเกตเห็นไม่มีที่UIViewController's NotepadViewControllerวิธีการหมุนเรียกว่าจริงใน ระบบจะเรียกเฉพาะเมธอดเหล่านั้นในตัวควบคุมมุมมองหลักเท่านั้น

ในการแก้ปัญหานี้ฉันต้องเรียกวิธีการทั้งหมดจากNotepadViewControllerด้วยตนเองเมื่อถูกเรียกในตัวควบคุมมุมมองหลัก เร็ว ๆ นี้จะทำให้สิ่งต่างๆซับซ้อนและสร้างการพึ่งพาเพิ่มเติมระหว่างส่วนประกอบที่ไม่เกี่ยวข้องในแอป

นั่นคือในอดีตก่อนที่จะมีการนำแนวคิดของตัวควบคุมมุมมองเด็กมาใช้ แต่ตอนนี้คุณต้อง addChildViewControllerใช้ตัวควบคุมมุมมองหลักเท่านั้นและทุกอย่างจะทำงานได้ตามที่คาดไว้โดยไม่ต้องทำงานด้วยตนเองอีกต่อไป

แก้ไข: มีเหตุการณ์สองประเภทที่ส่งต่อไปยังตัวควบคุมมุมมองเด็ก:

1- วิธีการปรากฏ:

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

2- วิธีการหมุน:

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

นอกจากนี้คุณยังสามารถควบคุมสิ่งที่ประเภทกิจกรรมที่คุณต้องการที่จะถูกส่งต่อไปโดยอัตโนมัติโดยการเอาชนะและshouldAutomaticallyForwardRotationMethodsshouldAutomaticallyForwardAppearanceMethods


จากเอกสารและหลังจากทำการทดสอบอย่างรวดเร็วฉันไม่คิดว่าจะมีเหตุการณ์อื่นใดที่จะถูกส่งต่อหากคุณaddChildViewControllerไปยังตัวควบคุมหลักเท่านั้น
Hejazi

หวังว่ามันจะส่งต่อโดยอัตโนมัติ viewWillLayoutSubviews
MobileMon

10

-[UIViewController addChildViewController:]เพิ่มเฉพาะคอนโทรลเลอร์ที่ส่งผ่านในมุมมองในอาร์เรย์ของ viewControllers ที่ viewController (พาเรนต์) ต้องการเก็บข้อมูลอ้างอิง คุณควรเพิ่มมุมมองของ viewController เหล่านั้นบนหน้าจอด้วยตัวคุณเองโดยเพิ่มเป็นมุมมองย่อยของมุมมองอื่น (เช่นมุมมองของ parentViewController) นอกจากนี้ยังมีวัตถุอำนวยความสะดวกใน Interface Builder เพื่อใช้ childrenViewControllers ใน Storyboards

ก่อนหน้านี้ในการเก็บข้อมูลอ้างอิงของ viewControllers อื่น ๆ ที่คุณใช้มุมมองคุณต้องเก็บข้อมูลอ้างอิงด้วยตนเองไว้ใน @properties การมีคุณสมบัติในตัวเช่นนี้childViewControllersจึงparentViewControllerเป็นวิธีที่สะดวกในการจัดการการโต้ตอบดังกล่าวและสร้าง viewControllers แบบประกอบเช่น UISplitViewController ที่คุณพบในแอพ iPad

นอกจากนี้ childrenViewControllers ยังรับเหตุการณ์ระบบทั้งหมดที่พาเรนต์ได้รับโดยอัตโนมัติ: -viewWillAppear, -viewWillDisappear เป็นต้นก่อนหน้านี้คุณควรเรียกเมธอดนี้ด้วยตนเองใน "childrenViewControllers" ของคุณ

แค่นั้นแหละ.


อะไรคือพื้นฐานของคุณในการคิดว่านั่นคือทั้งหมด? นอกจากนี้คุณสามารถระบุรายการ "เหตุการณ์ของระบบ" ที่เด็กได้รับหรือไม่ การค้นหาโดย Google iOS "system events"ไม่ได้ทำให้เกิดความเสียหายมากนัก ดูเหมือนจะไม่ใช่คำที่ Apple ใช้?
Mark Amery

โดยพื้นฐานแล้วเป็นวิธีอำนวยความสะดวกที่ช่วยให้คุณเพิ่มมุมมองของ View Controller B เป็นมุมมองย่อยของ View Controller A แต่ยังมี View Controller B จัดการมุมมอง เพื่อให้สิ่งนี้ทำงานได้อย่างถูกต้องคุณต้องตรวจสอบให้แน่ใจว่า View Controller B ได้รับเหตุการณ์ของระบบ (อ่านการเรียกกลับ UIViewControllerDelegate) 'addChildViewController' จะเชื่อมต่อสิ่งนี้ให้คุณเพื่อช่วยให้คุณประหยัดเวลาในการส่งผ่านทุกอย่างด้วยตนเอง
Sam Clewlow
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.