在以模态方式呈现视图控制器时,如何将视图控制器从搜索结果推入导航堆栈?

Ste*_*ser 13 uinavigationcontroller ios ios8 uisearchcontroller

我想重新创建iOS 7/8日历应用程序中显示的搜索UI.以模态方式呈现搜索UI不是问题.我使用UISearchController和模态呈现它就像UICatalog示例代码显示,它给了我一个很好的下拉动画.尝试从结果视图控制器推送视图控制器时出现问题.它没有包裹在导航控制器中,所以我无法推动它.如果我将它包装在导航控制器中,那么当我呈现时,我没有得到默认的下拉动画UISearchController.有任何想法吗?

编辑:我通过将结果视图控制器包装在导航控制器中来推动它.但是,在将新VC推入堆栈后,搜索栏仍然存在.

编辑(2):Apple的DTS表示日历应用程序使用非标准方法从搜索结果中推送.相反,他们建议从搜索控制器中移除焦点,然后将焦点推向并返回pop.这类似于我想象的设置应用程序中的搜索方式.

小智 12

苹果在那里变得非常聪明,但它并不是一种推动,即使它看起来像一个.

他们使用自定义转换(类似于导航控制器所做的)在嵌入导航控制器的视图控制器中滑动.

您可以通过缓慢地向后滑动该详细视图并让上一个视图开始出现来发现差异.请注意顶部导航如何与细节一起滑动到右侧,而不是其栏位按钮和标题过渡到位?

更新:

您看到的问题是搜索控制器出现在导航控制器上方.正如您所发现的,即使您将视图控制器推到导航控制器的堆栈上,导航栏仍然位于搜索控制器的演示文稿下方,因此搜索栏会遮挡任何(推送视图控制器)导航栏.

如果要在搜索控制器顶部显示结果而不解除它,则需要提供自己的模态导航视图控制器.

不幸的是,没有过渡风格可以让你以与内置推动画动作相同的方式呈现你的导航控制器.

我可以看到,有三种效果需要重复.

  1. 当出现的视图出现时,底层内容会变暗.
  2. 呈现的视图有阴影.
  3. 底层内容的导航完全在屏幕外动画,但其内容部分动画.

我在交互式自定义模态转换中重现了一般效果. 它通常模仿日历的动画,但有一些差异(未显示),例如键盘(重新)出现太快.

提供的模态控制器是导航控制器.我连接了一个后退按钮和边缘滑动手势(以交互方式)将其关闭.

在此输入图像描述

以下是涉及的步骤:

  1. 在你的故事板,你会从改变Segue公司类型显示详细信息 ,以出示模态.

