Praktik terbaik untuk layar masuk Storyboard, menangani pembersihan data saat keluar


290

Saya sedang membangun aplikasi iOS menggunakan Storyboard. Pengontrol tampilan root adalah Pengontrol Tab Bar. Saya membuat proses masuk / keluar, dan sebagian besar berfungsi dengan baik, tapi saya punya beberapa masalah. Saya perlu tahu cara TERBAIK untuk mengatur semua ini.

Saya ingin mencapai yang berikut:

  1. Tampilkan layar masuk saat aplikasi pertama kali diluncurkan. Saat mereka masuk, buka tab pertama dari Pengontrol Tab Bar.
  2. Setiap kali mereka meluncurkan aplikasi setelah itu, periksa apakah mereka masuk, dan lewati langsung ke tab pertama dari Pengontrol Tab Bar root.
  3. Ketika mereka secara manual mengklik tombol logout, perlihatkan layar login, dan hapus semua data dari pengontrol tampilan.

Apa yang telah saya lakukan sejauh ini adalah mengatur pengontrol tampilan root ke Pengontrol Tab Bar, dan membuat segue khusus untuk pengontrol tampilan Login saya. Di dalam kelas Pengontrol Tab Bar saya, saya memeriksa apakah mereka masuk di dalam viewDidAppearmetode, dan melakukan segue:[self performSegueWithIdentifier:@"pushLogin" sender:self];

Saya juga mengatur pemberitahuan kapan tindakan logout perlu dilakukan: [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(logoutAccount) name:@"logoutAccount" object:nil];

Setelah keluar, saya menghapus kredensial dari Gantungan Kunci, menjalankan [self setSelectedIndex:0]dan melakukan segue untuk menampilkan kembali pengontrol tampilan login.

Ini semua berfungsi dengan baik, tapi saya bertanya-tanya: haruskah logika ini ada di AppDelegate? Saya juga punya dua masalah:

  • Pertama kali mereka meluncurkan aplikasi , Kontroler Tab Bar menunjukkan sebentar sebelum segue dilakukan. Saya sudah mencoba memindahkan kode ke viewWillAppeartetapi segue tidak akan bekerja lebih awal.
  • Saat mereka keluar, semua data masih di dalam semua pengontrol tampilan. Jika mereka masuk ke akun baru, data akun lama masih ditampilkan sampai mereka menyegarkan. Saya perlu cara untuk menghapus ini dengan mudah di logout.

Saya terbuka untuk mengerjakan ulang ini. Saya telah mempertimbangkan menjadikan layar masuk pengontrol tampilan root, atau membuat pengontrol navigasi di AppDelegate untuk menangani semuanya ... Saya hanya tidak yakin apa metode terbaik pada saat ini.


Apakah Anda menampilkan pengontrol tampilan masuk sebagai modal?
vokilam

@TrevorGehman - dapat menambahkan pic storyboard Anda
rohan k shah

Saya mengirimkan jawaban dengan perincian tentang apa yang akhirnya saya lakukan. Ini mirip dengan beberapa jawaban lain yang disediakan, terutama @bhavya kothari.
Trevor Gehman

Untuk menyajikan layar login, AuthNavigation mungkin bermanfaat. Ini mengatur presentasi layar login jika diperlukan dan juga mendukung login otomatis.
Codey

Salah satu masalah yang sangat mendasar yang hampir selalu dipecahkan tetapi pada saat yang sama rasanya bisa dilakukan dengan lebih baik
amar

Jawaban:


311

Papan cerita Anda akan terlihat seperti ini

Di appDelegate.m Anda di dalam didFinishLaunchingWithOptions Anda

//authenticatedUser: check from NSUserDefaults User credential if its present then set your navigation flow accordingly

if (authenticatedUser) 
{
    self.window.rootViewController = [[UIStoryboard storyboardWithName:@"Main" bundle:[NSBundle mainBundle]] instantiateInitialViewController];        
}
else
{
    UIViewController* rootController = [[UIStoryboard storyboardWithName:@"Main" bundle:[NSBundle mainBundle]] instantiateViewControllerWithIdentifier:@"LoginViewController"];
    UINavigationController* navigation = [[UINavigationController alloc] initWithRootViewController:rootController];

    self.window.rootViewController = navigation;
}

Dalam file SignUpViewController.m

- (IBAction)actionSignup:(id)sender
{
    AppDelegate *appDelegateTemp = [[UIApplication sharedApplication]delegate];

    appDelegateTemp.window.rootViewController = [[UIStoryboard storyboardWithName:@"Main" bundle:[NSBundle mainBundle]] instantiateInitialViewController];
}

Dalam file MyTabThreeViewController.m

- (IBAction)actionLogout:(id)sender {

    // Delete User credential from NSUserDefaults and other data related to user

    AppDelegate *appDelegateTemp = [[UIApplication sharedApplication]delegate];

    UIViewController* rootController = [[UIStoryboard storyboardWithName:@"Main" bundle:[NSBundle mainBundle]] instantiateViewControllerWithIdentifier:@"LoginViewController"];

    UINavigationController* navigation = [[UINavigationController alloc] initWithRootViewController:rootController];
    appDelegateTemp.window.rootViewController = navigation;

}

