我以各種形式看到此問題,但沒有明確的答案。我會在這裏提問並回答。我的應用程序需要在啓動時進行工作... inits,幾個網絡調用,登錄等。我不想讓我的主視圖控制器工作,直到完成。這樣做有什麼好的模式?如何使用故事板呈現飛濺/登錄視圖控制器
要求:
- 的iOS5 +,情節串連圖板,ARC。
- 直到啓動工作完成,主vc的視圖才能出現。
- 想要在啓動工作完成時向主vc選擇轉換樣式。
- 想要在故事板中儘可能多地進行佈局。代碼應該乾淨地包含在某個明顯的地方。
我以各種形式看到此問題,但沒有明確的答案。我會在這裏提問並回答。我的應用程序需要在啓動時進行工作... inits,幾個網絡調用,登錄等。我不想讓我的主視圖控制器工作,直到完成。這樣做有什麼好的模式?如何使用故事板呈現飛濺/登錄視圖控制器
要求:
編輯,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;
在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
}
}
我希望這是有幫助的。
使用導航控制器時的另一種解決方案。
您的導航控制器是初始視圖控制器,將您的主控制器設置爲導航控制器根視圖。
將您的加載控制器添加到故事板,並鏈接到樣式模態的命名segue。
在您的主控制器的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
,我嘗試了你的解決方案爲我的應用程序,但我確實得到了你提到的順利。 您是否介意說明「視圖會出現我以前無條件完成的任務」 – 2012-08-01 14:47:24
當然 - 在我提供的解決方案中,您的應用中的主要vc將獲取viewWillAppear的兩個調用: - 首先在您的AppStartup vc被呈現,而第二個主vc真的要出現了。問題在於第一個。我希望我的主要vc能夠假設在獲取任何查看消息之前,所有事情都已準備好,但首先(並且錯誤由Apple,我認爲)發送給viewWillAppear:發生在我運行啓動邏輯之前。 (它不會出現,爲什麼它會得到這個信息?)繼續... – danh 2012-08-01 16:13:00
因此,我在那裏添加了readyToRun條件。 「視圖將顯示內容......」是您打算在該方法中執行的任何操作,任何應該只在應用程序準備就緒時才運行的內容。這很難表達。請考慮如何編輯我的soln以獲得更多清晰度。 – danh 2012-08-01 16:14:43