เริ่มต้นตามเงื่อนไขที่ตำแหน่งต่างๆในสตอรีบอร์ดจาก AppDelegate


107

ฉันมีสตอรี่บอร์ดที่ตั้งค่าด้วยการเข้าสู่ระบบที่ใช้งานได้และตัวควบคุมมุมมองหลักส่วนหลังเป็นตัวควบคุมมุมมองที่ผู้ใช้จะถูกนำทางไปเมื่อการเข้าสู่ระบบสำเร็จ วัตถุประสงค์ของฉันคือแสดงตัวควบคุมมุมมองหลักทันทีหากการตรวจสอบความถูกต้อง (เก็บไว้ในพวงกุญแจ) สำเร็จและแสดงตัวควบคุมมุมมองการเข้าสู่ระบบหากการรับรองความถูกต้องล้มเหลว โดยทั่วไปฉันต้องการทำสิ่งนี้ใน AppDelegate ของฉัน:

// url request & response work fine, assume success is a BOOL here
// that indicates whether login was successful or not

if (success) {
          // 'push' main view controller
} else {
          // 'push' login view controller
}

ฉันรู้เกี่ยวกับวิธีการ performSegueWithIdentifier: แต่วิธีนี้เป็นวิธีการอินสแตนซ์ของ UIViewController ดังนั้นจึงไม่สามารถเรียกได้จากภายใน AppDelegate ฉันจะทำสิ่งนี้โดยใช้สตอรี่บอร์ดที่มีอยู่ได้อย่างไร?

แก้ไข:

ตัวควบคุมมุมมองเริ่มต้นของ Storyboard ตอนนี้เป็นตัวควบคุมการนำทางซึ่งไม่ได้เชื่อมต่อกับอะไรเลย ฉันใช้ setRootViewController: distinction เนื่องจาก MainIdentifier เป็น UITabBarController นี่คือลักษณะของเส้นของฉัน:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{        
    BOOL isLoggedIn = ...;    // got from server response

    NSString *segueId = isLoggedIn ? @"MainIdentifier" : @"LoginIdentifier";
    UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Storyboard" bundle:nil];
    UIViewController *initViewController = [storyboard instantiateViewControllerWithIdentifier:segueId];

    if (isLoggedIn) {
        [self.window setRootViewController:initViewController];
    } else {
        [(UINavigationController *)self.window.rootViewController pushViewController:initViewController animated:NO];
    }

    return YES;
}

ข้อเสนอแนะ / การปรับปรุงยินดีต้อนรับ!

คำตอบ:


25

ฉันถือว่าสตอรีบอร์ดของคุณถูกตั้งค่าเป็น "สตอรีบอร์ดหลัก" (คีย์UIMainStoryboardFileใน Info.plist ของคุณ) ในกรณีนั้น UIKit จะโหลดสตอรี่บอร์ดและตั้งค่าตัวควบคุมมุมมองเริ่มต้นเป็นตัวควบคุมมุมมองรูทของหน้าต่างก่อนที่จะส่งapplication:didFinishLaunchingWithOptions:ไปยัง AppDelegate ของคุณ

ฉันยังถือว่าตัวควบคุมมุมมองเริ่มต้นในสตอรีบอร์ดของคุณคือตัวควบคุมการนำทางซึ่งคุณต้องการผลักดันตัวควบคุมมุมมองหลักหรือมุมมองการเข้าสู่ระบบของคุณ

คุณสามารถขอหน้าต่างของคุณสำหรับตัวควบคุมมุมมองรูทและส่งperformSegueWithIdentifier:sender:ข้อความไปที่:

NSString *segueId = success ? @"pushMain" : @"pushLogin";
[self.window.rootViewController performSegueWithIdentifier:segueId sender:self];

