編集、2017 年 7 月
最初の執筆以来、私は自分のプラクティスを自分のビュー コントローラーにスタートアップ タスクを与えるものに変更しました。その VC で、起動条件をチェックし、必要に応じて「ビジー」な UI を表示するなどします。起動時の状態に基づいて、ウィンドウのルート VC を設定します。
OP の問題を解決することに加えて、このアプローチには、起動 UI と UI 遷移をより適切に制御できるという追加の利点があります。方法は次のとおりです。
メインのストーリーボードで、という新しい VC を追加し、LaunchViewController
それをアプリの初期 VC にします。アプリの「実際の」初期 vc に「AppUI」などの識別子を付けます (識別子は IB の [ID] タブにあります)。
メインの UI フロー (サインアップ/ログイン、チュートリアルなど) の開始点である他の vc を識別し、これらの説明的な識別子も提供します。(各フローを独自のストーリーボードに保持することを好む人もいます。それは良い習慣です、IMO)。
もう 1 つの優れたオプションのアイデア: アプリの起動ストーリーボードの 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 を作成し、アプリ ストーリーボードに配置します。
- セグエをドラッグしないでください。また、凝視している VC にしないでください。どこかに浮かせたままにします。
- ストーリーボードの vc の属性インスペクターで、その識別子を「AppStartupViewController」に設定します。
AppStartupViewController.m では、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
}
}
それがほとんどの答えですが、問題が 1 つあります。残念ながら、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
}
}
これがお役に立てば幸いです。