Oturum kapatıldıktan sonra verilerin silinmesini sağlayan Storyboard giriş ekranı için en iyi uygulamalar


290

Bir Storyboard kullanarak bir iOS uygulaması oluşturuyorum. Kök görünüm denetleyicisi bir Sekme Çubuğu Denetleyicisidir. Giriş / çıkış işlemini oluşturuyorum ve çoğunlukla iyi çalışıyor, ancak birkaç sorunum var. Tüm bunları ayarlamanın en iyi yolunu bilmem gerekiyor.

Aşağıdakileri başarmak istiyorum:

  1. Uygulamanın ilk açılışında bir giriş ekranı gösterin. Oturum açtıklarında, Sekme Çubuğu Denetleyicisinin ilk sekmesine gidin.
  2. Bundan sonra uygulamayı başlattıklarında, oturum açıp açmadıklarını kontrol edin ve doğrudan kök Sekme Çubuğu Denetleyicisinin ilk sekmesine atlayın.
  3. Oturum kapatma düğmesini elle tıklattıklarında, oturum açma ekranını gösterin ve görünüm denetleyicilerinden tüm verileri silin.

Şimdiye kadar yaptığım, kök görünüm denetleyicisini Sekme Çubuğu Denetleyicisine ayarlamak ve Giriş görünümü denetleyicime özel bir segue oluşturmak. Tab Bar Denetleyici sınıfım içinde, viewDidAppearyöntem içinde oturum açmış olup olmadıklarını ve bir segue gerçekleştirin:[self performSegueWithIdentifier:@"pushLogin" sender:self];

Ayrıca, oturum kapatma eyleminin ne zaman gerçekleştirilmesi gerektiğine dair bir bildirim ayarladım: [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(logoutAccount) name:@"logoutAccount" object:nil];

Oturumu kapattıktan sonra, kimlik bilgilerini Anahtarlıktan temizler [self setSelectedIndex:0], giriş görünümü denetleyicisini tekrar göstermek için segimi çalıştırır ve gerçekleştiririm.

Tüm bunlar iyi çalışıyor, ama merak ediyorum: Bu mantık AppDelegate'te olmalı mı? Ayrıca iki sorunum var:

  • Uygulamayı ilk başlattıklarında, Sekme Çubuğu Denetleyicisi, segue gerçekleştirilmeden önce kısa bir süre gösterir. Kod taşıma denedim viewWillAppearama segue bu erken çalışmaz.
  • Oturumu kapattıklarında, tüm veriler hala tüm görünüm denetleyicilerinin içindedir. Yeni bir hesaba giriş yaparlarsa, eski hesap verileri yenilenene kadar görüntülenmeye devam eder. Çıkışta bunu kolayca temizlemenin bir yoluna ihtiyacım var.

Bunu elden geçirmeye açığım. Giriş ekranını kök görünüm denetleyicisi yapmayı veya her şeyi işlemek için AppDelegate'te bir gezinme denetleyicisi oluşturmayı düşündüm ... Bu noktada en iyi yöntemin ne olduğundan emin değilim.


Giriş görünümü denetleyicisini kalıcı olarak sunuyor musunuz?
vokilam

@TrevorGehman - film şeridi resminizi ekleyebilirsiniz
rohan k shah

Sonunda ne yaptığımın ayrıntılarını içeren bir cevap gönderdim. Sağlanan diğer cevaplara benzer, özellikle @bhavya kothari.
Trevor Gehman

Giriş ekranının sunulması için AuthNavigation yararlı olabilir. Gerekirse bir oturum açma ekranının sunumunu düzenler ve ayrıca otomatik oturum açmayı destekler.
Codey

Daha iyi yapılabilirdi gibi hemen hemen her zaman çözüldü ama aynı zamanda hissediyor de olduğu çok temel sorunlarından biri
amar

Yanıtlar:


311

Film şeridiniz şöyle görünmelidir

DidFinishLaunchingWithOptions içindeki appDelegate.m içinde

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

SignUpViewController.m dosyasında

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

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

MyTabThreeViewController.m dosyasında

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

}

Swift 4 Sürümü