1
ฉันใช้บรรทัดรหัสของคุณในแอปพลิเคชันของฉัน: didFinishLaunchingWithOptions: method การดีบักแสดงให้เห็นว่า rootViewController เป็นตัวควบคุมการนำทางเริ่มต้น แต่จะไม่ดำเนินการต่อ (แถบนำทางแสดงส่วนที่เหลือเป็นสีดำ) ฉันต้องบอกว่าตัวควบคุมการนำทางเริ่มต้นไม่มี rootViewController อีกต่อไปมีเพียง 2 segues (StartLoginSegue และ StartMainSegue)
mmvie

3
ใช่ไม่ทำงานสำหรับฉันเช่นกัน ทำไมคุณถึงทำเครื่องหมายตอบแล้วถ้ามันไม่ได้ผลสำหรับคุณ?
daidai

3
ฉันเชื่อว่านี่เป็นคำตอบที่ถูกต้อง คุณต้อง 1. มีคุณสมบัติหน้าต่างในผู้ร่วมประชุมแอปของคุณและ 2. เรียก[[self window] makeKeyAndVisible]ใช้ในแอปพลิเคชัน: didFinishLaunchingWithOptions: ก่อนที่คุณจะลองและดำเนินการทำเซ็กส์ตามเงื่อนไข UIApplicationMain () เป็นข้อความ makeKeyAndVisible แต่จะทำหลังจาก didFinish ... ตัวเลือก: เสร็จสิ้น ค้นหา "ความพยายามในการประสานงานระหว่าง View Controllers" ในเอกสารของ Apple เพื่อดูรายละเอียด
edelaney05

นี่เป็นความคิดที่ถูกต้อง แต่ไม่ค่อยได้ผล ดูคำตอบของฉันสำหรับวิธีแก้ปัญหาที่ใช้งานได้
Matthew Frederick

@MatthewFrederick วิธีแก้ปัญหาของคุณจะใช้งานได้หากตัวควบคุมเริ่มต้นเป็นตัวควบคุมการนำทาง แต่ไม่ใช่ตัวควบคุมมุมมองธรรมดา คำตอบที่แท้จริงคือการสร้างหน้าต่างและตัวควบคุมมุมมองรูทด้วยตัวคุณเองซึ่งเป็นสิ่งที่ Apple แนะนำใน View Controller Programming Guide ดูคำตอบของฉันด้านล่างสำหรับรายละเอียด
followben

170

ฉันแปลกใจที่มีการแนะนำวิธีแก้ปัญหาบางอย่างที่นี่

ไม่จำเป็นต้องมีตัวควบคุมการนำทางจำลองในสตอรีบอร์ดของคุณการซ่อนมุมมองและการถ่ายทำเซ็กส์บน viewDidAppear: หรือแฮ็กอื่น ๆ

หากคุณไม่ได้กำหนดค่าสตอรีบอร์ดในไฟล์ plist ของคุณคุณต้องสร้างทั้งหน้าต่างและตัวควบคุมมุมมองรูทด้วยตัวคุณเอง :

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{        
    BOOL isLoggedIn = ...;    // from your server response

    NSString *storyboardId = isLoggedIn ? @"MainIdentifier" : @"LoginIdentifier";
    UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Storyboard" bundle:nil];
    UIViewController *initViewController = [storyboard instantiateViewControllerWithIdentifier:storyboardId];

    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    self.window.rootViewController = initViewController;
    [self.window makeKeyAndVisible];

    return YES;
}

หากมีการกำหนดค่าสตอรีบอร์ดใน plist ของแอพตัวควบคุมหน้าต่างและมุมมองรูทจะถูกตั้งค่าตามเวลาแอปพลิเคชันแล้ว: didFinishLaunching: ถูกเรียกและ makeKeyAndVisible จะถูกเรียกบนหน้าต่างให้คุณ

ในกรณีนั้นมันง่ายกว่านั้น:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{        
    BOOL isLoggedIn = ...;    // from your server response

    NSString *storyboardId = isLoggedIn ? @"MainIdentifier" : @"LoginIdentifier";
    self.window.rootViewController = [self.window.rootViewController.storyboard instantiateViewControllerWithIdentifier:storyboardId];

    return YES;
}