您可以将PresentationTransition设置为Default,因为它们需要在代码中被覆盖.

  1. 在Xcode中,将新的NavigationControllerDelegate文件添加到项目中.

    NavigationControllerDelegate.h:

    @interface NavigationControllerDelegate : NSObject <UINavigationControllerDelegate>
    
    Run Code Online (Sandbox Code Playgroud)

    NavigationControllerDelegate.m:

    @interface NavigationControllerDelegate () <UIViewControllerTransitioningDelegate>
    @property (nonatomic, weak) IBOutlet UINavigationController *navigationController;
    @property (nonatomic, strong) UIPercentDrivenInteractiveTransition* interactionController;
    @end
    
    - (void)awakeFromNib
    {
        UIScreenEdgePanGestureRecognizer *panGestureRecognizer = [[UIScreenEdgePanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePan:)];
        panGestureRecognizer.edges = UIRectEdgeLeft;
    
        [self.navigationController.view addGestureRecognizer:panGestureRecognizer];
    }
    
    #pragma mark - Actions
    
    - (void)handlePan:(UIScreenEdgePanGestureRecognizer *)gestureRecognizer
    {
        UIView *view = self.navigationController.view;
    
        if (gestureRecognizer.state == UIGestureRecognizerStateBegan)
        {
            if (!self.interactionController)
            {
                self.interactionController = [UIPercentDrivenInteractiveTransition new];
                [self.navigationController dismissViewControllerAnimated:YES completion:nil];
            }
        }
        else if (gestureRecognizer.state == UIGestureRecognizerStateChanged)
        {
            CGFloat percent = [gestureRecognizer translationInView:view].x / CGRectGetWidth(view.bounds);
            [self.interactionController updateInteractiveTransition:percent];
        }
        else if (gestureRecognizer.state == UIGestureRecognizerStateEnded)
        {
            CGFloat percent = [gestureRecognizer translationInView:view].x / CGRectGetWidth(view.bounds);
            if (percent > 0.5 || [gestureRecognizer velocityInView:view].x > 50)
            {
                [self.interactionController finishInteractiveTransition];
            }
            else
            {
                [self.interactionController cancelInteractiveTransition];
            }
            self.interactionController = nil;
        }
    }
    
    #pragma mark - <UIViewControllerAnimatedTransitioning>
    
    - (id<UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)__unused presented presentingController:(UIViewController *)__unused presenting sourceController:(UIViewController *)__unused source
    {
        TransitionAnimator *animator = [TransitionAnimator new];
        animator.appearing = YES;
        return animator;
    }
    
    - (id<UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)__unused dismissed
    {
        TransitionAnimator *animator = [TransitionAnimator new];
        return animator;
    }
    
    - (id<UIViewControllerInteractiveTransitioning>)interactionControllerForPresentation:(id<UIViewControllerAnimatedTransitioning>)__unused animator
    {
        return nil;
    }
    
    - (id<UIViewControllerInteractiveTransitioning>)interactionControllerForDismissal:(id<UIViewControllerAnimatedTransitioning>)__unused animator
    {
    #pragma clang diagnostic push
    #pragma clang diagnostic ignored "-Wgnu-conditional-omitted-operand"
        return self.interactionController ?: nil;
    #pragma clang diagnostic pop
    }
    
    Run Code Online (Sandbox Code Playgroud)

    代表将为控制器提供其动画师,交互控制器,并管理屏幕边缘平移手势以消除模态演示.

  2. 在Storyboard中,将对象(黄色立方体)从对象库拖动到模态导航控制器.将它的类设置为我们的NavigationControllerDelegate,并将它delegatenavigationController插座连接到故事板的模态导航控制器.

  3. prepareForSegue搜索结果控制器中,您需要设置模态导航控制器的转换委托和模态演示样式.

    navigationController.transitioningDelegate = (id<UIViewControllerTransitioningDelegate>)navigationController.delegate;
    navigationController.modalPresentationStyle = UIModalPresentationCustom;
    
    Run Code Online (Sandbox Code Playgroud)

    模态演示文稿执行的自定义动画由过渡动画师处理.

  4. 在Xcode中,将新的TransitionAnimator文件添加到项目中.

    TransitionAnimator.h:

    @interface TransitionAnimator : NSObject <UIViewControllerAnimatedTransitioning>
    @property (nonatomic, assign, getter = isAppearing) BOOL appearing;
    
    Run Code Online (Sandbox Code Playgroud)

    TransitionAnimator.m:

    @implementation TransitionAnimator
    @synthesize appearing = _appearing;
    
    #pragma mark - <UIViewControllerAnimatedTransitioning>
    
    - (NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext
    {
        return 0.3;
    }
    
    - (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext
    {
        // Custom animation code goes here
    }
    
    Run Code Online (Sandbox Code Playgroud)

动画代码太长而无法在答案中提供,但它在我在GitHub上共享的示例项目中可用.

话虽如此,但现在的代码更像是一种有趣的练习.Apple已经有多年的时间来完善和支持他们的所有转换.如果您采用此自定义动画,您可能会发现动画与Apple不同的情况(例如可见键盘).您必须决定是否要花时间来改进代码以正确处理这些情况.


Mic*_*aur 7

我知道这个线程已经老了,但似乎有一个更简单的方法来获得所需的行为.

要实现的重要一点UISearchController是从源控制器呈现,源控制器是导航控制器内部的视图控制器.如果检查视图层次结构,您会发现搜索控制器与常规模式演示不同,不是作为窗口的直接子项呈现,而是作为导航控制器的子视图.

所以一般结构是

  • UINavigationController的
    • MyRootViewController
      • UISearchViewController(呈现伪"模态")
        • MyContentController

基本上你只需要从MyContentController到MyRootViewController,这样你就可以访问它的navigationController属性.在我tableView:didSelectRowAtIndexPath:的搜索内容控制器的方法中,我只需使用以下内容来访问我的根视图控制器.

UINavigationController *navigationController = nil;
if ([self.parentViewController isKindOfClass:[UISearchController class]]) {
    navigationController = self.parentViewController.presentingViewController.navigationController;
}
Run Code Online (Sandbox Code Playgroud)

从那里你可以轻松地将某些东西推到导航控制器上,动画正是你所期望的.