Versi Swift 4

didFinishLaunchingWithOptions dalam delegasi aplikasi dengan asumsi pengontrol tampilan awal Anda adalah yang terdaftar di TabbarController.

if Auth.auth().currentUser == nil {
        let rootController = UIStoryboard(name: "Main", bundle: Bundle.main).instantiateViewController(withIdentifier: "WelcomeNavigation")
        self.window?.rootViewController = rootController
    }

    return true

Di Daftar pengendali tampilan:

@IBAction func actionSignup(_ sender: Any) {
let appDelegateTemp = UIApplication.shared.delegate as? AppDelegate
appDelegateTemp?.window?.rootViewController = UIStoryboard(name: "Main", bundle: Bundle.main).instantiateInitialViewController()
}

MyTabThreeViewController

 //Remove user credentials
guard let appDel = UIApplication.shared.delegate as? AppDelegate else { return }
        let rootController = UIStoryboard(name: "Main", bundle: Bundle.main).instantiateViewController(withIdentifier: "WelcomeNavigation")
        appDel.window?.rootViewController = rootController

Anda lupa menghapus otentikasi bool dari userDefaults setelah logout
CodeLover

28
-1 untuk menggunakan AppDelegatebagian dalam UIViewControllerdan pengaturan di window.rootViewControllersana. Saya tidak menganggap ini sebagai "praktik terbaik".
derpoliuk

2
Tidak ingin memberi -1tanpa memposting jawaban: stackoverflow.com/a/30664935/1226304
derpoliuk

1
Saya mencoba melakukan ini dengan cepat di iOS8 tapi saya mendapatkan kesalahan berikut ketika aplikasi menyala dan layar login menunjukkan: "Panggilan tidak seimbang untuk memulai / mengakhiri transisi penampilan". Saya perhatikan bahwa ketika aplikasi memuat layar login muncul, tetapi juga tab pertama pada pengontrol tab bar juga dimuat. Mengonfirmasi ini melalui println () di viewdidload. Saran?
Alex Lacayo

1
bingo! -2. -1 untuk AppDelegatedi dalam UIViewController-1 untuk Menyimpan kunci masuk NSUserDefaults. Sangat-sangat tidak aman untuk data seperti itu!
skywinder

97

Inilah yang akhirnya saya lakukan untuk menyelesaikan segalanya. Satu-satunya hal yang perlu Anda pertimbangkan selain ini adalah (a) proses login dan (b) di mana Anda menyimpan data aplikasi Anda (dalam hal ini, saya menggunakan singleton).

Storyboard menampilkan pengontrol tampilan masuk dan pengontrol tab utama

Seperti yang Anda lihat, pengontrol tampilan root adalah milik saya Pengontrol Tab Utama . Saya melakukan ini karena setelah pengguna masuk, saya ingin aplikasi langsung diluncurkan ke tab pertama. (Ini menghindari "flicker" di mana tampilan login menunjukkan sementara.)

AppDelegate.m

Dalam file ini, saya memeriksa apakah pengguna sudah masuk. Jika tidak, saya mendorong pengontrol tampilan login. Saya juga menangani proses logout, tempat saya membersihkan data dan memperlihatkan tampilan login.

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

    // Show login view if not logged in already
    if(![AppData isLoggedIn]) {
        [self showLoginScreen:NO];
    }

    return YES;
}

-(void) showLoginScreen:(BOOL)animated
{

    // Get login screen from storyboard and present it
    UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"MainStoryboard" bundle:nil];
    LoginViewController *viewController = (LoginViewController *)[storyboard instantiateViewControllerWithIdentifier:@"loginScreen"];
    [self.window makeKeyAndVisible];
    [self.window.rootViewController presentViewController:viewController
                                             animated:animated
                                           completion:nil];
}

-(void) logout
{
    // Remove data from singleton (where all my app data is stored)
    [AppData clearData];

   // Reset view controller (this will quickly clear all the views)
   UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"MainStoryboard" bundle:nil];
   MainTabControllerViewController *viewController = (MainTabControllerViewController *)[storyboard instantiateViewControllerWithIdentifier:@"mainView"];
   [self.window setRootViewController:viewController];

   // Show login screen
   [self showLoginScreen:NO];

}

LoginViewController.m

Di sini, jika login berhasil, saya cukup mengabaikan tampilan dan mengirim pemberitahuan.

-(void) loginWasSuccessful
{

     // Send notification
     [[NSNotificationCenter defaultCenter] postNotificationName:@"loginSuccessful" object:self];

     // Dismiss login screen
     [self dismissViewControllerAnimated:YES completion:nil];

}

2
Untuk apa Anda menggunakan notifikasi?
pemberontakan

1
@ BeFeher benar. Saya menggunakan notifikasi untuk memicu tarikan data baru. Anda dapat menggunakannya untuk melakukan apa pun yang Anda inginkan, tetapi dalam kasus saya, saya perlu diberi tahu bahwa login berhasil, dan data segar diperlukan.
Trevor Gehman