@AdamRabung Spot on - ฉันแค่คัดลอกชื่อตัวแปรของ OP แต่ฉันได้อัปเดตคำตอบเพื่อความชัดเจน ไชโย
followben

สำหรับเคสสตอรีบอร์ด: หากคุณใช้ UINavigationViewcontroller เป็นตัวควบคุมมุมมองรูทคุณจะต้องกดตัวควบคุมมุมมองถัดไป
Shirish Kumar

นี่เป็นวิธีที่ง่ายกว่าสำหรับฉันแทนที่จะใช้ตัวควบคุมการนำทางตามลำดับชั้นที่ซับซ้อน Love this
Elliot Yap

สวัสดี @followben ในแอพของฉันฉันมี rootViewController ใน storyBoard มันเป็น tabBarController และ VC ที่เกี่ยวข้องทั้งหมดกับ tabBar นั้นได้รับการออกแบบใน VC ด้วยดังนั้นตอนนี้ฉันมีเคสที่ฉันต้องการแสดงคำแนะนำทีละแอพของฉันดังนั้น ตอนนี้เมื่อแอปของฉันเปิดตัวครั้งแรกฉันต้องการสร้างคำแนะนำแบบ VC เป็น VC รากแทน tabBarcontroller และเมื่อคำแนะนำของฉันเสร็จสิ้นฉันต้องการทำให้ tabBarController เป็น rootViewController ต้องทำอย่างไรฉันไม่เข้าใจ
Ranjit

1
จะเกิดอะไรขึ้นถ้าคำขอไปยังเซิร์ฟเวอร์เป็นแบบอะซิงโครนัส
Lior Burg

18

หากจุดเริ่มต้นของกระดานเรื่องราวของคุณไม่ใช่UINavigationController:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {


    //Your View Controller Identifiers defined in Interface Builder
    NSString *firstViewControllerIdentifier  = @"LoginViewController";
    NSString *secondViewControllerIdentifier = @"MainMenuViewController";

    //check if the key exists and its value
    BOOL appHasLaunchedOnce = [[NSUserDefaults standardUserDefaults] boolForKey:@"appHasLaunchedOnce"];

    //if the key doesn't exist or its value is NO
    if (!appHasLaunchedOnce) {
        //set its value to YES
        [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"appHasLaunchedOnce"];
        [[NSUserDefaults standardUserDefaults] synchronize];
    }

    //check which view controller identifier should be used
    NSString *viewControllerIdentifier = appHasLaunchedOnce ? secondViewControllerIdentifier : firstViewControllerIdentifier;

    //IF THE STORYBOARD EXISTS IN YOUR INFO.PLIST FILE AND YOU USE A SINGLE STORYBOARD
    UIStoryboard *storyboard = self.window.rootViewController.storyboard;

    //IF THE STORYBOARD DOESN'T EXIST IN YOUR INFO.PLIST FILE OR IF YOU USE MULTIPLE STORYBOARDS
    //UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"YOUR_STORYBOARD_FILE_NAME" bundle:nil];

    //instantiate the view controller
    UIViewController *presentedViewController = [storyboard instantiateViewControllerWithIdentifier:viewControllerIdentifier];

    //IF YOU DON'T USE A NAVIGATION CONTROLLER:
    [self.window setRootViewController:presentedViewController];

    return YES;
}

หากจุดเริ่มต้นของสตอรีบอร์ดของคุณถูกUINavigationControllerแทนที่:

//IF YOU DON'T USE A NAVIGATION CONTROLLER:
[self.window setRootViewController:presentedViewController];

กับ:

//IF YOU USE A NAVIGATION CONTROLLER AS THE ENTRY POINT IN YOUR STORYBOARD:
UINavigationController *navController = (UINavigationController *)self.window.rootViewController;
[navController pushViewController:presentedViewController animated:NO];

