起動時に Facebook ログインを実装するアプリがあります。このコードは、ストーリーボードを除いてFacebook Scrumptious Tutorialとほぼ同じです。
コードの基本的な要点は、アプリの起動時にアプリ デリゲートがユーザーが既にログインしているかどうかを確認し、ログインしている場合は直接メイン ビューに移動し、ログインしていない場合はメイン ビューにログインを表示するように要求することです。ユーザーがログインできるように表示します。
ビュー階層からメイン ビューを取得し、ビューでセグエを呼び出すことで、ストーリーボードのメイン ビューに別のビューを表示するように指示できるという他の問題は既に解決しています。それはすべて正常に機能しますが、解決すべき最後の問題が 1 つあります。
私の知る限り、アプリケーションの didFinishLaunchingWithOptionsメソッドは、ストーリーボードが完全に読み込まれた後に呼び出されることになっています。ただし、私のコードでは、メイン ビューに別のビューを表示するように指示しようとすると、基本的にまだ読み込まれていないというエラーが表示されます (警告: Attempt to present < QLoginViewController: 0x955c020> on < UINavigationController: 0xa28c6e0> which ビューはウィンドウ階層にありません! )。
ただし、遅延後にビューを表示するように指示した場合:
[self performSelector:@selector(showLoginViewAnimated:) withObject:NO afterDelay:.001];
( showLoginViewAnimated:は、ログイン ビューを表示するようにメイン ビューに指示するメソッドです)、正常に動作します。
ここで何が問題なのか、どうすれば修正できるのかを誰かが教えてくれますか? 別のデバイスがビューの読み込みに時間がかかるかどうかを知ることができないため、セレクターを遅延して実行することは明らかに悪い回避策です...
ここに私の appDelegate didFinishLaunchingWithOptionsコードがあります:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
self.navigationController = (UINavigationController *)self.window.rootViewController;
// Navigation Bar Color
[[UINavigationBar appearance] setTintColor:[UIColor colorWithRed:255.0/255.0 green:128.0/255.0 blue:60.0/255.0 alpha:1.0]];
/* Facebook Login */ // THIS IS THE RELEVANT CODE:
// See if we have a valid token for the current state
if (FBSession.activeSession.state == FBSessionStateCreatedTokenLoaded) {
// Yes, valid token exists - open the session (don't display login page)
[self openSession];
} else {
// No, valid token does not exist - display the login page.
if ([self.navigationController isViewLoaded]) {
[self showLoginViewAnimated:NO]; // MY ATTEMPT TO AVOID USING THE DELAY IF POSSIBLE
}
else {
[self performSelector:@selector(showLoginViewAnimated:) withObject:NO afterDelay:.001]; // Delay needed to allow for storyboard loading
}
}
return YES;
}
そして、ここにshowLoginViewAnimated:コードがあります:
- (void)showLoginViewAnimated:(BOOL)animated
{
UIViewController *topViewController = [self.navigationController topViewController];
UIViewController *presentedViewController = [topViewController presentedViewController];
// If the login screen is not already displayed, display it. If the login screen is
// displayed, then getting back here means the login in progress did not successfully
// complete. In that case, notify the login view so it can update its UI appropriately.
if (![presentedViewController isKindOfClass:[QLoginViewController class]]) {
if (animated) {
[topViewController performSegueWithIdentifier:@"ShowLoginViewAnimated" sender:self];
}
else {
[topViewController performSegueWithIdentifier:@"ShowLoginViewStatic" sender:self];
}
}
else {
QLoginViewController *loginViewController = (QLoginViewController *)presentedViewController;
[loginViewController loginFailed];
}
}
アプリの元の非ストーリーボード バージョンでは、アプリケーションdidFinishLaunchingWithOptionsでshowLoginViewAnimated:メソッドが呼び出されませんでしたが、次のように手動でビューを作成していました。
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.
self.viewController = [[MainViewController alloc] initWithNibName:@"MainViewController" bundle:nil];
self.navController = [[UINavigationController alloc] initWithRootViewController:self.viewController];
self.window.rootViewController = self.navController;
[self.window makeKeyAndVisible];
したがって、ビューに新しいビューを提示するように指示する前に、ビューが存在することを 100% 確実に知っています。ただし、アプリのストーリーボード バージョンでは、そのコードは存在しないため、理論的には、ストーリーボード ビューが完全に読み込まれるまでアプリケーションの didFinishLaunchingWithOptionsメソッドが呼び出されないことを信頼する必要がありますが、そうではないようです。 . おそらく、代わりに非同期で実行していますか?何も思いつきません...
何か案は?助けてくれてありがとう!
編集:これはチュートリアルの元のコードです。これは完全に正常に動作し、ペン先だけでほぼ同じことを行います。(BOOL)animatedパラメーターをコードのshowLoginViewメソッドに追加しましたが、それは別のものであり、何も変更しません (確認済み)。
元の (ストーリーボードではない) appDelegate didFinishLaunchingWithOptionsメソッドは次のとおりです。
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.
self.viewController = [[FBLViewController alloc] initWithNibName:@"FBLViewController" bundle:nil];
self.navController = [[UINavigationController alloc] initWithRootViewController:self.viewController];
self.window.rootViewController = self.navController;
[self.window makeKeyAndVisible];
// See if we have a valid Facebook token for the current state
if (FBSession.activeSession.state == FBSessionStateCreatedTokenLoaded) {
// Yes, so just open the session (this won't display any UX).
[self openSession];
}
else {
// No, display the login page.
[self showLoginView];
}
return YES;
}
元の (ストーリーボードではない) showLoginViewメソッドは次のとおりです。
- (void)showLoginView
{
UIViewController *topViewController = [self.navController topViewController];
UIViewController *presentedViewController = [topViewController presentedViewController];
// IF the login screen is not already displayed, display it. If the login screen is
// displayed, then getting back here means the login in progress did not successfully
// complete. In that case, notify the login view so it can update its UI appropriately.
if (![presentedViewController isKindOfClass:[FBLLoginViewController class]]) {
FBLLoginViewController *loginViewController = [[FBLLoginViewController alloc] initWithNibName:@"FBLLoginViewController" bundle:nil];
loginViewController.delegate = self;
[topViewController presentViewController:loginViewController animated:NO completion:nil];
}
else {
FBLLoginViewController *loginViewController = (FBLLoginViewController *)presentedViewController;
[loginViewController loginFailed];
}
}