2012-07-28 62 views
4

我以各種形式看到此問題,但沒有明確的答案。我會在這裏提問並回答。我的應用程序需要在啓動時進行工作... inits,幾個網絡調用,登錄等。我不想讓我的主視圖控制器工作,直到完成。這樣做有什麼好的模式?如何使用故事板呈現飛濺/登錄視圖控制器

要求:

  • 的iOS5 +,情節串連圖板,ARC。
  • 直到啓動工作完成,主vc的視圖才能出現。
  • 想要在啓動工作完成時向主vc選擇轉換樣式。
  • 想要在故事板中儘可能多地進行佈局。代碼應該乾淨地包含在某個明顯的地方。

回答

13

編輯,2017年7月 由於第一次寫,我已經改變了我的做法是一個,我給啓動任務自己的視圖控制器。在那個VC中,我檢查啓動條件,根據需要提供一個「繁忙」的UI等。根據啓動時的狀態,我設置窗口的根VC。

除了解決OP問題之外,此方法還具有更好地控制啓動UI和UI轉換的其他好處。下面是如何做到這一點:

在您的主要故事板中,添加一個名爲LaunchViewController的新VC,並將其作爲應用程序的初始vc。爲您的應用程序的「真實」初始vc指定一個標識符,如「AppUI」(標識符位於IB的Identity標籤中)。

確定其他主要UI流(例如註冊/登錄,教程等)啓動的vcs並提供這些描述性標識符。 (有些人喜歡將每個流程都保存在自己的故事板中,這是一個很好的做法,IMO)。

另一個不錯的可選想法:給你的應用程序的啓動故事板的vc標識符(如「LaunchVC」),以便你可以抓住它並在啓動過程中使用它的視圖。這將在啓動期間以及在您執行啓動任務時爲用戶提供無縫體驗。

這裏是我的LaunchViewController樣子....

@implementation LaunchViewController 

- (void)viewDidLoad { 
    [super viewDidLoad]; 

    // optional, but I really like this: 
    // cover my view with my launch screen's view for a seamless start 
    UIStoryboard *storyboard = [self.class storyboardWithKey:@"UILaunchStoryboardName"]; 
    UINavigationController *vc = [storyboard instantiateViewControllerWithIdentifier:@"LaunchVC"]; 
    [self.view addSubview:vc.view]; 
} 

- (void)viewWillDisappear:(BOOL)animated { 
    [super viewWillDisappear:animated]; 
    [self hideBusyUI]; 
} 

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

    // start your startup logic here: 
    // let's say you need to do a network transaction... 
    // 
    [someNetworkCallingObject doSomeNetworkCallCompletion:^(id result, NSError *error) { 
     if (/* some condition */) [self.class presentUI:@"AppUI"]; 
     else if (/* some condition */) [self.class presentUI:@"LoginUI"]; 
     // etc. 
    }]; 
} 

#pragma mark - Busy UI 

// optional, but maybe you want a spinner or something while getting started 
- (void)showBusyUI { 
    // in one app, I add a spinner on my launch storyboard vc 
    // give it a tag, and give the logo image a tag, too 
    // here in animation, I fade out the logo and fade in a spinner 
    UIImageView *logo = (UIImageView *)[self.view viewWithTag:32]; 
    UIActivityIndicatorView *aiv = (UIActivityIndicatorView *)[self.view viewWithTag:33]; 
    [UIView animateWithDuration:0.5 animations:^{ 
     logo.alpha = 0.0; 
     aiv.alpha = 1.0; 
    }]; 
} 

- (void)hideBusyUI { 
    // an animation that reverses the showBusyUI 
} 

#pragma mark - Present UI 

+ (void)presentUI:(NSString *)identifier { 
    UIStoryboard *storyboard = [self storyboardWithKey:@"UIMainStoryboardFile"]; 
    UINavigationController *vc = [storyboard instantiateViewControllerWithIdentifier:identifier]; 

    UIWindow *window = [UIApplication sharedApplication].delegate.window; 
    window.rootViewController = vc; 

    // another bonus of this approach: any VC transition you like to 
    // any of the app's main flows 
    [UIView transitionWithView:window 
         duration:0.3 
         options:UIViewAnimationOptionTransitionCrossDissolve 
        animations:nil 
        completion:nil]; 
} 