1
ทำงานได้ดี เพียงแค่ความคิดเห็นสิ่งนี้ไม่แสดง "firstViewControllerIdentifier" หลังจากที่ป้อนครั้งแรกเท่านั้นใช่หรือไม่ จึงไม่ควรย้อนกลับ? appHasLaunchedOnce ? secondViewControllerIdentifier : firstViewControllerIdentifier;
ammianus

@ammianus คุณถูกต้อง พวกเขาควรจะย้อนกลับและฉันแก้ไข
Razvan

9

ในapplication:didFinishLaunchingWithOptionsวิธีการของ AppDelegate ของคุณก่อนreturn YESบรรทัดให้เพิ่ม:

UINavigationController *navigationController = (UINavigationController*) self.window.rootViewController;
YourStartingViewController *yourStartingViewController = [[navigationController viewControllers] objectAtIndex:0];
[yourStartingViewController performSegueWithIdentifier:@"YourSegueIdentifier" sender:self];

แทนที่YourStartingViewControllerด้วยชื่อของคลาสคอนโทรลเลอร์มุมมองแรกที่แท้จริงของคุณ (อันที่คุณไม่ต้องการให้ปรากฏ) และYourSegueIdentifierด้วยชื่อจริงของการต่อระหว่างตัวควบคุมเริ่มต้นนั้นกับตัวควบคุมที่คุณต้องการเริ่มต้นจริง ๆ (อันที่อยู่หลังการต่อ ).

ห่อรหัสนั้นตามifเงื่อนไขหากคุณไม่ต้องการให้เกิดขึ้นเสมอไป


6

เนื่องจากคุณใช้ Storyboard อยู่แล้วคุณสามารถใช้สิ่งนี้เพื่อนำเสนอผู้ใช้ด้วย MyViewController ซึ่งเป็นคอนโทรลเลอร์ที่กำหนดเอง ( คำตอบของ Boiling down followbenเล็กน้อย)

ในAppDelegate.m :

-(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    MyCustomViewController *controller = [self.window.rootViewController.storyboard instantiateViewControllerWithIdentifier:@"MyCustomViewController"];

    // now configure the controller with a model, etc.

    self.window.rootViewController = controller;

    return YES;
}

สตริงที่ส่งผ่านไปยัง instantiateViewControllerWithIdentifier หมายถึง Storyboard ID ซึ่งสามารถตั้งค่าได้ในตัวสร้างอินเทอร์เฟซ:

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

เพียงแค่รวมสิ่งนี้ด้วยตรรกะตามต้องการ

หากคุณเริ่มต้นด้วย UINavigationController วิธีนี้จะไม่ให้การควบคุมทิศทางแก่คุณ

ในการ 'ข้ามไปข้างหน้า' จากจุดเริ่มต้นของตัวควบคุมการนำทางที่ตั้งค่าผ่านตัวสร้างอินเทอร์เฟซให้ใช้แนวทางนี้:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    UINavigationController *navigation = (UINavigationController *) self.window.rootViewController;

    [navigation.visibleViewController performSegueWithIdentifier:@"my-named-segue" sender:nil];

    return YES;
}

4

ทำไมไม่มีหน้าจอเข้าสู่ระบบที่ปรากฏขึ้นก่อนตรวจสอบว่าผู้ใช้เข้าสู่ระบบแล้วและผลักหน้าจอถัดไปทันทีหรือไม่? ทั้งหมดใน ViewDidLoad


2
สิ่งนี้ใช้งานได้จริง แต่เป้าหมายของฉันคือการแสดงภาพการเปิดตัวตราบใดที่แอปยังรอการตอบสนองของเซิร์ฟเวอร์ (ไม่ว่าการเข้าสู่ระบบจะสำเร็จหรือไม่ก็ตาม) เช่นเดียวกับแอพ Facebook ...
mmvie