İlk görünüm denetleyicinizin TabbarController'da imzalı olduğunu varsayarak uygulama temsilcisindeki didFinishLaunchingWithOptions.

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

    return true

Kayıt görünümü denetleyicisinde:

@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

Oturumdan sonra bool kimlik doğrulamasını userDefaults'tan silmeyi unuttunuz
CodeLover

28
AppDelegateİçinde kullanmak UIViewControllerve window.rootViewControllerorada ayarlamak için -1 . Bunu "en iyi uygulama" olarak görmüyorum.
derpoliuk

2
-1Bir cevap göndermeden vermek istemedim : stackoverflow.com/a/30664935/1226304
derpoliuk

1
Im IOS8 üzerinde hızlı bir şekilde bunu yapmaya çalışıyorum ama app ateş ve giriş ekranı gösterirken aşağıdaki hatayı alıyorum: "Görünüm geçişleri başlatmak / bitirmek için dengesiz aramalar". Uygulama yüklendiğinde giriş ekranının gösterildiğini, ancak sekme çubuğu denetleyicisindeki ilk sekmenin de yüklendiğini fark ettim. Bu, viewdidload'da println () ile onaylandı. Öneriler?
Alex Lacayo

1
Bingo! -2. -1 için AppDelegateUIViewController-1 içinde giriş anahtar saklanması için NSUserDefaults. Bu tür veriler için çok güvensiz!
skywinder

97

İşte her şeyi başarmak için yaptığım şey. Buna ek olarak dikkate almanız gereken tek şey (a) giriş işlemi ve (b) uygulama verilerinizi depoladığınız yerdir (bu durumda, bir singleton kullandım).

Giriş görünümü denetleyicisini ve ana sekme denetleyicisini gösteren film şeridi

Gördüğünüz gibi, kök görünüm denetleyicisi Ana Sekme Denetleyicim . Bunu yaptım çünkü kullanıcı giriş yaptıktan sonra, uygulamanın doğrudan ilk sekmeye başlamasını istiyorum. (Bu, giriş görünümünün geçici olarak gösterdiği "titremeyi" önler.)

AppDelegate.m

Bu dosyada, kullanıcının zaten giriş yapmış olup olmadığını kontrol ediyorum. Değilse, giriş görünümü denetleyicisini itiyorum. Ayrıca, verileri temizlediğim ve giriş görünümünü gösterdiğim oturum kapatma işlemini de ele alıyorum.

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

Burada, giriş başarılı olursa, sadece görünümü kapatıp bir bildirim gönderirim.

-(void) loginWasSuccessful
{

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

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

}

2
Bildirimi ne için kullanıyorsunuz?
isyan

1
@BFeher haklı. Yeni bir veri çekme işlemini tetiklemek için bildirimi kullandım. İstediğiniz her şeyi yapmak için kullanabilirsiniz, ancak benim durumumda, girişin başarılı olduğu ve yeni verilere ihtiyaç duyulduğu konusunda bilgilendirilmem gerekiyordu.
Trevor Gehman

24
İOS 8.1'de (ve belki de 8.0, test edilmedi) bu artık düzgün çalışmıyor. İlk Görüntü Denetleyicisi kısa bir süre yanıp söner.
BFeher

7
Bu yaklaşımın Swift versiyonu var mı?
Seano

9
@Julian iOS 8'de titremeyi önlemek için iki satırı [self.window makeKeyAndVisible]; [self.window.rootViewController presentViewController:viewController animated:animated completion:nil];da değiştiriyorum self.window.rootViewController = viewController;. Bu sadece bir sarın canlandırmak için[UIView transitionWithView...];
BFeher

20

EDIT: Çıkış eylemi ekleyin.

resim açıklamasını buraya girin

1. Öncelikle uygulama delege dosyasını hazırlayın

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. Kullanıcı adında bir sınıf oluşturun.

User.h

#import <Foundation/Foundation.h>

@interface User : NSObject

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

@end