24
Di iOS 8.1 (dan mungkin 8.0, belum diuji) ini tidak lagi berfungsi dengan lancar. Pengontrol Tampilan awal berkedip sebentar.
BFeher

7
Apakah ada versi Swift dari pendekatan ini?
Seano

9
@Julian Di iOS 8, saya mengganti dua baris [self.window makeKeyAndVisible]; [self.window.rootViewController presentViewController:viewController animated:animated completion:nil];dengan self.window.rootViewController = viewController;untuk mencegah flicker. Untuk menghidupkan yang hanya membungkusnya dalam[UIView transitionWithView...];
BFeher

20

EDIT: Tambahkan tindakan logout.

masukkan deskripsi gambar di sini

1. Pertama-tama siapkan file delegasi aplikasi

AppDelegate.h

#import <UIKit/UIKit.h>

@interface AppDelegate : UIResponder <UIApplicationDelegate>

@property (strong, nonatomic) UIWindow *window;
@property (nonatomic) BOOL authenticated;

@end

AppDelegate.m

#import "AppDelegate.h"
#import "User.h"

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    User *userObj = [[User alloc] init];
    self.authenticated = [userObj userAuthenticated];

    return YES;
}

2. Buat kelas bernama Pengguna.

User.h

#import <Foundation/Foundation.h>

@interface User : NSObject

- (void)loginWithUsername:(NSString *)username andPassword:(NSString *)password;
- (void)logout;
- (BOOL)userAuthenticated;

@end

Pengguna.m

#import "User.h"

@implementation User

- (void)loginWithUsername:(NSString *)username andPassword:(NSString *)password{

    // Validate user here with your implementation
    // and notify the root controller
    [[NSNotificationCenter defaultCenter] postNotificationName:@"loginActionFinished" object:self userInfo:nil];
}

- (void)logout{
    // Here you can delete the account
}

- (BOOL)userAuthenticated {

    // This variable is only for testing
    // Here you have to implement a mechanism to manipulate this
    BOOL auth = NO;

    if (auth) {
        return YES;
    }

    return NO;
}

3. Buat controller RootViewController baru dan terhubung dengan tampilan pertama, tempat tombol login hidup. Tambahkan juga ID Storyboard: "initialView".

RootViewController.h

#import <UIKit/UIKit.h>
#import "LoginViewController.h"

@protocol LoginViewProtocol <NSObject>

- (void)dismissAndLoginView;

@end

@interface RootViewController : UIViewController

@property (nonatomic, weak) id <LoginViewProtocol> delegate;
@property (nonatomic, retain) LoginViewController *loginView;


@end

RootViewController.m

#import "RootViewController.h"

@interface RootViewController ()

@end

@implementation RootViewController

@synthesize loginView;

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

- (IBAction)loginBtnPressed:(id)sender {

    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(loginActionFinished:)
                                                 name:@"loginActionFinished"
                                               object:loginView];

}

#pragma mark - Dismissing Delegate Methods

-(void) loginActionFinished:(NSNotification*)notification {

    AppDelegate *authObj = (AppDelegate*)[[UIApplication sharedApplication] delegate];
    authObj.authenticated = YES;

    [self dismissLoginAndShowProfile];
}

- (void)dismissLoginAndShowProfile {
    [self dismissViewControllerAnimated:NO completion:^{
        UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
        UITabBarController *tabView = [storyboard instantiateViewControllerWithIdentifier:@"profileView"];
        [self presentViewController:tabView animated:YES completion:nil];
    }];


}

@end

4. Buat controller baru LoginViewController dan terhubung dengan tampilan login.

LoginViewController.h

#import <UIKit/UIKit.h>
#import "User.h"

@interface LoginViewController : UIViewController

LoginViewController.m

#import "LoginViewController.h"
#import "AppDelegate.h"

- (void)viewDidLoad
{
    [super viewDidLoad];
}

- (IBAction)submitBtnPressed:(id)sender {
    User *userObj = [[User alloc] init];

    // Here you can get the data from login form
    // and proceed to authenticate process
    NSString *username = @"username retrieved through login form";
    NSString *password = @"password retrieved through login form";
    [userObj loginWithUsername:username andPassword:password];
}

@end

5. Pada akhirnya tambahkan controller ProfileViewController baru dan terhubung dengan tampilan profil di tabViewController.

ProfileViewController.h

#import <UIKit/UIKit.h>

@interface ProfileViewController : UIViewController

@end

ProfileViewController.m

#import "ProfileViewController.h"
#import "RootViewController.h"
#import "AppDelegate.h"
#import "User.h"

@interface ProfileViewController ()

@end

@implementation ProfileViewController

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        // Custom initialization
    }
    return self;
}

- (void)viewDidLoad
{
    [super viewDidLoad];

}

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

    if(![(AppDelegate*)[[UIApplication sharedApplication] delegate] authenticated]) {

        UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];

        RootViewController *initView =  (RootViewController*)[storyboard instantiateViewControllerWithIdentifier:@"initialView"];
        [initView setModalPresentationStyle:UIModalPresentationFullScreen];
        [self presentViewController:initView animated:NO completion:nil];
    } else{
        // proceed with the profile view
    }
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

