3

簡単に言うと、ユーザーがself.view(ナビゲーション バー以外の場所で) タップできるビュー コントローラーがあり、下部のコントロールがフェードアウトし、ナビゲーションとステータス バーがフェードアウトするフル スクリーン モードになります。iBooksに似ています。

ナビゲーション バーのアルファを単純にフェードすることもできますが、ユーザーが新しく取得した領域 (ナビゲーション バーがフェードアウトした場所) をタップして何かを実行できるようにするには、アルファ、ナビゲーション バーはまだ技術的に領域を占めているためです。

でナビゲーションバーを非表示にし[self.navigationController setNavigationBarHidden:YES animated:NO];ます。アニメーション ブロックが終了した後にこれを行う必要があります。そうしないと、アニメーション ブロックに含まれ、ブロックの一部としてアニメーション化されます。dispatch_afterそのため、アニメーションが完了した後に終了させるためにa を使用します(0.35 秒の遅延)。

ただし、これにより、ユーザーが 0.35 秒のアニメーションで終了を待っている間にいつでもタップすると、別のブロックが 0.35 秒待機しているにもかかわらず、別のブロックが開始されるグリッチ動作が発生するという問題が発生します。終わる。これにより、いくつかのグリッチ動作が発生し、ナビゲーション バーが非表示のままになります。きもい。

それが起こっているビデオ: http://cl.ly/2i3H0k0Q1T0V

私がやっていることを示すコードは次のとおりです。

- (void)hideControls:(BOOL)hidden {
    self.navigationController.view.backgroundColor = self.view.backgroundColor;
    int statusBarHeight = [UIApplication sharedApplication].statusBarFrame.size.height;

    [UIView animateWithDuration:0.35 animations:^{
        [[UIApplication sharedApplication] setStatusBarHidden:hidden withAnimation:UIStatusBarAnimationFade];

        if (hidden) {
            self.navigationController.navigationBar.alpha = 0.0;
            self.instructionsLabel.alpha = 0.0;
            self.backFiftyWordsButton.alpha = 0.0;
            self.forwardFiftyWordsButton.alpha = 0.0;
            self.WPMLabel.alpha = 0.0;
            self.timeRemainingLabel.alpha = 0.0;
        }
        else {
            self.navigationController.navigationBar.alpha = 1.0;
            self.instructionsLabel.alpha = 1.0;
            self.backFiftyWordsButton.alpha = 1.0;
            self.forwardFiftyWordsButton.alpha = 1.0;
            self.WPMLabel.alpha = 1.0;
            self.timeRemainingLabel.alpha = 1.0;
        }

        [self.view layoutIfNeeded];
    }];

    // Perform an "actual" hide (more than just alpha changes) after the animation finishes in order to regain that touch area
    if (hidden) {
        double delayInSeconds = 0.35;
        dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
        dispatch_after(popTime, dispatch_get_main_queue(), ^(void) {
            [self.navigationController setNavigationBarHidden:YES animated:NO];
            self.textToReadLabelPositionFromTopConstraint.constant = TEXT_LABEL_DISTANCE + self.navigationController.navigationBar.frame.size.height + statusBarHeight;
        });
    }
    else {
        [self.navigationController setNavigationBarHidden:NO animated:NO];
        self.textToReadLabelPositionFromTopConstraint.constant = TEXT_LABEL_DISTANCE;
    }
}

私が行っている唯一の他のことは、Auto Layout 制約の定数を変更して、そこにあるかどうかに依存するナビゲーション バーとステータス バーを考慮に入れることです。

ダブルタップが実際にフルスクリーンプロセスに不具合を起こす可能性があるという事実をどのように考慮に入れるかはわかりません. アニメーションのプロセス中にタップすると、アニメーションがキャンセルされ、意図したとおりに目的のアクションが実行されるようにするにはどうすればよいですか? このプロセスをもっとうまくやれるでしょうか?

4

8 に答える 8

2

これらの原則を使用して、フレームや制約を調整せずにこれを行うことができると思います。

1) ウィンドウの背景色をビューの色と同じにする

2) ウィンドウにタップ ジェスチャ レコグナイザーを追加します。これにより、ナビゲーション バーが表示されているかどうかに関係なく、画面上の任意の場所 (アルファが 0 でない場合のステータス バーを除く) をタップできます。これにより、ビューのサイズ変更の原因となるナビゲーション バーを非表示に設定する必要がなくなります。

3) タッパーのアクション メソッドで hitTest: を使用して、ユーザーがナビゲーション バーをタップしたかどうかを確認し、タップがあった場合はフェードアウトしないようにします。

4) アニメーション ブロックで UIViewAnimationOptionBeginFromCurrentState と UIViewAnimationOptionAllowUserInteraction を使用して、フェードインまたはフェードアウトを別のタッチでスムーズに反転できるようにします。

5) 下部のすべてのコントロールを明確な UIView で囲み、個々のコントロールすべてではなくその UIView をフェードアウトできるようにします。

動作したコードは次のとおりです。

- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    self.view.window.backgroundColor = self.view.backgroundColor;
    UITapGestureRecognizer *tapper = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(fadeInFadeOut:)];
    [self.view.window addGestureRecognizer:tapper];

}