User.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. Yeni bir RootViewController denetleyicisi oluşturun ve ilk görünüm ile bağlantı kurun, burada oturum açma düğmesi canlı. Ayrıca bir Storyboard Kimliği ekleyin: "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. LoginViewController adlı yeni bir denetleyici oluşturun ve oturum açma görünümüyle bağlantı kurun.

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. Sonunda yeni bir denetleyici ProfileViewController ekleyin ve tabViewController'daki profil görünümü ile bağlantı kurun.

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 , ekstra yardım için örnek bir projedir.


3
örnek proje bana giriş n oturum kapatma kavramını anlamak için çok yardımcı oldu .. çok teşekkürler :)
Dave

16

AppDelegateGörünüm Denetleyicileri içinde kullandığım ve ayarın rootViewControlleranimasyon olmadığı için bhavya'nın cevabını beğenmedim . Ve Trevor'ın cevabının iOS8'de yanıp sönen görünüm denetleyicisi ile ilgili sorunu var.

UPD 18.07.2015

Görünüm Denetleyicilerinin İçindeki AppDelegate:

Görünüm denetleyicisinin içindeki AppDelegate durumunu (özelliklerini) değiştirmek kapsüllemeyi keser.

Her iOS projesinde çok basit nesne hiyerarşisi:

AppDelegate (sahibi windowve rootViewController)

ViewController (sahibi view)

En üstteki nesnelerin alt taraftaki nesneleri değiştirmeleri uygundur, çünkü onları oluşturuyorlar. Ancak alt kısımdaki nesneler üstlerindeki nesneleri değiştirirse sorun olmaz (bazı temel programlama / OOP prensibini tanımladım: DIP (Bağımlılık Ters Çevirme Prensibi: yüksek seviye modülü düşük seviye modülüne bağlı olmamalı, ancak soyutlamalara bağlı olmalıdır) ).

Herhangi bir nesne bu hiyerarşideki herhangi bir nesneyi değiştirirse, er ya da geç kodda bir karışıklık olacaktır. Küçük projelerde iyi olabilir ama bit projelerinde bu karışıklığı kazmak eğlenceli değil]

UPD 18.07.2015

Modsal denetleyici animasyonlarını UINavigationController(tl; dr: projeyi kontrol et ) kullanarak çoğaltırım .

UINavigationControllerUygulamamdaki tüm denetleyicileri sunmak için kullanıyorum . Başlangıçta navigasyon yığınında düz itme / pop animasyonlu oturum açma görünümü denetleyicisini görüntüledim. Daha sonra minimal değişikliklerle modal olarak değiştirmeye karar verdim.

Nasıl çalışır:

  1. İlk görünüm denetleyicisi (veya self.window.rootViewController), ProgressViewController olarak bir UINavigationController rootViewController. ProgressViewController gösteriyorum çünkü DataModel başlatmak için biraz zaman alabilir çünkü bu makalede olduğu gibi çekirdek veri yığını inits (gerçekten bu yaklaşım gibi).

  2. AppDelegate, giriş durumu güncellemelerini almaktan sorumludur.

  3. DataModel kullanıcı oturum açma / kapatma işlemlerini gerçekleştirir ve AppDelegate userLoggedInmülkünü KVO aracılığıyla gözlemler. Muhtemelen bunu yapmak için en iyi yöntem değil ama benim için çalışıyor. (Neden KVO kötü, check-in yapabilirsiniz bu ya bu makalede (Neden Değil Kullanım Bildirimleri? Kısım).

  4. ModalDismissAnimator ve ModalPresentAnimator varsayılan push animasyonunu özelleştirmek için kullanılır.

Animatörler mantığı nasıl çalışır:

  1. AppDelegate kendisini self.window.rootViewController(UINavigationController olan) bir temsilci olarak ayarlar .

  2. AppDelegate -[AppDelegate navigationController:animationControllerForOperation:fromViewController:toViewController:]gerekirse animatörlerden birini döndürür .

  3. Animatörler uygular -transitionDuration:ve -animateTransition:yöntemler. -[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]];
         }];
    }

Test projesi burada .