- (IBAction)logoutAction:(id)sender {

   User *userObj = [[User alloc] init];
   [userObj logout];

   AppDelegate *authObj = (AppDelegate*)[[UIApplication sharedApplication] delegate];
   authObj.authenticated = NO;

   UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];

   RootViewController *initView =  (RootViewController*)[storyboard instantiateViewControllerWithIdentifier:@"initialView"];
   [initView setModalPresentationStyle:UIModalPresentationFullScreen];
   [self presentViewController:initView animated:NO completion:nil];

}

@end

LoginExample adalah proyek sampel untuk bantuan ekstra.


3
proyek sampel sangat membantu saya untuk memahami konsep login n logout .. terima kasih banyak :)
Dave

16

Saya tidak suka jawaban bhavya karena menggunakan AppDelegatedi dalam View Controllers dan pengaturan rootViewControllertidak memiliki animasi. Dan jawaban Trevor memiliki masalah dengan pengontrol tampilan berkedip di iOS8.

UPD 07/18/2015

AppDelegate di dalam View Controllers:

Mengubah status AppDelegate (properti) di dalam view controller merusak enkapsulasi.

Hirarki objek yang sangat sederhana di setiap proyek iOS:

AppDelegate (memiliki windowdan rootViewController)

ViewController (memiliki view)

Tidak apa-apa bahwa objek dari atas mengubah objek di bagian bawah, karena mereka membuatnya. Tapi tidak apa-apa jika objek di bawah mengubah objek di atasnya (saya jelaskan beberapa prinsip dasar pemrograman / OOP: DIP (Prinsip Ketergantungan Pembalikan: modul tingkat tinggi tidak boleh bergantung pada modul tingkat rendah, tetapi mereka harus bergantung pada abstraksi) ).

Jika ada objek yang akan mengubah objek apa pun dalam hierarki ini, cepat atau lambat akan ada kekacauan dalam kode. Mungkin ok di proyek kecil tapi tidak menyenangkan untuk menggali kekacauan ini di proyek bit =]

UPD 07/18/2015

Saya meniru animasi pengontrol modal menggunakan UINavigationController(tl; dr: periksa proyek ).

saya menggunakan UINavigationController untuk menyajikan semua pengontrol di aplikasi saya. Awalnya saya menampilkan pengontrol tampilan login di tumpukan navigasi dengan animasi push / pop biasa. Daripada saya memutuskan untuk mengubahnya ke modal dengan perubahan minimal.

Bagaimana itu bekerja:

  1. Pengontrol tampilan awal (atau self.window.rootViewController) adalah UINavigationController dengan ProgressViewController sebagai a rootViewController. Saya menunjukkan ProgressViewController karena DataModel dapat mengambil beberapa waktu untuk menginisialisasi karena inits tumpukan data inti seperti dalam artikel ini (saya sangat suka pendekatan ini).

  2. AppDelegate bertanggung jawab untuk mendapatkan pembaruan status login.

  3. DataModel menangani login / keluar pengguna dan AppDelegate mengamati userLoggedInproperti itu melalui KVO. Bisa dibilang bukan metode terbaik untuk melakukan ini tetapi itu bekerja untuk saya. (Mengapa KVO buruk, Anda dapat memeriksa bagian ini atau artikel ini (Mengapa Tidak Menggunakan Pemberitahuan?).

  4. ModalDismissAnimator dan ModalPresentAnimator digunakan untuk menyesuaikan animasi push standar.

Cara kerja logika animator:

  1. AppDelegate menetapkan dirinya sebagai delegasi dari self.window.rootViewController(yang merupakan UINavigationController).

  2. AppDelegate mengembalikan salah satu animator -[AppDelegate navigationController:animationControllerForOperation:fromViewController:toViewController:]jika perlu.

  3. Animator menerapkan -transitionDuration:dan -animateTransition:metode. -[ModalPresentAnimator animateTransition:]:

    - (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext
    {
        UIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
        [[transitionContext containerView] addSubview:toViewController.view];
        CGRect frame = toViewController.view.frame;
        CGRect toFrame = frame;
        frame.origin.y = CGRectGetHeight(frame);
        toViewController.view.frame = frame;
        [UIView animateWithDuration:[self transitionDuration:transitionContext]
                         animations:^
         {
             toViewController.view.frame = toFrame;
         } completion:^(BOOL finished)
         {
             [transitionContext completeTransition:![transitionContext transitionWasCancelled]];
         }];
    }

Proyek uji ada di sini .


3
Secara pribadi saya tidak punya masalah dengan View Controllers mengetahui tentang AppDelegate(saya akan tertarik untuk memahami mengapa Anda melakukannya) - tetapi komentar Anda tentang kurangnya animasi sangat valid. Itu bisa dipecahkan dengan jawaban ini: stackoverflow.com/questions/8053832/…
HughHughTeotl

2
@HughHughTeotl Terima kasih atas komentar dan tautannya. Saya memperbarui jawaban saya.
derpoliuk

1
@derpoliuk bagaimana jika pengontrol tampilan dasar saya adalah UITabBarController? Saya tidak bisa mendorongnya di UINavigationController.
Giorgio

@Iorgio, ini pertanyaan yang menarik, saya tidak menggunakannya UITabBarControlleruntuk waktu yang lama. Saya mungkin akan mulai dengan pendekatan jendela alih-alih memanipulasi pengontrol tampilan.
derpoliuk

11

Inilah solusi Swifty saya untuk setiap penonton di masa depan.

1) Buat protokol untuk menangani fungsi masuk dan keluar:

protocol LoginFlowHandler {
    func handleLogin(withWindow window: UIWindow?)
    func handleLogout(withWindow window: UIWindow?)
}

2) Perluas protokol tersebut dan berikan fungsionalitas di sini untuk keluar:

extension LoginFlowHandler {

    func handleLogin(withWindow window: UIWindow?) {

        if let _ = AppState.shared.currentUserId {
            //User has logged in before, cache and continue
            self.showMainApp(withWindow: window)
        } else {
            //No user information, show login flow
            self.showLogin(withWindow: window)
        }
    }

    func handleLogout(withWindow window: UIWindow?) {

        AppState.shared.signOut()

        showLogin(withWindow: window)
    }

    func showLogin(withWindow window: UIWindow?) {
        window?.subviews.forEach { $0.removeFromSuperview() }
        window?.rootViewController = nil
        window?.rootViewController = R.storyboard.login.instantiateInitialViewController()
        window?.makeKeyAndVisible()
    }

    func showMainApp(withWindow window: UIWindow?) {
        window?.rootViewController = nil
        window?.rootViewController = R.storyboard.mainTabBar.instantiateInitialViewController()
        window?.makeKeyAndVisible()
    }

}

3) Lalu saya bisa menyesuaikan AppDelegate saya dengan protokol LoginFlowHandler, dan memanggil handleLoginstartup:

class AppDelegate: UIResponder, UIApplicationDelegate, LoginFlowHandler {

    var window: UIWindow?

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {

        window = UIWindow.init(frame: UIScreen.main.bounds)

        initialiseServices()

        handleLogin(withWindow: window)

        return true
    }

}

Dari sini, ekstensi protokol saya akan menangani logika atau menentukan apakah pengguna jika masuk / keluar, dan kemudian mengubah windows rootViewController sesuai!


Tidak yakin apakah saya bodoh tetapi, AppDelegate tidak sesuai LoginFlowHandler. Apakah saya melewatkan sesuatu? Juga, saya menduga kode ini hanya mengelola login saat start up. Bagaimana cara saya mengelola logout dari pengontrol tampilan?
Lukas

@ Lukas karena semua logika diimplementasikan dalam ekstensi tidak perlu untuk mengimplementasikannya di AppDelegate. Itulah yang begitu hebat dalam Protokol Ekstensi.
shannoga

1
Maaf @sirFunkenstine, itu adalah kelas khusus yang saya buat untuk menunjukkan contoh bagaimana orang akan memeriksa cache aplikasi mereka untuk memeriksa pengguna yang sebelumnya masuk atau tidak. AppStateImplementasi ini karena itu akan tergantung pada bagaimana Anda menyimpan data pengguna Anda ke disk.
Harry Bloom

@ HarryBloom bagaimana cara menggunakan handleLogoutfungsionalitas?
nithinisreddy

1
Hai @nithinisreddy - untuk memanggil fungsionalitas handleLogout, Anda harus menyesuaikan kelas yang Anda panggil dari ke LoginFlowHandlerprotokol. Kemudian Anda akan mendapatkan ruang lingkup untuk dapat memanggil metode handleLogout. Lihat langkah 3 saya untuk contoh bagaimana saya melakukan itu untuk kelas AppDelegate.
Harry Bloom

8

Melakukan ini dari delegasi aplikasi TIDAK dianjurkan. AppDelegate mengelola siklus hidup aplikasi yang terkait dengan peluncuran, penangguhan, penghentian, dan sebagainya. Saya sarankan melakukan ini dari pengontrol tampilan awal Anda di viewDidAppear. Anda dapat self.presentViewControllerdan self.dismissViewControllerdari pengontrol tampilan login. Simpan boolkunci NSUserDefaultsuntuk melihat apakah itu diluncurkan untuk pertama kali.


