Storyboard - อ้างถึง ViewController ใน AppDelegate


122

ลองพิจารณาสถานการณ์ต่อไปนี้: ฉันมีแอปที่ใช้สตอรี่บอร์ด ฉันเพิ่มออบเจ็กต์ ViewController ลงในสตอรี่บอร์ดเพิ่มไฟล์คลาสสำหรับ ViewController นี้ลงในโปรเจ็กต์และระบุชื่อของคลาสใหม่ในตัวตรวจสอบข้อมูลประจำตัว IB ตอนนี้ฉันจะอ้างถึง ViewController นี้โดยทางโปรแกรมจาก AppDelegate ได้อย่างไร ฉันได้สร้างตัวแปรกับคลาสที่เกี่ยวข้องและเปลี่ยนเป็นคุณสมบัติ IBOutlet แต่ฉันไม่เห็นวิธีใดที่จะสามารถอ้างถึง ViewController ใหม่ในโค้ดได้ - ความพยายามใด ๆ ที่จะ ctrl ลากการเชื่อมต่อไม่ได้ผล .

เช่นภายใน AppDelegate ฉันสามารถไปที่ ViewController พื้นฐานได้เช่นนี้

(MyViewController*) self.window.rootViewController

แต่ ViewController อื่น ๆ ที่อยู่ในสตอรี่บอร์ดล่ะ?


ตรวจสอบคำตอบนี้ มันรวดเร็ว แต่ภาษาก็คล้ายกัน
Borzh

คำตอบ:


165

ดูเอกสารสำหรับ-[UIStoryboard instantiateViewControllerWithIdentifier:]. สิ่งนี้ช่วยให้คุณสร้างอินสแตนซ์ตัวควบคุมมุมมองจากสตอรีบอร์ดของคุณโดยใช้ตัวระบุที่คุณตั้งไว้ใน IB Attributes Inspector:

ใส่คำอธิบายภาพที่นี่

แก้ไขเพื่อเพิ่มโค้ดตัวอย่าง:

UIStoryboard *mainStoryboard = [UIStoryboard storyboardWithName:@"MainStoryboard"
                                                         bundle: nil];

MyViewController *controller = (MyViewController*)[mainStoryboard 
                    instantiateViewControllerWithIdentifier: @"<Controller ID>"];

สวัสดีโรบินขอบคุณสำหรับสิ่งนั้น! ฉันดูเอกสารนี้ แต่มีคำว่าอินสแตนซ์และการเริ่มต้นผสมกัน ... สิ่งนี้ทำให้เราไปที่นั่น (หลังจากทำตามคำแนะนำของคุณ :) (แช่งไม่มีการจัดรูปแบบโค้ดในการตอบกลับ ... ) UIStoryboard * mainStoryboard = [UIStoryboard storyboardWithName: @ ชุด "MainStoryboard": nil]; MyViewController * thisController = (MyViewController *) [mainStoryboard instantiateViewControllerWithIdentifier: @ "myvc"];
Matthias D

ฉันได้เพิ่มโค้ดตัวอย่างของคุณในคำตอบพร้อมการจัดรูปแบบสำหรับทุกคนที่กำลังดูสิ่งนี้
Robin Summerhill

24
หากคุณกำลังสร้างแอปสากลอย่าลืมใช้ MainStoryboard_iPhone / MainStoryboard_iPad ไม่เช่นนั้นคุณจะเกิดปัญหา
roocell

16
จากภายในผู้รับมอบสิทธิ์คุณสามารถเข้าถึงอินสแตนซ์สตอรีบอร์ดที่โหลดโดย info.plist ของคุณได้ดังนี้: [[[self window] rootViewController] storyboard] ตามเอกสารนี้จะส่งคืน "สตอรีบอร์ดที่มาจากตัวควบคุมมุมมอง" (หรือไม่มีถ้าไม่ได้มาจากสตอรี่บอร์ด) จาก UIStoryboard * นั้นคุณสามารถใช้การเรียกใช้อินสแตนซ์ที่ @RobinSummerhill กล่าวถึง โปรดทราบว่าสตอรี่บอร์ดจะสร้างอินสแตนซ์ใหม่ของ viewControllers ( ฉาก ) ของคุณตามความจำเป็นและไม่ใช้ซ้ำที่เคยดู
Tad Bumcrot

6
ฉันเชื่อว่า OP ต้องการทราบวิธีการได้รับ vc ที่กำลังทำงานอยู่ในปัจจุบัน ไม่ใช่วิธีการโหลด ตามที่เขาอธิบายคุณเคยสามารถพูดself.window.rootViewControllerนี้ได้และตอนนี้คุณไม่สามารถทำได้อีกแล้ว
Fattie

41

ถ้าคุณใช้XCode5 คุณควรทำด้วยวิธีอื่น

  • เลือกของคุณUIViewControllerในUIStoryboard
  • ไปที่Identity Inspectorบานหน้าต่างด้านขวาบน
  • ตรวจสอบUse Storyboard IDช่องทำเครื่องหมาย
  • เขียนรหัสเฉพาะลงในStoryboard IDฟิลด์

จากนั้นเขียนโค้ดของคุณ

// Override point for customization after application launch.