3
Şahsen View Controllers ile ilgili hiçbir sorunum yok AppDelegate(neden yaptığınızı anlamak isterim) - ama animasyon eksikliği hakkındaki yorumunuz çok geçerli. Bu şu cevapla çözülebilir: stackoverflow.com/questions/8053832/…
HughHughTeotl

2
@HughHughTeotl Yorum ve bağlantı için teşekkür ederiz. Cevabımı güncelledim.
derpoliuk

1
@derpoliuk temel görünüm denetleyicim bir UITabBarController ise? Bir UINavigationController itemezsiniz.
Giorgio

@Giorgio, ilginç bir soru, UITabBarControllerçok uzun zamandır kullanmadım. Muhtemelen görünüm denetleyicilerini manipüle etmek yerine pencere yaklaşımı ile başlardım .
derpoliuk

11

İşte gelecekteki izleyiciler için Swifty çözümüm.

1) Hem oturum açma hem de oturum kapatma işlevlerini işlemek için bir protokol oluşturun:

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

2) Söz konusu protokolü uzatın ve oturumu kapatmak için işlevselliği sağlayın:

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) Sonra AppDelegate'imi LoginFlowHandler protokolüne uygun hale getirebilir ve handleLoginbaşlangıçta çağırabilirim :

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
    }

}

Buradan, benim protokol uzantısı benim mantık veya kullanıcı giriş / çıkış olup olmadığını belirlemek ve daha sonra windows rootViewController uygun şekilde değiştirmek olacaktır!


Aptal olup olmadığımdan emin değilim, AppDelegate uymuyor LoginFlowHandler. Bir şey mi kaçırıyorum? Ayrıca, bu kod sadece başlangıçta giriş yönetir tahmin ediyorum. Oturumu bir görünüm denetleyicisinden nasıl yönetirim?
luke

@luke, tüm mantık uzantıda uygulandığından, AppDelegate'te uygulamaya gerek yoktur. Protokol Uzantılarında bu kadar harika.
shannoga

1
Üzgünüz @sirFunkenstine, bir kullanıcının önceden oturum açıp açmadığını kontrol etmek için uygulama önbelleğini nasıl kontrol edeceğine dair bir örnek göstermek için oluşturduğum özel bir sınıftı. Bu AppStatenedenle, bu uygulama kullanıcı verilerinizi diske nasıl kaydettiğinize bağlı olacaktır.
Harry Bloom

@HarryBloom işlevsellik nasıl kullanılır handleLogout?
nithinisreddy

1
Hi @nithinisreddy - handleLogout işlevini çağırmak için, aradığınız sınıfı LoginFlowHandlerprotokole uymanız gerekir . Daha sonra handleLogout yöntemini çağırabilmek için kapsam elde edersiniz. AppDelegate sınıfı için bunu nasıl yaptığımın bir örneği için 3. adımıma bakın.
Harry Bloom

8

Bunu uygulama temsilcisinden yapmanız önerilmez. AppDelegate, başlatma, askıya alma, sonlandırma vb. İle ilgili uygulama yaşam döngüsünü yönetir. Bunu, içindeki ilk görünüm denetleyicinizden yapmanızı öneririz viewDidAppear. Yapabilirsiniz self.presentViewControllerve self.dismissViewControllergiriş görünümü denetleyicisinden. İlk kez başlatılıp başlatılmadığını görmek için bir boolanahtarı NSUserDefaultssaklayın.


2
Görünüm `` viewDidAppear '' da görünmeli mi (kullanıcı tarafından görülebilir)? Bu yine de bir titreşim yaratacaktır.
Mark13426

2
Cevap değil. Ve "İlk kez başlatılıp başlatılmadığını görmek için NSUserDefaults'ta bir bool anahtarı depolayın." Bu tür veriler için çok tehlikelidir.
skywinder

6

** LoginViewController ** ve ** TabBarController ** oluşturun.

LoginViewController ve TabBarController'ı oluşturduktan sonra sırasıyla “ loginViewController ” ve “ tabBarController ” olarak bir StoryboardID eklememiz gerekiyor .

Sonra Constant yapısını oluşturmayı tercih ederim :

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

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

In LoginViewController eklemek IBAction :

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

In ProfileViewController eklemek IBAction :

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