2
Haruskah tampilan muncul (terlihat oleh pengguna) di `viewDidAppear '? Ini masih akan membuat kedipan.
Mark13426

2
Bukan jawaban. Dan "Simpan kunci bool di NSUserDefaults untuk melihat apakah itu diluncurkan untuk pertama kalinya." Sangat sangat berbahaya untuk data seperti itu.
skywinder

6

Buat ** LoginViewController ** dan ** TabBarController **.

Setelah membuat LoginViewController dan TabBarController , kita perlu menambahkan StoryboardID masing-masing sebagai " loginViewController " dan " tabBarController ".

Maka saya lebih suka membuat struct Constant :

struct Constants {
    struct StoryboardID {
        static let signInViewController = "SignInViewController"
        static let mainTabBarController = "MainTabBarController"
    }

    struct kUserDefaults {
        static let isSignIn = "isSignIn"
    }
}

Di LoginViewController tambahkan IBAction :

@IBAction func tapSignInButton(_ sender: UIButton) {
    UserDefaults.standard.set(true, forKey: Constants.kUserDefaults.isSignIn)
    Switcher.updateRootViewController()
}

Di ProfileViewController tambahkan IBAction :

@IBAction func tapSignOutButton(_ sender: UIButton) {
    UserDefaults.standard.set(false, forKey: Constants.kUserDefaults.isSignIn)
    Switcher.updateRootViewController()
}

Di AppDelegate tambahkan baris kode di didFinishLaunchingWithOptions :

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {

    Switcher.updateRootViewController()

    return true
}

Akhirnya buat kelas Switcher :

import UIKit

class Switcher {

    static func updateRootViewController() {

        let status = UserDefaults.standard.bool(forKey: Constants.kUserDefaults.isSignIn)
        var rootViewController : UIViewController?

        #if DEBUG
        print(status)
        #endif

        if (status == true) {
            let mainStoryBoard = UIStoryboard(name: "Main", bundle: nil)
            let mainTabBarController = mainStoryBoard.instantiateViewController(withIdentifier: Constants.StoryboardID.mainTabBarController) as! MainTabBarController
            rootViewController = mainTabBarController
        } else {
            let mainStoryBoard = UIStoryboard(name: "Main", bundle: nil)
            let signInViewController = mainStoryBoard.instantiateViewController(withIdentifier: Constants.StoryboardID.signInViewController) as! SignInViewController
            rootViewController = signInViewController
        }

        let appDelegate = UIApplication.shared.delegate as! AppDelegate
        appDelegate.window?.rootViewController = rootViewController

    }

}

Itu semuanya!


Apakah ada perbedaan tampilan controller mana yang paling awal di storyboard? Di foto Anda yang ditambahkan, saya dapat melihat bahwa Anda memiliki opsi "adalah Pengendali Tampilan Awal" yang dicentang pada Pengontrol Tab Bar. Di AppDelegate u beralih controller tampilan root utama jadi saya kira itu tidak masalah, bukan?
ShadeToD

@iAleksandr Harap perbarui jawaban untuk iOS 13. Coz dari SceneDelegate saat ini tidak berfungsi.
Nitesh

5

Di Xcode 7 Anda dapat memiliki beberapa storyBoards. Akan lebih baik jika Anda dapat menyimpan alur Login di storyboard terpisah.

Ini dapat dilakukan dengan menggunakan SELECT VIEWCONTROLLER> Editor> Refactor to Storyboard

Dan di sini adalah versi Swift untuk menetapkan tampilan sebagai RootViewContoller-

    let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
    appDelegate.window!.rootViewController = newRootViewController

    let rootViewController: UIViewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("LoginViewController")

3

Saya menggunakan ini untuk memeriksa peluncuran pertama:

- (NSInteger) checkForFirstLaunch
{
    NSInteger result = 0; //no first launch

    // Get current version ("Bundle Version") from the default Info.plist file
    NSString *currentVersion = (NSString*)[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"];
    NSArray *prevStartupVersions = [[NSUserDefaults standardUserDefaults] arrayForKey:@"prevStartupVersions"];
    if (prevStartupVersions == nil)
    {
        // Starting up for first time with NO pre-existing installs (e.g., fresh
        // install of some version)
        [[NSUserDefaults standardUserDefaults] setObject:[NSArray arrayWithObject:currentVersion] forKey:@"prevStartupVersions"];
        result = 1; //first launch of the app
    } else {
        if (![prevStartupVersions containsObject:currentVersion])
        {
            // Starting up for first time with this version of the app. This
            // means a different version of the app was alread installed once
            // and started.
            NSMutableArray *updatedPrevStartVersions = [NSMutableArray arrayWithArray:prevStartupVersions];
            [updatedPrevStartVersions addObject:currentVersion];
            [[NSUserDefaults standardUserDefaults] setObject:updatedPrevStartVersions forKey:@"prevStartupVersions"];
            result = 2; //first launch of this version of the app
        }
    }

    // Save changes to disk
    [[NSUserDefaults standardUserDefaults] synchronize];

    return result;
}

(jika pengguna menghapus aplikasi dan menginstalnya kembali, itu dianggap seperti peluncuran pertama)

Di AppDelegate saya memeriksa peluncuran pertama dan membuat pengontrol navigasi dengan layar login (login dan daftar), yang saya letakkan di atas jendela utama saat ini:

[self.window makeKeyAndVisible];

if (firstLaunch == 1) {
    UINavigationController *_login = [[UINavigationController alloc] initWithRootViewController:loginController];
    [self.window.rootViewController presentViewController:_login animated:NO completion:nil];
}

Karena ini ada di atas pengontrol tampilan biasa, pengontrol independen dari aplikasi Anda dan Anda dapat mengabaikan pengontrol tampilan, jika Anda tidak membutuhkannya lagi. Dan Anda juga dapat menyajikan tampilan dengan cara ini, jika pengguna menekan tombol secara manual.

BTW: Saya menyimpan data masuk dari pengguna saya seperti ini:

KeychainItemWrapper *keychainItem = [[KeychainItemWrapper alloc] initWithIdentifier:@"com.youridentifier" accessGroup:nil];
[keychainItem setObject:password forKey:(__bridge id)(kSecValueData)];
[keychainItem setObject:email forKey:(__bridge id)(kSecAttrAccount)];

Untuk logout: Saya beralih dari CoreData (terlalu lambat) dan menggunakan NSArrays dan NSDictionaries untuk mengelola data saya sekarang. Logout berarti mengosongkan array dan kamus tersebut. Plus saya memastikan untuk mengatur data saya di viewWillAppear.

Itu dia.


0

Saya berada dalam situasi yang sama dengan Anda dan solusi yang saya temukan untuk membersihkan data menghapus semua hal-hal CoreData yang diandalkan oleh pengontrol tampilan saya untuk menggambar info itu. Tetapi saya masih menemukan pendekatan ini sangat buruk, saya pikir cara yang lebih elegan untuk melakukan ini dapat dicapai tanpa storyboard dan hanya menggunakan kode untuk mengelola transisi antara pengontrol tampilan.

Saya telah menemukan proyek ini di Github yang melakukan semua ini hanya dengan kode dan sangat mudah dimengerti. Mereka menggunakan menu samping seperti Facebook dan yang mereka lakukan adalah mengubah pengontrol tampilan tengah tergantung apakah pengguna masuk atau tidak. Ketika pengguna keluar, appDelegatemenghapus data dari CoreData dan menetapkan pengontrol tampilan utama ke layar login lagi.


0

Saya memiliki masalah serupa untuk dipecahkan dalam suatu aplikasi dan saya menggunakan metode berikut. Saya tidak menggunakan notifikasi untuk menangani navigasi.

Saya memiliki tiga storyboard di app.

  1. Storyboard layar splash - untuk inisialisasi aplikasi dan memeriksa apakah pengguna sudah masuk
  2. Login storyboard - untuk menangani alur masuk pengguna
  3. Tab bar storyboard - untuk menampilkan konten aplikasi

Storyboard awal saya di app adalah storyboard layar Splash. Saya memiliki pengontrol navigasi sebagai root dari login dan storyboard tab bar untuk menangani navigasi pengontrol tampilan.

Saya membuat kelas Navigator untuk menangani navigasi aplikasi dan tampilannya seperti ini:

class Navigator: NSObject {

   static func moveTo(_ destinationViewController: UIViewController, from sourceViewController: UIViewController, transitionStyle: UIModalTransitionStyle? = .crossDissolve, completion: (() -> ())? = nil) {
       

       DispatchQueue.main.async {

           if var topController = UIApplication.shared.keyWindow?.rootViewController {

               while let presentedViewController = topController.presentedViewController {

                   topController = presentedViewController

               }

               
               destinationViewController.modalTransitionStyle = (transitionStyle ?? nil)!

               sourceViewController.present(destinationViewController, animated: true, completion: completion)

           }

       }

   }

}

Mari kita lihat skenario yang mungkin:

  • Peluncuran aplikasi pertama; Layar splash akan dimuat di mana saya memeriksa apakah pengguna sudah masuk. Kemudian layar masuk akan dimuat menggunakan kelas Navigator sebagai berikut;

Karena saya memiliki pengontrol navigasi sebagai root, saya instantiate pengontrol navigasi sebagai pengontrol tampilan awal.

let loginSB = UIStoryboard(name: "splash", bundle: nil)

let loginNav = loginSB.instantiateInitialViewcontroller() as! UINavigationController

Navigator.moveTo(loginNav, from: self)

Ini menghapus storyboard slpash dari root jendela aplikasi dan menggantinya dengan storyboard login.

Dari papan cerita masuk, ketika pengguna berhasil masuk, saya menyimpan data pengguna ke Default Pengguna dan menginisialisasi singleton UserData untuk mengakses detail pengguna. Kemudian storyboard Tab bar dimuat menggunakan metode navigator.

Let tabBarSB = UIStoryboard(name: "tabBar", bundle: nil)
let tabBarNav = tabBarSB.instantiateInitialViewcontroller() as! UINavigationController

Navigator.moveTo(tabBarNav, from: self)

Sekarang pengguna keluar dari layar pengaturan di bilah tab. Saya menghapus semua data pengguna yang disimpan dan menavigasi ke layar masuk.

let loginSB = UIStoryboard(name: "splash", bundle: nil)

let loginNav = loginSB.instantiateInitialViewcontroller() as! UINavigationController

Navigator.moveTo(loginNav, from: self)
  • Pengguna masuk dan memaksa membunuh aplikasi

Saat pengguna meluncurkan aplikasi, layar Splash akan dimuat. Saya memeriksa apakah pengguna masuk dan mengakses data pengguna dari Default Pengguna. Kemudian inisialisasi singleton UserData dan tampilkan bilah tab alih-alih layar masuk.


-1

Terima kasih solusi bhavya. Ada dua jawaban tentang swift, tetapi itu tidak terlalu utuh. Saya telah melakukannya di swift3. Di bawah ini adalah kode utama.

Di AppDelegate.swift

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
    // Override point for customization after application launch.

    // seclect the mainStoryBoard entry by whthere user is login.
    let userDefaults = UserDefaults.standard

    if let isLogin: Bool = userDefaults.value(forKey:Common.isLoginKey) as! Bool? {
        if (!isLogin) {
            self.window?.rootViewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "LogIn")
        }
   }else {
        self.window?.rootViewController = mainStoryboard.instantiateViewController(withIdentifier: "LogIn")
   }

    return true
}

Di SignUpViewController.swift

@IBAction func userLogin(_ sender: UIButton) {
    //handle your login work
    UserDefaults.standard.setValue(true, forKey: Common.isLoginKey)
    let delegateTemp = UIApplication.shared.delegate
    delegateTemp?.window!?.rootViewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "Main")
}

Dalam fungsi logOutAction

@IBAction func logOutAction(_ sender: UIButton) {
    UserDefaults.standard.setValue(false, forKey: Common.isLoginKey)
    UIApplication.shared.delegate?.window!?.rootViewController = UIStoryboard(name: "Main", bundle: nil).instantiateInitialViewController()
}

Hai Eli. Pertanyaan yang Anda jawab sudah memiliki jawaban yang sangat bagus bagi pasangan. Ketika Anda memutuskan untuk menjawab pertanyaan seperti itu, harap pastikan untuk menjelaskan mengapa jawaban Anda lebih baik daripada jawaban yang sangat bagus yang sudah diposting.
Noel Widmer

Hai Noel. Saya perhatikan jawaban lain untuk cepat. Tapi saya menganggap jawabannya tidak terlalu utuh. Jadi saya mengirimkan jawaban saya tentang versi swift3. Ini akan membantu programmer baru yang cepat. Terima kasih! @Noel Widmer.
WangYang

Bisakah Anda menambahkan penjelasan itu di bagian atas posting Anda? Dengan begitu semua orang dapat langsung melihat manfaat dari jawaban Anda. Selamat bersenang-senang di SO! :)
Noel Widmer

1
Tank untuk saran Anda. Saya telah menambahkan penjelasannya. Terima kasih lagi. @ Noel Widmer.
WangYang

Solusi samar yang tidak menyoroti penggunaan kata kunci 'Biasa'.
Samarey

-3

masukkan deskripsi gambar di sini

Di App Delegate.m

 - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
[[UIBarButtonItem appearance] setBackButtonTitlePositionAdjustment:UIOffsetMake(0, -60)
                                                     forBarMetrics:UIBarMetricsDefault];

NSString *identifier;
BOOL isSaved = [[NSUserDefaults standardUserDefaults] boolForKey:@"loginSaved"];
if (isSaved)
{
    //identifier=@"homeViewControllerId";
    UIWindow* mainWindow=[[[UIApplication sharedApplication] delegate] window];
    UITabBarController *tabBarVC =
    [[UIStoryboard storyboardWithName:@"Main" bundle:nil] instantiateViewControllerWithIdentifier:@"TabBarVC"];
    mainWindow.rootViewController=tabBarVC;
}
else
{


    identifier=@"loginViewControllerId";
    UIStoryboard *    storyboardobj=[UIStoryboard storyboardWithName:@"Main" bundle:nil];
    UIViewController *screen = [storyboardobj instantiateViewControllerWithIdentifier:identifier];

    UINavigationController *navigationController=[[UINavigationController alloc] initWithRootViewController:screen];

    self.window.rootViewController = navigationController;
    [self.window makeKeyAndVisible];

}

return YES;

}

view controller.m Dalam pandangan memang memuat

- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.

UIBarButtonItem* barButton = [[UIBarButtonItem alloc] initWithTitle:@"Logout" style:UIBarButtonItemStyleDone target:self action:@selector(logoutButtonClicked:)];
[self.navigationItem setLeftBarButtonItem:barButton];

}

Dalam aksi tombol logout

-(void)logoutButtonClicked:(id)sender{

UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"Alert" message:@"Do you want to logout?" preferredStyle:UIAlertControllerStyleAlert];

    [alertController addAction:[UIAlertAction actionWithTitle:@"Logout" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
           NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    [defaults setBool:NO forKey:@"loginSaved"];
           [[NSUserDefaults standardUserDefaults] synchronize];
      AppDelegate *appDelegate = [UIApplication sharedApplication].delegate;
    UIStoryboard *    storyboardobj=[UIStoryboard storyboardWithName:@"Main" bundle:nil];
    UIViewController *screen = [storyboardobj instantiateViewControllerWithIdentifier:@"loginViewControllerId"];
    [appDelegate.window setRootViewController:screen];
}]];


[alertController addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
    [self dismissViewControllerAnimated:YES completion:nil];
}]];

dispatch_async(dispatch_get_main_queue(), ^ {
    [self presentViewController:alertController animated:YES completion:nil];
});}

Mengapa ada kebutuhan untuk menambahkan beberapa fungsionalitas ke dalam file ViewController.m ??
Eesha

@ Eesha Dia menambahkan item tombol "logout" TabBar ke TabBar. Saya kira gambar hilang jika tidak Anda bisa melihatnya.
helloWorld

Menyimpan kunci masuk NSUserDefaultssangat tidak aman untuk data seperti itu!
skywinder
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.