3

アプリの起動時、およびユーザーがホームボタンを押してアプリを再起動した後に再びアクティブになったときに、モーダルビューコントローラー(ログイン画面用)を表示したいと思います。

私は最初に、ルートビューコントローラのviewDidAppear:メソッドでモーダルビューを表示しようとしました。これは、アプリが最初に起動したときにうまく機能しますが、アプリが再びアクティブになったときにこのメソッドは呼び出されません。

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

- (void)presentModalView {
    if(![AuthenticationService sharedInstance].isAuthenticated) {
        _modalVC = [self.storyboard instantiateViewControllerWithIdentifier:self.modalViewControllerIdentifier];
        _modalVC.delegate = self;
        [self presentViewController:_modalVC animated:YES completion:nil];
    }
}

applicationDidBecomeActive:次に、メソッド内のアプリデリゲートからこれを呼び出そうとしました。

- (void)applicationDidBecomeActive:(UIApplication *)application
{
    // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.

    ModalPresentingUISplitViewController *splitViewController = (ModalPresentingUISplitViewController *)self.window.rootViewController;
    [splitViewController presentModalView];
}

これは表面上は正常に機能しているように見えますがUnbalanced calls to begin/end appearance transitions for <ModalPresentingUISplitViewController: 0x7251590>、ログに警告が表示されます。UISplitViewがそれ自体の表示を終了する前に、どういうわけかモーダルビューを表示しているように感じますが、これを回避する方法がわかりません。

アプリがアクティブになったときにルートビューコントローラーからモーダルビューを「自動的に」表示し、分割ビューコントローラーのバランスを崩さないように「適切な」タイミングで表示するにはどうすればよいですか?

4

3 に答える 3

1

この質問がここにあったことを忘れていました。はい、解決策があります。これを行うためのよりエレガントな、または正しい方法があると感じずにはいられませんが、これは私にとってはうまくいきました...

これは、ARC とストーリーボードを使用していることを前提としています。UISplitViewController (またはルート ビュー コントローラー) からのモーダル セグエを使用して、ログイン ビュー用の UIViewController を作成しました。

UISplitViewController (またはルート ビュー コントローラーが何であれ)

- (id)initWithCoder:(NSCoder *)aDecoder {
    if(self = [super initWithCoder:aDecoder]) {
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(presentModalView) name:UIApplicationDidBecomeActiveNotification object:nil];
    }
    return self;
}

- (void)dealloc {
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidBecomeActiveNotification object:nil];
}

- (void) viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    self.viewHasAppeared = YES;
    [self presentModalView];
}

- (void) presentModalView {
    if(self.viewHasAppeared && !self.userAuthenticated) {
        [self performSegueWithIdentifier:@"ShowLoginView" sender:self];
    }
}

- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
    if([[segue identifier] isEqualToString:@"ShowLoginView"]) {
        JDPLoginViewController *dest = [segue destinationViewController];
        dest.delegate = self;
    }
}

- (void) dismissLogin {
    self.userAuthenticated = YES;
    [self dismissViewControllerAnimated:YES completion:nil];
}

注意すべきコードの重要な部分は次のとおりです...

  1. presentModalView2 つの場所を呼び出しています。ここviewDidAppearでは、アプリの最初の起動時にログイン ビューを表示し、
  2. を UIApplicationDidBecomeActiveNotification イベントのオブザーバーとして登録しpresentModalViewているため、アプリがバックグラウンドでアクティブになったときにメソッドが呼び出されます。
  3. viewHasAppeared最後に、UISplitViewController のビューが表示されたかどうかを追跡するために、UISplitViewController にBOOL プロパティを作成しているため、UISplitViewController のビューが表示される前にモーダル ログインを提示しようとしません。

ここにさまざまなシナリオがあります...