+ (UIStoryboard *)storyboardWithKey:(NSString *)key { 
    NSBundle *bundle = [NSBundle mainBundle]; 
    NSString *storyboardName = [bundle objectForInfoDictionaryKey:key]; 
    return [UIStoryboard storyboardWithName:storyboardName bundle:bundle]; 
} 

@end 

下原來的答案,但我更喜歡我目前的做法

讓我們表達應用程序的準備運行具有主VC布爾,是這樣的:

BOOL readyToRun = startupWorkIsDone && userIsLoggedIn; 
  1. 創建一個AppStartupViewController並將它放在應用程序故事板中。
  2. 不要拖動任何segue到它,並且不要將它變成凝視vc,只是讓它在某處浮動。
  3. 在故事板中的vc屬性檢查器中,將其標識符設置爲「AppStartupViewController」。

在AppStartupViewController中。男,當readyToRun條件已經滿足,就可以辭退自己:

self.modalTransitionStyle = UIModalTransitionStyleCrossDissolve; // your choice here from UIModalTransitionStyle 
[self dismissViewControllerAnimated:YES completion:nil]; 

現在,每當應用程序被激活,它可以檢查準備工作,因此,它在需要出示AppStartupViewController。 在AppDelegate.h

- (void)applicationDidBecomeActive:(UIApplication *)application { 

    BOOL readyToRun = startupWorkIsDone && userIsLoggedIn; 

    if (!readyToRun) { 
     UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"MainStoryboard" bundle:nil]; 
     AppStartupViewController *startupVC = [storyboard instantiateViewControllerWithIdentifier:@"AppStartupViewController"]; 

     [self.window.rootViewController presentViewController:startupVC animated:NO completion:nil]; 
     // animate = NO because we don't want to see the mainVC's view 
    } 
} 

這主要是答案,但有一個順利。不幸的是,在AppStartupViewController呈現之前,主要的vc被加載(沒關係),並得到一個viewWillAppear:消息(不好)。這意味着我們必須蔓延一點點額外的啓動邏輯,這樣,在MainViewController.m:

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

    if (readyToRun) { 
     // the view will appear stuff i would have done unconditionally before 
    } 
} 

我希望這是有幫助的。

+0

,我嘗試了你的解決方案爲我的應用程序,但我確實得到了你提到的順利。 您是否介意說明「視圖會出現我以前無條件完成的任務」 – 2012-08-01 14:47:24

+0

當然 - 在我提供的解決方案中,您的應用中的主要vc將獲取viewWillAppear的兩個調用: - 首先在您的AppStartup vc被呈現,而第二個主vc真的要出現了。問題在於第一個。我希望我的主要vc能夠假設在獲取任何查看消息之前,所有事情都已準備好,但首先(並且錯誤由Apple,我認爲)發送給viewWillAppear:發生在我運行啓動邏輯之前。 (它不會出現,爲什麼它會得到這個信息?)繼續... – danh 2012-08-01 16:13:00

+0

因此,我在那裏添加了readyToRun條件。 「視圖將顯示內容......」是您打算在該方法中執行的任何操作,任何應該只在應用程序準備就緒時才運行的內容。這很難表達。請考慮如何編輯我的soln以獲得更多清晰度。 – danh 2012-08-01 16:14:43

0

使用導航控制器時的另一種解決方案。

  1. 您的導航控制器是初始視圖控制器,將您的主控制器設置爲導航控制器根視圖。

  2. 將您的加載控制器添加到故事板,並鏈接到樣式模態的命名segue。

  3. 在您的主控制器的viewWillAppear觸發segue(每個應用程序只運行一次)。

- (void)viewWillAppear:(BOOL)animated 
{ 
    [super viewWillAppear:animated]; 
    if(_isFirstRun) 
    { 
     _isFirstRun = NO; 
     [self performSegueWithIdentifier:@"segueLoading" sender:nil]; 
    } 
} 

如果觸發viewDidLoad中的SEGUE它不會工作可能是因爲導航控制器的動畫還沒有完成,如果你不介意我問,你會收到一個unbalanced calls to begin/end appearance transitions

相關問題