2
คุณสามารถมีมุมมองแรกของคุณได้ตลอดเวลาเพียงแค่ UIImage ที่ใช้ภาพเดียวกับสแปลชของคุณและในการตรวจสอบพื้นหลังเพื่อดูว่าลงชื่อเข้าใช้และแสดงมุมมองถัดไปหรือไม่
Darren

3

การใช้งานที่รวดเร็วเหมือนกัน:

หากคุณใช้UINavigationControllerเป็นจุดเริ่มต้นในกระดานเรื่องราว

let storyboard = UIStoryboard(name: "Main", bundle: nil)

var rootViewController = self.window!.rootViewController as! UINavigationController;

    if(loginCondition == true){

         let profileController = storyboard.instantiateViewControllerWithIdentifier("ProfileController") as? ProfileController  
         rootViewController.pushViewController(profileController!, animated: true) 
    }
    else {

         let loginController =   storyboard.instantiateViewControllerWithIdentifier("LoginController") as? LoginController 
         rootViewController.pushViewController(loginController!, animated: true) 
    }

1

นี่คือโซลูชันที่ใช้งานได้กับ iOS7 เพื่อเพิ่มความเร็วในการโหลดครั้งแรกและไม่ทำการโหลดที่ไม่จำเป็นฉันมี UIViewcontroller ที่ว่างเปล่าที่เรียกว่า "DUMMY" ในไฟล์ Storyboard ของฉัน จากนั้นฉันสามารถใช้รหัสต่อไปนี้:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    UIStoryboard* storyboard = [UIStoryboard storyboardWithName:@"MainStoryboard" bundle:nil];

    NSString* controllerId = @"Publications";
    if (![NSUserDefaults.standardUserDefaults boolForKey:@"hasSeenIntroduction"])
    {
        controllerId = @"Introduction";
    }
    else if (![NSUserDefaults.standardUserDefaults boolForKey:@"hasDonePersonalizationOrLogin"])
    {
        controllerId = @"PersonalizeIntro";
    }

    if ([AppDelegate isLuc])
    {
        controllerId = @"LoginStart";
    }

    if ([AppDelegate isBart] || [AppDelegate isBartiPhone4])
    {
        controllerId = @"Publications";
    }

    UIViewController* controller = [storyboard instantiateViewControllerWithIdentifier:controllerId];
    self.window.rootViewController = controller;

    return YES;
}

0

ฉันขอแนะนำให้สร้าง MainViewController ใหม่ที่เป็น Root View Controller ของ Navigation Controller ในการทำเช่นนั้นเพียงแค่กดการควบคุมค้างไว้จากนั้นลากการเชื่อมต่อระหว่าง Navigation Controller และ MainViewController เลือก 'Relationship - Root View Controller' จากพรอมต์

ใน MainViewController:

- (void)viewDidLoad
{
    [super viewDidLoad];
    if (isLoggedIn) {
        [self performSegueWithIdentifier:@"HomeSegue" sender:nil];
    } else {
        [self performSegueWithIdentifier:@"LoginSegue" sender:nil];
    }
}

อย่าลืมสร้าง segues ระหว่าง MainViewController กับ Home และ Login view controllers หวังว่านี่จะช่วยได้ :)


0

หลังจากลองใช้วิธีต่างๆมากมายฉันสามารถแก้ปัญหานี้ได้ด้วยสิ่งนี้:

-(void)viewWillAppear:(BOOL)animated {

    // Check if user is already logged in
    NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
    if ([[prefs objectForKey:@"log"] intValue] == 1) {
        self.view.hidden = YES;
    }
}

-(void)viewDidAppear:(BOOL)animated{
    [super viewDidAppear:animated];

    // Check if user is already logged in
    NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
    if ([[prefs objectForKey:@"log"] intValue] == 1) {
        [self performSegueWithIdentifier:@"homeSeg3" sender:self];
    }
}

-(void)viewDidUnload {
    self.view.hidden = NO;
}

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