-(void)fadeInFadeOut:(UITapGestureRecognizer *)sender {
    static BOOL hide = YES;
    id hitView = [self.navigationController.view hitTest:[sender locationInView:self.navigationController.view] withEvent:nil];

    if (! [hitView isKindOfClass:[UINavigationBar class]] && hide == YES) {
        hide = ! hide;
        [[UIApplication sharedApplication] setStatusBarHidden:YES withAnimation:UIStatusBarAnimationFade];
        [UIView animateWithDuration:.35 delay:0 options:UIViewAnimationOptionBeginFromCurrentState|UIViewAnimationOptionAllowUserInteraction animations:^{
            self.navigationController.navigationBar.alpha = 0;
            self.bottomView.alpha = 0;
        } completion:nil];

    }else if (hide == NO){
        hide = ! hide;
        [[UIApplication sharedApplication] setStatusBarHidden:NO withAnimation:UIStatusBarAnimationFade];
        [UIView animateWithDuration:.35 delay:0 options:UIViewAnimationOptionBeginFromCurrentState|UIViewAnimationOptionAllowUserInteraction animations:^{
            self.navigationController.navigationBar.alpha = 1;
            self.bottomView.alpha = 1;
        } completion:nil];
    }
}
于 2013-08-07T04:42:44.420 に答える
1

編集#2

ドキュメントを見ると、ビューのすべてのサブビューをhitTest:withEvent:呼び出すだけです。pointTest:withEvent:アルファ レベルが 0.01 未満のビューは無視されることが明確に示されています。私たちはここで正しい道を進んでいると思います。さらに調査する必要があります。alpha == 0.0fその下のビューへのパススルータッチを持つビューを持つ方法があると確信しています。うまくいけば、あなた(またはここにいる他の誰か)がそれを手に入れるでしょう. 時間があれば、いくつかのコードに飛び込んで、さらに支援しようとします。

編集#1:オーバーライドしてみてくださいpointInside:withEvent:

ここで意識の流れに答えてすみません。通常、私はこれを自分でテストするか、本番アプリからコードを貼り付けますが、今は忙しすぎます。

オーバーライドが機能すると思いますpointInside:withEvent

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
    if (self.alpha == 0.0f) {
        return NO;
    }
    else {
        return [super pointInside:point withEvent:event];
    }
}

元の回答:ナビゲーション バーが非表示のときにタッチを無視するように、サブクラス化UINavigationBarとオーバーライドを試みます。hitTest:withEvent:私はこれをテストしませんでしたが、次のようになるはずです:

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
    // if alpha == 0.0f, the nav bar should ignore touches
    if (self.alpha == 0.0f) {
        return nil;
    }

    // else hitTest as normal
    else {
        return [super hitTest:point withEvent:event];
    }
}

alpha == 0.0f のテストが、タッチを無視することを決定する方法ではない場合は、独自のプロパティを追加して、そのようにすることもできます。

@property (nonatomic) BOOL ignoreTouches;

実装では、nilhitTest:withEvent:をチェックして返します。if (ignoreTouches)

于 2013-08-07T00:10:41.627 に答える
0

を使用できますanimateWithDuration:animations:completion:。完了ブロックは、アニメーションが完了した後に実行されます ( source )。また、追加の利点もあります。将来、アニメーションのタイミングを変更することにした場合、2 つの場所でタイミングを変更することを心配する必要はありません。

[UIView animateWithDuration:0.35 animations:^{
    [[UIApplication sharedApplication] setStatusBarHidden:hidden withAnimation:UIStatusBarAnimationFade];

    if (hidden) {
        self.navigationController.navigationBar.alpha = 0.0;
        self.instructionsLabel.alpha = 0.0;
        self.backFiftyWordsButton.alpha = 0.0;
        self.forwardFiftyWordsButton.alpha = 0.0;
        self.WPMLabel.alpha = 0.0;
        self.timeRemainingLabel.alpha = 0.0;
    }
    else {
        self.navigationController.navigationBar.alpha = 1.0;
        self.instructionsLabel.alpha = 1.0;
        self.backFiftyWordsButton.alpha = 1.0;
        self.forwardFiftyWordsButton.alpha = 1.0;
        self.WPMLabel.alpha = 1.0;
        self.timeRemainingLabel.alpha = 1.0;
    }

    [self.view layoutIfNeeded];
}
completion:^(BOOL finished){
    // Perform an "actual" hide (more than just alpha changes) after the animation finishes in order to regain that touch area
    if ( finished ) {
        if (hidden) {
            [self.navigationController setNavigationBarHidden:YES animated:NO];
            self.textToReadLabelPositionFromTopConstraint.constant = TEXT_LABEL_DISTANCE + self.navigationController.navigationBar.frame.size.height + statusBarHeight;
        }
        else {
            [self.navigationController setNavigationBarHidden:NO animated:NO];
            self.textToReadLabelPositionFromTopConstraint.constant = TEXT_LABEL_DISTANCE;
        }
    }
}];

コメントに基づいて、アニメーションが開始する前にナビゲーションバーを無効にし、完了ブロックで再度有効にすることができますが、それはあなたに最適なものをテストする次第です..

于 2013-08-06T16:40:57.857 に答える
0

nav-controller スタックのビューをいじるのではなく、FullScreenViewController (UIViewController から) を用意して、それをアプリのルートにしないでください。次に、その上に NavController (およびそのスタック) を追加します。時が来たら、NavController 全体をフェードして、FullScreenViewController を公開します。

(このアイデアを逆さまにすることもできます -- 次のようなものです (ブラウザに入力すると、構文エラーがたくさん!):

UIViewController *vc = // ... FullScreenViewController
vc.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
[navController presentViewController: vc animated: YES completion: nil];

注: childViewControllers を使用して、フルスクリーン バージョンと非フル スクリーン バージョンの両方になる VC を含む抽象クラスを作成し、必要に応じてそのビューを盗むこともできます。

于 2013-08-07T02:44:35.603 に答える