Mulai bersyarat di berbagai tempat di storyboard dari AppDelegate

107

Saya memiliki storyboard yang disiapkan dengan login yang berfungsi dan pengontrol tampilan utama, yang terakhir adalah pengontrol tampilan tempat pengguna dinavigasi saat login berhasil. Tujuan saya adalah untuk segera menampilkan pengontrol tampilan utama jika otentikasi (disimpan dalam rantai kunci) berhasil, dan menampilkan pengontrol tampilan masuk jika otentikasi gagal. Pada dasarnya, saya ingin melakukan ini di AppDelegate saya:

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

Saya tahu tentang metode performSegueWithIdentifier: tetapi metode ini adalah metode contoh UIViewController, jadi tidak dapat dipanggil dari dalam AppDelegate. Bagaimana cara melakukan ini menggunakan papan cerita saya yang sudah ada ??

EDIT:

Pengontrol tampilan awal Storyboard sekarang adalah pengontrol navigasi yang tidak terhubung ke apa pun. Saya menggunakan perbedaan setRootViewController: karena MainIdentifier adalah UITabBarController. Maka seperti inilah garis saya terlihat:

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

Saran / perbaikan dipersilahkan!

mmvie.dll
sumber

Jawaban:

25

Saya berasumsi bahwa storyboard Anda ditetapkan sebagai "storyboard utama" (kunci UIMainStoryboardFileInfo.plist Anda). Dalam hal ini, UIKit akan memuat storyboard dan menyetel pengontrol tampilan awalnya sebagai pengontrol tampilan root jendela Anda sebelum dikirim application:didFinishLaunchingWithOptions:ke AppDelegate Anda.

Saya juga berasumsi bahwa pengontrol tampilan awal di storyboard Anda adalah pengontrol navigasi, tempat Anda ingin mendorong pengontrol tampilan utama atau masuk.

Anda dapat meminta jendela Anda untuk pengontrol tampilan root, dan mengirimkan performSegueWithIdentifier:sender:pesan ke sana:

NSString *segueId = success ? @"pushMain" : @"pushLogin";
[self.window.rootViewController performSegueWithIdentifier:segueId sender:self];
merampok mayoff
sumber
1
Saya menerapkan baris kode Anda di application: didFinishLaunchingWithOptions: method. Debugging menunjukkan bahwa rootViewController memang pengontrol navigasi awal, namun perpindahannya tidak dilakukan (bilah navigasi ditampilkan, sisanya berwarna hitam). Saya harus mengatakan pengontrol navigasi awal tidak memiliki rootViewController lagi, hanya 2 segues (StartLoginSegue dan StartMainSegue).
mmvie
3
Ya, tidak berhasil untuk saya juga. Mengapa Anda menandainya Dijawab jika tidak berhasil untuk Anda?
daidai
3
Saya yakin ini adalah jawaban yang benar. Anda harus 1. memiliki properti jendela pada delegasi aplikasi Anda dan 2. memanggil [[self window] makeKeyAndVisible]dalam application: didFinishLaunchingWithOptions: sebelum Anda mencoba dan melakukan segue bersyarat. UIApplicationMain () seharusnya pesan makeKeyAndVisible tetapi melakukannya hanya setelah didFinish ... Options: finishes. Cari "Upaya Koordinasi Antara Pengontrol Tampilan" di dokumen Apple untuk detailnya.
edelaney05
Ini adalah ide yang tepat, tetapi tidak berhasil. Lihat jawaban saya untuk solusi yang berhasil.
Matthew Frederick
@MatthewFrederick Solusi Anda akan berfungsi jika pengontrol awal adalah pengontrol navigasi, tetapi tidak jika pengontrol tampilan biasa. Jawaban sebenarnya adalah hanya membuat sendiri jendela dan pengontrol tampilan root - memang inilah yang direkomendasikan Apple dalam Panduan Pemrograman Pengontrol Tampilan. Lihat jawaban saya di bawah untuk detailnya.
followben
170

Saya terkejut dengan beberapa solusi yang disarankan di sini.

Benar-benar tidak perlu pengontrol navigasi tiruan di papan cerita Anda, menyembunyikan tampilan & menembakkan segmen di viewDidAppear: atau peretasan lainnya.

Jika storyboard belum dikonfigurasi di file plist, Anda harus membuat sendiri jendela dan pengontrol tampilan root :

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

Jika storyboard yang dikonfigurasi dalam plist aplikasi, jendela dan akar controller tampilan sudah akan setup oleh aplikasi waktu: didFinishLaunching: disebut, dan makeKeyAndVisible akan dipanggil pada jendela untuk Anda.

Dalam hal ini, lebih sederhana lagi:

