我如何做到这一点,如果我淡出我的导航栏,然后实际上隐藏它的程序,如果我撤消这没有时间问题?

长话短说,我有一个视图控制器,用户可以点击self.view (除了导航栏之外的任何地方),它将进入全屏模式,底部的控件淡出,导航和状态栏淡出。 类似于iBooks。

我可以简单地淡化导航栏的alpha,但是为了让用户能够在新获得的区域(导航栏现在已经淡出)中点击,并且让它做一些事情,我不得不做更多的事情,阿尔法,因为导航栏仍然在技术上占据领域。

所以我用[self.navigationController setNavigationBarHidden:YES animated:NO];隐藏导航栏[self.navigationController setNavigationBarHidden:YES animated:NO]; 。 在animation块结束后我必须这样做,否则它将在animation块中并作为块的一部分进行animation处理。 所以我使用dispatch_after在animation完成后(0.35秒延迟)完成。

然而,这导致如果用户在0.35秒的时间段内在任何时间点击animation并且等待完成的事件,那么引起另一个块开始时的瑕疵行为,即使对于另一个块仍然等待0.35秒完成。 它会导致一些不好的行为,并导致导航栏保持隐藏状态。 毛。

它发生的video: 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; } } 

我正在做的唯一的事情是改变我的自动布局约束的常量来说明导航栏和状态栏,取决于他们是否在那里。

我不确定如何考虑到双击可能会影响整个屏幕过程。 我怎么能这样做,如果他们在animation过程中点击,它只会取消animation,并按预期做他们想要的动作? 我能更好地做这个过程吗?

我想你可以做到这一点,而不用调整任何框架或约束使用这些原则:

1)使窗口的背景颜色与视图相同

2)在窗口中添加一个轻击手势识别器。 这允许在屏幕上的任何地方点击(除了状态栏的alpha不是0),导航栏是否可见。 这使您不必将导航栏设置为隐藏,这会导致您的视图resize。

3)使用hitTest:在tapper的action方法中检查用户是否敲击导航栏,如果敲击在那里,不要淡出。

4)在animation块中使用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]; } } 

其他的答案是有帮助的,但是你应该做的一件事可能不是将animation持续时间硬编码为0.35,而是尝试使用UINavigationControllerHideShowBarDuration 。 这将使您的应用程序更易于更改UIKit行为。

为什么不使用animationCompleted委托或块?

我这样做的方式是简单地创build一个BOOL标志,我打电话像isTransitioning ,这样一旦隐藏/ hideControls隐藏进程启动, hideControls方法立即返回,如果正在进行转换。 这样你就不会碰触触摸事件。 你直接停止不需要的故障,而不会在其他地方引起副作用( isTransitioning ,你需要在方法之外声明isTransitioning属性/ ivar):

 - (void)hideControls:(BOOL)hidden { //Check there isn't a hide/unhide already in progress: if(self.isTransitioning == YES) return; //if there wasn't already a transition in progress, set //isTransitioning to YES and off we go: self.isTransitioning = YES; 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; //Unset isTransitioning now we're finished: self.isTransitioning = NO; }); } else { [self.navigationController setNavigationBarHidden:NO animated:NO]; self.textToReadLabelPositionFromTopConstraint.constant = TEXT_LABEL_DISTANCE; //Unset isTransitioning now we're finished: self.isTransitioning = NO; } } 

我没有直接testing这个代码,但是你可以看到我在做什么,我敢肯定。

编辑#2

查看文档, hitTest:withEvent:只需在视图的所有子视图上调用pointTest:withEvent: 。 它清楚地表明,alpha级别小于0.01的视图被忽略。 我认为我们走在这条正确的道路上,只需要进一步探索。 我敢肯定,有一种方法可以通过alpha == 0.0f的视图传递给它下面的任何视图。 希望你(或者其他人)能够得到它。 如果我有时间,我会潜入一些代码,并尝试进一步帮助。

编辑#1:尝试重写pointInside:withEvent:

对不起,意识stream在这里回答。 通常我会自己testing一下,或者从产品应用程序粘贴代码,但是我现在太忙了。

我认为压倒一切的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:以便导航栏忽略触摸,而它是不可见的。 我没有testing,但应该是这样的:

 - (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]; } } 

如果testingalpha == 0.0f不是您想要决定忽略触摸的方式,您也可以添加自己的属性并以此方式执行。

 @property (nonatomic) BOOL ignoreTouches; 

在你的hitTest:withEvent:执行检查if (ignoreTouches)并返回nil。

要在为UINavigationBar设置animation时closures接收触摸事件,然后对其进行子类化并添加以下方法:

 -(BOOL) canBecomeFirstResponder{ NSLog(@"Decide if to allow the tap through"); if (self.hiding) return NO; return YES; } 

然后,您可以控制是否以及何时允许触摸响应。

请记住, UINavigationBarUIResponder一个子类,它允许你重写这个和许多其他的方法。 我也经常忘记查看inheritance链。

你可以使用animateWithDuration:animations:completion: 。 完成块在animation完成后执行( 来源 )。 它也有一个额外的好处; 如果你决定改变未来的animation时间,你将不必担心在两个地方改变时间。

 [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; } } }]; 

基于评论,您可以在animation开始之前禁用导航栏,并在完成块中重新启用,但这取决于您testing什么最适合您。

为什么没有一个FullScreenViewController(来自UIViewController)并将其作为你的应用程序的根,而不是在nav-controller堆栈中搞乱视图。 然后添加NavController(及其堆栈)。 当时间到了,只需淡化整个NavController,暴露您的FullScreenViewController。

(你也可以颠倒这个想法 – 像这样的东西(在浏览器中键入 – 很多语法错误!):

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

注意:你也可以使用childViewControllers来创build一个包含VC的抽象类,这个VC既可以是全屏版本,也可以是非全屏版本,然后根据需要窃取它的视图。