In AppDelegate kod satırını ekleyin didFinishLaunchingWithOptions :

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

    Switcher.updateRootViewController()

    return true
}

Son olarak Switcher sınıfını oluşturun :

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

    }

}

Hepsi bu!


Film şeridinde hangi görünüm denetleyicisinin başlangıçta olduğu konusunda herhangi bir fark var mı? Eklediğiniz fotoğrafta Tab Bar Controller'da "Initial View Controller" seçeneğinin işaretli olduğunu görebiliyorum. AppDelegate u önemli değil sanırım ana kök görünüm denetleyicisi geçiş, değil mi?
ShadeToD

@iAleksandr Lütfen iOS 13 için cevabı güncelleyin. SceneDelegate'in Coz güncel yanıtı çalışmıyor.
Nitesh

5

Xcode 7'de birden fazla storyBoards olabilir. Giriş akışını ayrı bir hikaye tahtasında tutabilmeniz daha iyi olacaktır.

Bu, SELECT VIEWCONTROLLER> Editör> Öykü Panosuna Refactor kullanılarak yapılabilir

Ve burada RootViewContoller olarak bir görünüm ayarlamak için Swift sürümü

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

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

3

İlk lansmanı kontrol etmek için bunu kullanıyorum:

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

(kullanıcı uygulamayı silip yeniden yüklerse, ilk başlatma gibi sayılır)

AppDelegate'te ilk başlatmayı kontrol ediyorum ve mevcut ana pencerenin üstüne koyduğum giriş ekranlarıyla (giriş ve kayıt) bir navigasyon denetleyicisi oluşturuyorum:

[self.window makeKeyAndVisible];

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

Bu normal görünüm denetleyicisinin üstünde olduğundan, uygulamanızın geri kalanından bağımsızdır ve artık ihtiyacınız yoksa görünüm denetleyicisini kapatabilirsiniz. Ayrıca kullanıcı manuel olarak bir düğmeye bastığında da görünümü bu şekilde sunabilirsiniz.

BTW: Kullanıcılarımdan giriş verilerini şu şekilde kaydediyorum:

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

Çıkış için: CoreData'dan (çok yavaş) geçtim ve şimdi verilerimi yönetmek için NSArrays ve NSDictionaries kullanıyorum. Çıkış sadece dizileri ve sözlükleri boşaltmak anlamına gelir. Ayrıca verilerimi viewWillAppear'da ayarladığımdan emin olun.

Bu kadar.


0

Sizinle aynı durumdayım ve verileri temizlemek için bulduğum çözüm, görünüm denetleyicilerinin bu bilgileri çizmek için güvendiği tüm CoreData öğelerini silmektir. Ama yine de bu yaklaşımı çok kötü buldum, bunu yapmak için daha zarif bir yolun film şeridi olmadan ve görünüm denetleyicileri arasındaki geçişleri yönetmek için sadece kod kullanılarak gerçekleştirilebileceğini düşünüyorum.

Tüm projeyi sadece kodla yapan Github'da buldum ve anlaşılması oldukça kolay. Facebook benzeri bir yan menü kullanıyorlar ve yaptıkları şey, kullanıcının oturum açmış olup olmamasına bağlı olarak orta görünüm denetleyicisini değiştirmek. Kullanıcı oturumu kapattığında appDelegateverileri CoreData öğesinden kaldırır ve ana görünüm denetleyicisini tekrar oturum açma ekranına ayarlar.


0

Bir uygulamada çözmek için benzer bir sorunum vardı ve aşağıdaki yöntemi kullandım. Gezinmeyi işlemek için bildirimler kullanmadım.

Uygulamada üç film şeridim var.

  1. Açılış ekranı film şeridi - uygulamanın başlatılması ve kullanıcının zaten giriş yapmış olup olmadığını kontrol etmek için
  2. Giriş film şeridi - kullanıcı giriş akışını işlemek için
  3. Sekme çubuğu film şeridi - uygulama içeriğini görüntülemek için

Uygulamadaki ilk film şeridim Splash ekran film şerididir. Görünüm denetleyicisi gezinmelerini işlemek için oturum açma kökü ve sekme çubuğu film şeridi olarak gezinti denetleyicim var.