- (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;
}
followben
sumber
@AdamRabung Spot on - Saya baru saja menyalin nama variabel OP, tetapi saya telah memperbarui jawaban saya untuk kejelasan. Bersulang.
followben
untuk kasus storyboard: Jika Anda menggunakan UINavigationViewcontroller sebagai pengontrol tampilan root, Anda harus mendorong pengontrol tampilan berikutnya.
Shirish Kumar
Ini adalah cara yang lebih intuitif bagi saya daripada melalui pengontrol navigasi hierarki yang kompleks. Suka ini
Elliot Yap
Halo @followben, di aplikasi saya, saya memiliki rootViewController di storyBoard, ini adalah tabBarController, dan semua VC terkait dengan tabBar juga dirancang di VC, jadi sekarang saya punya kasus, di mana saya ingin menunjukkan panduan aplikasi saya, Jadi sekarang ketika aplikasi saya pertama kali diluncurkan, saya ingin menjadikan VC panduan sebagai VC root daripada tabBarcontroller dan ketika panduan saya selesai, saya ingin menjadikan tabBarController sebagai rootViewController. Bagaimana melakukannya Saya tidak mengerti
Ranjit
1
Bagaimana jika permintaan ke server asynchronous?
Lior Burg
18

JIKA titik masuk papan cerita Anda bukanlah 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;
}

JIKA titik masuk storyboard Anda ADALAH UINavigationControllerpengganti:

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

dengan:

//IF YOU USE A NAVIGATION CONTROLLER AS THE ENTRY POINT IN YOUR STORYBOARD:
UINavigationController *navController = (UINavigationController *)self.window.rootViewController;
[navController pushViewController:presentedViewController animated:NO];
Razvan
sumber
1
Bekerja dengan baik. Hanya sebuah komentar, bukankah ini menampilkan "firstViewControllerIdentifier" hanya setelah mereka masuk pada awalnya? Jadi, bukankah seharusnya itu dibalik? appHasLaunchedOnce ? secondViewControllerIdentifier : firstViewControllerIdentifier;
ammianus
@ammianus Anda benar. Mereka harus dibalik dan saya edit.
Razvan
9

Dalam application:didFinishLaunchingWithOptionsmetode AppDelegate Anda , sebelum return YESbaris, tambahkan:

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

Ganti YourStartingViewControllerdengan nama kelas pengontrol tampilan pertama Anda yang sebenarnya (yang Anda tidak ingin selalu muncul) dan YourSegueIdentifierdengan nama sebenarnya dari segue antara pengontrol awal itu dan pengontrol yang ingin Anda mulai (yang setelah segue ).

Bungkus kode itu dalam ifkondisi jika Anda tidak selalu menginginkannya terjadi.

Matthew Frederick
sumber
6

Mengingat bahwa Anda sudah menggunakan Storyboard, Anda dapat menggunakan ini untuk menyajikan pengguna dengan MyViewController, pengontrol khusus ( Mempelajari jawaban followben sedikit).

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

String yang diteruskan ke instantiateViewControllerWithIdentifier mengacu pada ID Storyboard, yang bisa disetel di pembuat antarmuka:

masukkan deskripsi gambar di sini

Bungkus saja ini dalam logika sesuai kebutuhan.

Jika Anda memulai dengan UINavigationController, pendekatan ini tidak akan memberi Anda kontrol navigasi.

Untuk 'melompat maju' dari titik awal pengontrol navigasi yang disiapkan melalui pembuat antarmuka, gunakan pendekatan ini:

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

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

    return YES;
}
Apodaca Kaya
sumber
4

Mengapa tidak ada layar login yang muncul pertama kali, periksa apakah pengguna sudah login dan langsung tekan layar berikutnya? Semua di ViewDidLoad.

Darren
sumber
2
Ini memang berfungsi, tetapi tujuan saya adalah untuk menunjukkan gambar peluncuran selama aplikasi masih menunggu respons server (apakah login berhasil atau tidak). Sama seperti aplikasi Facebook ...
mmvie
2
Anda selalu bisa mendapatkan tampilan pertama Anda hanya UIImage yang menggunakan gambar yang sama dengan splash Anda dan di pemeriksaan latar belakang untuk melihat apakah masuk dan menampilkan tampilan berikutnya.
Darren
3

Implementasi cepat yang sama:

Jika Anda gunakan UINavigationControllersebagai titik masuk di storyboard

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) 
    }
Dashrath
sumber
1

Ini adalah solusi yang berhasil di iOS7. Untuk mempercepat pemuatan awal dan tidak melakukan pemuatan yang tidak perlu, saya memiliki UIViewcontroller yang benar-benar kosong yang disebut "DUMMY" di file Storyboard saya. Kemudian saya dapat menggunakan kode berikut:

- (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;
}
Luc Bloom
sumber
0

Saya menyarankan untuk membuat MainViewController baru yaitu Root View Controller dari Navigation Controller. Untuk melakukan itu, cukup tahan kontrol, lalu seret koneksi antara Pengontrol Navigasi dan MainViewController, pilih 'Hubungan - Root View Controller' dari prompt.

Di MainViewController:

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

Ingatlah untuk membuat segues antara MainViewController dengan pengontrol tampilan Beranda dan Login. Semoga ini membantu. :)

thanhbinh84.dll
sumber
0

Setelah mencoba banyak metode berbeda, saya dapat menyelesaikan masalah ini dengan ini:

-(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;
}
AddisDev
sumber
Jika Anda tidak terlalu jauh dengan Taylor, Anda mungkin ingin mengubah menjadi sesuatu yang lebih sederhana. Lihat jawaban saya untuk detailnya :)
followben