アプリの最初の起動:

  • presentModalViewがイベントによって呼び出されUIApplicationDidBecomeActiveNotificationますが、UISplitViewController のビューが読み込まれていないため (そしてviewHasAppearedBOOL が NO であるため、何も起こりません。Win ではありません。表示すべきでないときにビューを表示しません。
  • その後、最終的にが呼び出され、 YESviewDidAppearに設定されてから が呼び出されます。ログイン画面が表示されます。すべてが期待どおりに機能します。viewHasAppearedpresentModalView

バックグラウンドになった後にアプリがアクティブになる

  • presentModalView最初のシナリオと同様にイベントによって再度呼び出されUIApplicationDidBecomeActiveNotificationますが、今回viewHasAppearedは YES であるため、ログイン ビューは期待どおりに表示されます。またね!

私が言ったように、これはちょっと醜い気がしますが、より良い解決策が見つかるまで仕事を終わらせます. それがうまくいくことを願っています。

于 2013-04-06T01:58:02.203 に答える
0

UIViewのviewWillAppearを試しましたか?

于 2012-09-21T15:42:41.173 に答える
0

@jpoleteの答えを連鎖させて、私は少し違ったやり方をしました。さらに、アプリがバックグラウンドで 15 秒以上経過した後にのみログイン画面が表示されるようにしました (ユーザーが常にログインし直す必要があるのは苦痛です)。

このデモのソース コードはgithubにあります。

@jpolete のように、ほとんどのロジックをルート ビュー コントローラー (私の場合はナビゲーション コントローラー) にカプセル化しました (iPhone の例)。userLoggedInユーザーが認証されているかどうかのフラグ。フラグはpresentingLoginController、ログイン画面が現在表示されているかどうかを知らせてくれます。backgroundTimeユーザーがバックグラウンドに入ったときのタイムスタンプを保持します。クラス拡張は次のとおりです。

@interface RootNavigationController () <LoginDelegate>
@property (assign, nonatomic) BOOL userLoggedIn;
@property (strong, nonatomic) NSDate *backgroundTime;
@property (assign, nonatomic) BOOL presentingLoginController;
-(void)applicationDidBecomeActive:(NSNotification*) notification;
-(void)applicationDidEnterBackground:(NSNotification*) notification;
@end

ビューが読み込まれたら、適切な通知フックを追加します。

@implementation RootNavigationController

- (void)viewDidLoad
{
    [super viewDidLoad];
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(applicationDidBecomeActive:)
                                                 name:UIApplicationDidBecomeActiveNotification
                                               object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(applicationDidEnterBackground:)
                                                 name:UIApplicationDidEnterBackgroundNotification
                                               object:nil];
}

-(void)dealloc {
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

ここでは、ユーザーが認証されておらず、現在ログイン コントローラーを提示していない場合に、ログイン セグエをトリガーします。

-(void)loginIfNecessary {
    if (!self.userLoggedIn && !self.presentingLoginController) {
        self.presentingLoginController = YES;
        [self performSegueWithIdentifier:@"RootLoginSegue" sender:self];
    }
}

ここでは、ルート ビュー コントローラーをloginDelegateログイン コントローラーの に設定します。このデリゲートは、ログインが成功すると通知されます (ログイン コントローラーは別のナビゲーション コントローラーに組み込まれています)。

-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
    if ([segue.identifier isEqualToString:@"RootLoginSegue"]) {
        UINavigationController *navController = segue.destinationViewController;
        LoginTableViewController *loginController = (LoginTableViewController *) navController.topViewController;
        loginController.loginDelegate = self;
    }
}

ログインが成功すると、次の処理が行われます。

-(void)didLogin { // LoginDelegate method called to login controller after successsful login
    self.presentingLoginController = NO;
    self.userLoggedIn = YES;
}

ビューが初めて表示されたとき、バックグラウンドになった後に表示されたとき、または「隠蔽」された後にログインします (必要な場合)。

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

バックグラウンドに入ると、時間を記録します。

-(void)applicationDidEnterBackground:(NSNotification*) notification {
    self.backgroundTime = [NSDate date];
}

フォアグラウンドに入って初めて、または十分な時間が経過すると、ユーザーに再度ログインを強制します (必要な場合)。

-(void) applicationDidBecomeActive:(NSNotification*) notification {
    const NSTimeInterval maxBackgroundTime = 15.0;
    if (!self.backgroundTime || [[NSDate date] timeIntervalSinceDate:self.backgroundTime] > maxBackgroundTime) {
        self.userLoggedIn = NO;
    }
    [self loginIfNecessary];
}

@end
于 2014-06-12T19:26:27.997 に答える