Uygulama navigasyonunu işlemek için bir Navigator sınıfı oluşturdum ve şöyle görünüyor:

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)

           }

       }

   }

}

Olası senaryolara bakalım:

  • İlk uygulama lansmanı; Kullanıcının zaten oturum açıp açmadığını kontrol ettiğimde açılış ekranı yüklenecek. Sonra oturum açma ekranı Navigator sınıfı kullanılarak yüklenecektir;

Kök olarak gezinme denetleyicim olduğundan, gezinme denetleyicisini ilk görünüm denetleyicisi olarak başlatırım.

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

let loginNav = loginSB.instantiateInitialViewcontroller() as! UINavigationController

Navigator.moveTo(loginNav, from: self)

Bu, slpash film şeridini uygulama penceresinin kökünden kaldırır ve giriş film şeridi ile değiştirir.

Oturum açma film şeridinden, kullanıcı başarıyla giriş yaptığında, kullanıcı verilerini Kullanıcı Varsayılanlarına kaydeder ve kullanıcı ayrıntılarına erişmek için bir UserData singletonu başlatırım. Ardından Sekme çubuğu film şeridi, navigator yöntemi kullanılarak yüklenir.

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

Navigator.moveTo(tabBarNav, from: self)

Şimdi kullanıcı sekme çubuğundaki ayarlar ekranından çıkış yapıyor. Kaydedilmiş tüm kullanıcı verilerini temizler ve giriş ekranına giderim.

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

let loginNav = loginSB.instantiateInitialViewcontroller() as! UINavigationController

Navigator.moveTo(loginNav, from: self)
  • Kullanıcı oturum açtı ve uygulamayı öldürmeye zorla

Kullanıcı uygulamayı başlattığında, Sıçrama ekranı yüklenir. Kullanıcının oturum açıp açmadığını kontrol ediyorum ve kullanıcı verilerine Kullanıcı Varsayılanları'ndan erişiyorum. Ardından UserData singletonunu başlatır ve giriş ekranı yerine sekme çubuğunu gösterir.


-1

Teşekkürler bhavya'nın çözümü.Hızla ilgili iki cevap var, ama bunlar çok sağlam değil. Bunu swift3.Below ana kodu var.

AppDelegate.swift'te

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
}

SignUpViewController.swift içinde

@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")
}

LogOutAction işlevinde

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

Merhaba Eli. Cevapladığınız sorunun zaten birkaç iyi cevabı var. Böyle bir soruyu cevaplamaya karar verdiğinizde, lütfen yanıtınızın neden zaten gönderilmiş olan çok iyi olanlardan daha iyi olduğunu açıkladığınızdan emin olun.
Noel Widmer

Merhaba Noel. Diğer cevapların hızlı olduğunu fark ettim. Ama cevapların çok sağlam olmadığını düşündüm. Bu yüzden swift3 sürümü hakkındaki cevabımı gönderiyorum. Yeni hızlı programcı için yardım olurdu. Teşekkür ederim! @Noel Widmer.
WangYang

Bu açıklamayı yayınınızın üstüne ekleyebilir misiniz? Bu şekilde herkes cevabınızın faydasını hemen görebilir. SO iyi eğlenceler! :)
Noel Widmer

1
Öneriniz için tanklar.Açıklamayı ekledim.Teşekkürler. @ Noel Widmer.
WangYang

'Ortak' anahtar kelime kullanımını vurgulamayan belirsiz çözüm.
Samarey

-3

resim açıklamasını buraya girin

Uygulama Delegesi.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 Görünümde yük var

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

}

Çıkış düğmesi eyleminde

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

Neden ViewController.m dosyasında bazı işlevsellik eklemek gerekiyor?
Eesha

@Eesha TabBar'a bir "oturumu kapat" TabBar düğmesi öğesi ekledi. Sanırım görüntü eksik, onu görebiliyordunuz.
helloWorld

Mağaza giriş anahtarı NSUserDefaultsbu tür veriler için çok çok güvensizdir!
skywinder
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.