if (<your implementation>) {
    UIStoryboard *mainStoryboard = [UIStoryboard storyboardWithName:@"Main" 
                                                             bundle: nil];
    YourViewController *yourController = (YourViewController *)[mainStoryboard 
      instantiateViewControllerWithIdentifier:@"YourViewControllerID"];
    self.window.rootViewController = yourController;
}

return YES;

4
เท่าที่ฉันรู้และสามารถทดลองใช้นี่คือคำตอบที่อัปเดตแล้ว
gnclmorais

ชื่อของสตอรีบอร์ดคือชื่อไฟล์ที่ไม่มีนามสกุลดังนั้นจึงอาจเป็น "Main_iPhone" หรือ "Main_iPad" ได้หากคุณตั้งค่าโปรเจ็กต์ด้วยวิธีนี้
Brian White

แล้วเราจะเปลี่ยนกลับไปใช้ rootViewController เดิมได้อย่างไรหลังจากเข้าสู่ระบบ อย่างน้อยความรู้ของฉันเกี่ยวกับ iOS 7 และ Xcode 5.0.2 - การเปลี่ยนตัวควบคุมมุมมองรูทกลับจะทันทีและไม่มีภาพเคลื่อนไหวจะเปลี่ยนลำดับชั้นของมุมมอง ทีม UX ได้สังหารผู้เขียนโค้ดในราคาที่น้อยลง
lol

ฉันจะใช้ Swift ได้อย่างไร
Leo Dabus

8

โดยทั่วไประบบควรจัดการอินสแตนซ์ตัวควบคุมมุมมองด้วยสตอรีบอร์ด สิ่งที่คุณต้องการคือการสำรวจลำดับชั้นของ viewController โดยใช้การอ้างอิงไปยังสิ่งself.window.rootViewControllerที่ตรงข้ามกับการเริ่มต้นตัวควบคุมมุมมองซึ่งควรเริ่มต้นอย่างถูกต้องหากคุณตั้งค่าสตอรี่บอร์ดอย่างถูกต้อง

สมมติว่าคุณrootViewControllerเป็น UINavigationController จากนั้นคุณต้องการส่งบางอย่างไปยังตัวควบคุมมุมมองด้านบนคุณจะทำเช่นนี้ใน AppDelegate ของคุณdidFinishLaunchingWithOptions:

UINavigationController *nav = (UINavigationController *) self.window.rootViewController;
MyViewController *myVC = (MyViewController *)nav.topViewController;
myVC.data = self.data;

ใน Swift ถ้าจะคล้ายกันมาก:

let nav = self.window.rootViewController as! UINavigationController;
let myVC = nav.topViewController as! MyViewController
myVc.data = self.data

คุณไม่ควรเริ่มต้นตัวควบคุมมุมมองโดยใช้รหัสสตอรี่บอร์ดจากตัวแทนของแอปเว้นแต่คุณต้องการข้ามวิธีโหลดสตอรีบอร์ดปกติและโหลดสตอรีบอร์ดทั้งหมดด้วยตัวเอง หากคุณต้องเริ่มต้นฉากจาก AppDelegate คุณมักจะทำอะไรผิดพลาด ฉันหมายถึงลองนึกดูว่าคุณต้องการส่งข้อมูลไปยังตัวควบคุมมุมมองทางลงสแต็กด้วยเหตุผลบางประการ AppDelegate ไม่ควรเข้าไปในสแต็กคอนโทรลเลอร์มุมมองเพื่อตั้งค่าข้อมูล นั่นไม่ใช่ธุรกิจของมัน มันเป็นธุรกิจคือ rootViewController ปล่อยให้ rootViewController จัดการลูก ๆ ของมันเอง! ดังนั้นถ้าฉันข้ามขั้นตอนการโหลดสตอรี่บอร์ดตามปกติโดยระบบโดยลบการอ้างอิงไปยังไฟล์ info.plist ฉันจะสร้างอินสแตนซ์ rootViewController โดยใช้instantiateViewControllerWithIdentifier:และอาจเป็นรูทถ้าเป็นคอนเทนเนอร์เช่น UINavigationController สิ่งที่คุณต้องการหลีกเลี่ยงคือการสร้างอินสแตนซ์ตัวควบคุมมุมมองที่ได้รับการสร้างอินสแตนซ์โดยสตอรี่บอร์ดแล้ว นี่เป็นปัญหาที่ฉันเห็นมาก ในระยะสั้นฉันไม่เห็นด้วยกับคำตอบที่ยอมรับ มันไม่ถูกต้องเว้นแต่ผู้โพสต์จะหมายถึงการลบการโหลดสตอรีบอร์ดออกจาก info.plist เนื่องจากคุณจะโหลดสตอรีบอร์ด 2 รายการเป็นอย่างอื่นซึ่งไม่สมเหตุสมผล อาจไม่ใช่หน่วยความจำรั่วเนื่องจากระบบเริ่มต้นฉากรูทและกำหนดให้กับหน้าต่าง แต่จากนั้นคุณก็เข้ามาและสร้างอินสแตนซ์อีกครั้งและกำหนดอีกครั้ง แอปของคุณเริ่มต้นได้ไม่ดีนัก!


โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.