以模态方式呈现时,如何将视图控制器从搜索结果推送到导航堆栈?
How do I push a view controller onto the nav stack from search results when presenting them modally?
我想重新创建 iOS 7/8 日历应用程序中显示的搜索 UI。以模态方式显示搜索 UI 不是问题。我使用 UISearchController
并以模式方式呈现它,就像 UICatalog 示例代码显示的那样,它给了我一个很好的下拉动画。尝试从结果视图控制器推送视图控制器时出现问题。它没有包含在导航控制器中,所以我无法推动它。如果我确实将它包装在导航控制器中,那么当我显示 UISearchController
时,我不会获得默认的下拉动画。有什么想法吗?
编辑:
我通过将我的结果视图控制器包装在导航控制器中来推动它。然而,在将新 VC 推入堆栈后,搜索栏仍然存在。
编辑(2):
Apple 的 DTS 表示,日历应用程序使用非标准方法从搜索结果中推送。相反,他们建议从搜索控制器中移除焦点,然后将焦点推送并返回到 pop。这类似于我想象中的设置应用程序中的搜索方式。
当您呈现 viewController 时,navigationController 将变得不可用。所以你必须先关闭你的模态,然后再推另一个 viewController.
UISearchController 必须是 UINavigationController 的 rootViewController,然后您将导航控制器呈现为模态。
Apple 在这方面做得非常聪明,但这并不是推动,尽管它看起来像一个。
他们使用自定义转换(类似于导航控制器所做的)在嵌入在导航控制器中的视图控制器中滑动。
您可以通过慢慢 edge-swiping 返回详细视图并让之前的视图开始出现来发现差异。请注意顶部导航如何与详细信息一起向右滑动,而不是其栏按钮和标题过渡 in-place?
更新:
您看到的问题是搜索控制器显示在导航控制器上方。正如您所发现的,即使您将视图控制器推送到导航控制器的堆栈上,导航栏仍位于搜索控制器的显示下方,因此搜索栏会遮挡任何(推送的视图控制器的)导航栏。
如果您想在搜索控制器顶部显示结果而不关闭它,您需要展示您自己的模态导航视图控制器。
不幸的是,没有过渡样式可以让您以与 built-in 推送动画行为相同的方式呈现导航控制器。
如我所见,需要复制三个效果。
- 随着呈现的视图出现,基础内容变暗。
- 呈现的视图有阴影。
- 底层内容的导航完全动画化 off-screen,但其内容部分动画化。
I've reproduced the general effect within an interactive custom modal transition. 大体模仿日历的动画,但也有一些差异(未显示),例如键盘(重新)出现得太快。
显示的模态控制器是一个导航控制器。我连接了一个后退按钮和边缘滑动手势以(交互地)关闭它。
以下是涉及的步骤:
- 在您的 Storyboard 中,您可以将 Segue 类型从 Show Detail 更改为 Present Modally.
您可以将 Presentation 和 Transition 设置为默认值,因为它们需要在代码中被覆盖。
在 Xcode 中,将新的 NavigationControllerDelegate 文件添加到您的项目。
NavigationControllerDelegate.h:
@interface NavigationControllerDelegate : NSObject <UINavigationControllerDelegate>
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
}
委托将为控制器提供其动画器、交互控制器,并管理屏幕边缘平移手势以关闭模态呈现。
在 Storyboard 中,将 Object(黄色立方体)从 object 库拖到模态导航控制器。将其 class 设置为我们的 NavigationControllerDelegate
,并将其 delegate
和 navigationController
出口连接到故事板的模态导航控制器。
在搜索结果控制器的 prepareForSegue
中,您需要设置模态导航控制器的转换委托和模态呈现样式。
navigationController.transitioningDelegate = (id<UIViewControllerTransitioningDelegate>)navigationController.delegate;
navigationController.modalPresentationStyle = UIModalPresentationCustom;
模式呈现执行的自定义动画由过渡动画师处理。
在 Xcode 中,将新的 TransitionAnimator 文件添加到您的项目中。
TransitionAnimator.h:
@interface TransitionAnimator : NSObject <UIViewControllerAnimatedTransitioning>
@property (nonatomic, assign, getter = isAppearing) BOOL appearing;
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
}
动画代码太长,无法在答案中提供,但在 a sample project which I've shared on GitHub 中可用。
话虽如此,就目前而言,代码更像是一个有趣的练习。 Apple 多年来一直在完善和支持他们的所有过渡。如果您采用此自定义动画,您可能会发现动画无法执行 Apple 所执行的操作的情况(例如可见键盘)。您必须决定是否要花时间改进代码以正确处理这些情况。
EDIT:不依赖于 UIWindow
的替代解决方案。我觉得效果和日历app很像。
@interface SearchResultsController () <UINavigationControllerDelegate>
@end
@implementation SearchResultsController
- (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// this will be the UINavigationController that provides the push animation.
// its rootViewController is a placeholder that exists so we can actually push and pop
UIViewController* rootVC = [UIViewController new]; // this is the placeholder
rootVC.view.backgroundColor = [UIColor clearColor];
UINavigationController* nc = [[UINavigationController alloc] initWithRootViewController: rootVC];
nc.modalPresentationStyle = UIModalPresentationCustom;
nc.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
[UIView transitionWithView: self.view.window
duration: 0.25
options: UIViewAnimationOptionTransitionCrossDissolve | UIViewAnimationOptionAllowAnimatedContent
animations: ^{
[self.parentViewController presentViewController: nc animated: NO completion: ^{
UIViewController* resultDetailViewController = [UIViewController alloc];
resultDetailViewController.title = @"Result Detail";
resultDetailViewController.view.backgroundColor = [UIColor whiteColor];
[nc pushViewController: resultDetailViewController animated: YES];
}];
}
completion:^(BOOL finished) {
nc.delegate = self;
}];
}
- (void) navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated
{
// pop to root? then dismiss our window.
if ( navigationController.viewControllers[0] == viewController )
{
[UIView transitionWithView: self.view.window
duration: [CATransaction animationDuration]
options: UIViewAnimationOptionTransitionCrossDissolve | UIViewAnimationOptionAllowAnimatedContent
animations: ^{
[self.parentViewController dismissViewControllerAnimated: YES completion: nil];
}
completion: nil];
}
}
@end
原始解决方案:
这是我的解决方案。我开始使用您在 UICatalog 示例中发现的相同技术来显示搜索控制器:
- (IBAction)search:(id)sender
{
SearchResultsController* searchResultsController = [self.storyboard instantiateViewControllerWithIdentifier: @"SearchResultsViewController"];
self.searchController = [[UISearchController alloc] initWithSearchResultsController:searchResultsController];
self.searchController.hidesNavigationBarDuringPresentation = NO;
[self presentViewController:self.searchController animated:YES completion: nil];
}
在我的示例中,SearchResultsController 是派生的 UITableViewController class。当点击搜索结果时,它会创建一个带有根 UINavigationController 的新 UIWindow 并将结果详细信息视图控制器推送到它。它监视弹出到根的 UINavigationController,以便它可以关闭特殊的 UIWindow。
现在,UIWindow 并不是严格要求的。我使用它是因为它有助于在 push/pop 转换期间保持 SearchViewController 可见。相反,您可以只显示 UISearchController 中的 UINavigationController(并从 navigationController:didShowViewController: 委托方法中将其关闭)。但是模态呈现的视图控制器默认情况下呈现在不透明的视图上,隐藏了下面的内容。您可以通过编写将作为 UINavigationController 的 transitioningDelegate 应用的自定义转换来解决此问题。
@interface SearchResultsController () <UINavigationControllerDelegate>
@end
@implementation SearchResultsController
{
UIWindow* _overlayWindow;
}
- (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// this will be the UINavigationController that provides the push animation.
// its rootViewController is a placeholder that exists so we can actually push and pop
UINavigationController* nc = [[UINavigationController alloc] initWithRootViewController: [UIViewController new]];
// the overlay window
_overlayWindow = [[UIWindow alloc] initWithFrame: self.view.window.frame];
_overlayWindow.rootViewController = nc;
_overlayWindow.windowLevel = self.view.window.windowLevel+1; // appear over us
_overlayWindow.backgroundColor = [UIColor clearColor];
[_overlayWindow makeKeyAndVisible];
// get this into the next run loop cycle:
dispatch_async(dispatch_get_main_queue(), ^{
UIViewController* resultDetailViewController = [UIViewController alloc];
resultDetailViewController.title = @"Result Detail";
resultDetailViewController.view.backgroundColor = [UIColor whiteColor];
[nc pushViewController: resultDetailViewController animated: YES];
// start looking for popping-to-root:
nc.delegate = self;
});
}
- (void) navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated
{
// pop to root? then dismiss our window.
if ( navigationController.viewControllers[0] == viewController )
{
[_overlayWindow resignKeyWindow];
_overlayWindow = nil;
}
}
@end
我知道这个帖子很旧,但似乎有一种更简单的方法来获得所需的行为。
要认识到的重要一点是 UISearchController
是从源控制器呈现的,它是导航控制器内的视图控制器。如果您检查视图层次结构,您会发现搜索控制器与常规模式呈现不同,它不是作为 window 的直接子视图呈现,而是作为导航控制器的子视图呈现。
所以大体结构是
- UINavigationController
- MyRootViewController
- UISearchViewController(呈现伪"modally")
- MyContentController
基本上你只需要从 MyContentController 到 MyRootViewController,这样你就可以访问它的 navigationController
属性。在我的搜索内容控制器的 tableView:didSelectRowAtIndexPath:
方法中,我只需使用以下内容来访问我的根视图控制器。
UINavigationController *navigationController = nil;
if ([self.parentViewController isKindOfClass:[UISearchController class]]) {
navigationController = self.parentViewController.presentingViewController.navigationController;
}
从那里您可以轻松地将某些内容推送到导航控制器上,动画正是您所期望的。
我想重新创建 iOS 7/8 日历应用程序中显示的搜索 UI。以模态方式显示搜索 UI 不是问题。我使用 UISearchController
并以模式方式呈现它,就像 UICatalog 示例代码显示的那样,它给了我一个很好的下拉动画。尝试从结果视图控制器推送视图控制器时出现问题。它没有包含在导航控制器中,所以我无法推动它。如果我确实将它包装在导航控制器中,那么当我显示 UISearchController
时,我不会获得默认的下拉动画。有什么想法吗?
编辑: 我通过将我的结果视图控制器包装在导航控制器中来推动它。然而,在将新 VC 推入堆栈后,搜索栏仍然存在。
编辑(2): Apple 的 DTS 表示,日历应用程序使用非标准方法从搜索结果中推送。相反,他们建议从搜索控制器中移除焦点,然后将焦点推送并返回到 pop。这类似于我想象中的设置应用程序中的搜索方式。
当您呈现 viewController 时,navigationController 将变得不可用。所以你必须先关闭你的模态,然后再推另一个 viewController.
UISearchController 必须是 UINavigationController 的 rootViewController,然后您将导航控制器呈现为模态。
Apple 在这方面做得非常聪明,但这并不是推动,尽管它看起来像一个。
他们使用自定义转换(类似于导航控制器所做的)在嵌入在导航控制器中的视图控制器中滑动。
您可以通过慢慢 edge-swiping 返回详细视图并让之前的视图开始出现来发现差异。请注意顶部导航如何与详细信息一起向右滑动,而不是其栏按钮和标题过渡 in-place?
更新:
您看到的问题是搜索控制器显示在导航控制器上方。正如您所发现的,即使您将视图控制器推送到导航控制器的堆栈上,导航栏仍位于搜索控制器的显示下方,因此搜索栏会遮挡任何(推送的视图控制器的)导航栏。
如果您想在搜索控制器顶部显示结果而不关闭它,您需要展示您自己的模态导航视图控制器。
不幸的是,没有过渡样式可以让您以与 built-in 推送动画行为相同的方式呈现导航控制器。
如我所见,需要复制三个效果。
- 随着呈现的视图出现,基础内容变暗。
- 呈现的视图有阴影。
- 底层内容的导航完全动画化 off-screen,但其内容部分动画化。
I've reproduced the general effect within an interactive custom modal transition. 大体模仿日历的动画,但也有一些差异(未显示),例如键盘(重新)出现得太快。
显示的模态控制器是一个导航控制器。我连接了一个后退按钮和边缘滑动手势以(交互地)关闭它。
以下是涉及的步骤:
- 在您的 Storyboard 中,您可以将 Segue 类型从 Show Detail 更改为 Present Modally.
您可以将 Presentation 和 Transition 设置为默认值,因为它们需要在代码中被覆盖。
在 Xcode 中,将新的 NavigationControllerDelegate 文件添加到您的项目。
NavigationControllerDelegate.h:
@interface NavigationControllerDelegate : NSObject <UINavigationControllerDelegate>
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 }
委托将为控制器提供其动画器、交互控制器,并管理屏幕边缘平移手势以关闭模态呈现。
在 Storyboard 中,将 Object(黄色立方体)从 object 库拖到模态导航控制器。将其 class 设置为我们的
NavigationControllerDelegate
,并将其delegate
和navigationController
出口连接到故事板的模态导航控制器。在搜索结果控制器的
prepareForSegue
中,您需要设置模态导航控制器的转换委托和模态呈现样式。navigationController.transitioningDelegate = (id<UIViewControllerTransitioningDelegate>)navigationController.delegate; navigationController.modalPresentationStyle = UIModalPresentationCustom;
模式呈现执行的自定义动画由过渡动画师处理。
在 Xcode 中,将新的 TransitionAnimator 文件添加到您的项目中。
TransitionAnimator.h:
@interface TransitionAnimator : NSObject <UIViewControllerAnimatedTransitioning> @property (nonatomic, assign, getter = isAppearing) BOOL appearing;
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 }
动画代码太长,无法在答案中提供,但在 a sample project which I've shared on GitHub 中可用。
话虽如此,就目前而言,代码更像是一个有趣的练习。 Apple 多年来一直在完善和支持他们的所有过渡。如果您采用此自定义动画,您可能会发现动画无法执行 Apple 所执行的操作的情况(例如可见键盘)。您必须决定是否要花时间改进代码以正确处理这些情况。
EDIT:不依赖于 UIWindow
的替代解决方案。我觉得效果和日历app很像。
@interface SearchResultsController () <UINavigationControllerDelegate>
@end
@implementation SearchResultsController
- (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// this will be the UINavigationController that provides the push animation.
// its rootViewController is a placeholder that exists so we can actually push and pop
UIViewController* rootVC = [UIViewController new]; // this is the placeholder
rootVC.view.backgroundColor = [UIColor clearColor];
UINavigationController* nc = [[UINavigationController alloc] initWithRootViewController: rootVC];
nc.modalPresentationStyle = UIModalPresentationCustom;
nc.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
[UIView transitionWithView: self.view.window
duration: 0.25
options: UIViewAnimationOptionTransitionCrossDissolve | UIViewAnimationOptionAllowAnimatedContent
animations: ^{
[self.parentViewController presentViewController: nc animated: NO completion: ^{
UIViewController* resultDetailViewController = [UIViewController alloc];
resultDetailViewController.title = @"Result Detail";
resultDetailViewController.view.backgroundColor = [UIColor whiteColor];
[nc pushViewController: resultDetailViewController animated: YES];
}];
}
completion:^(BOOL finished) {
nc.delegate = self;
}];
}
- (void) navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated
{
// pop to root? then dismiss our window.
if ( navigationController.viewControllers[0] == viewController )
{
[UIView transitionWithView: self.view.window
duration: [CATransaction animationDuration]
options: UIViewAnimationOptionTransitionCrossDissolve | UIViewAnimationOptionAllowAnimatedContent
animations: ^{
[self.parentViewController dismissViewControllerAnimated: YES completion: nil];
}
completion: nil];
}
}
@end
原始解决方案:
这是我的解决方案。我开始使用您在 UICatalog 示例中发现的相同技术来显示搜索控制器:
- (IBAction)search:(id)sender
{
SearchResultsController* searchResultsController = [self.storyboard instantiateViewControllerWithIdentifier: @"SearchResultsViewController"];
self.searchController = [[UISearchController alloc] initWithSearchResultsController:searchResultsController];
self.searchController.hidesNavigationBarDuringPresentation = NO;
[self presentViewController:self.searchController animated:YES completion: nil];
}
在我的示例中,SearchResultsController 是派生的 UITableViewController class。当点击搜索结果时,它会创建一个带有根 UINavigationController 的新 UIWindow 并将结果详细信息视图控制器推送到它。它监视弹出到根的 UINavigationController,以便它可以关闭特殊的 UIWindow。
现在,UIWindow 并不是严格要求的。我使用它是因为它有助于在 push/pop 转换期间保持 SearchViewController 可见。相反,您可以只显示 UISearchController 中的 UINavigationController(并从 navigationController:didShowViewController: 委托方法中将其关闭)。但是模态呈现的视图控制器默认情况下呈现在不透明的视图上,隐藏了下面的内容。您可以通过编写将作为 UINavigationController 的 transitioningDelegate 应用的自定义转换来解决此问题。
@interface SearchResultsController () <UINavigationControllerDelegate>
@end
@implementation SearchResultsController
{
UIWindow* _overlayWindow;
}
- (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// this will be the UINavigationController that provides the push animation.
// its rootViewController is a placeholder that exists so we can actually push and pop
UINavigationController* nc = [[UINavigationController alloc] initWithRootViewController: [UIViewController new]];
// the overlay window
_overlayWindow = [[UIWindow alloc] initWithFrame: self.view.window.frame];
_overlayWindow.rootViewController = nc;
_overlayWindow.windowLevel = self.view.window.windowLevel+1; // appear over us
_overlayWindow.backgroundColor = [UIColor clearColor];
[_overlayWindow makeKeyAndVisible];
// get this into the next run loop cycle:
dispatch_async(dispatch_get_main_queue(), ^{
UIViewController* resultDetailViewController = [UIViewController alloc];
resultDetailViewController.title = @"Result Detail";
resultDetailViewController.view.backgroundColor = [UIColor whiteColor];
[nc pushViewController: resultDetailViewController animated: YES];
// start looking for popping-to-root:
nc.delegate = self;
});
}
- (void) navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated
{
// pop to root? then dismiss our window.
if ( navigationController.viewControllers[0] == viewController )
{
[_overlayWindow resignKeyWindow];
_overlayWindow = nil;
}
}
@end
我知道这个帖子很旧,但似乎有一种更简单的方法来获得所需的行为。
要认识到的重要一点是 UISearchController
是从源控制器呈现的,它是导航控制器内的视图控制器。如果您检查视图层次结构,您会发现搜索控制器与常规模式呈现不同,它不是作为 window 的直接子视图呈现,而是作为导航控制器的子视图呈现。
所以大体结构是
- UINavigationController
- MyRootViewController
- UISearchViewController(呈现伪"modally")
- MyContentController
- UISearchViewController(呈现伪"modally")
- MyRootViewController
基本上你只需要从 MyContentController 到 MyRootViewController,这样你就可以访问它的 navigationController
属性。在我的搜索内容控制器的 tableView:didSelectRowAtIndexPath:
方法中,我只需使用以下内容来访问我的根视图控制器。
UINavigationController *navigationController = nil;
if ([self.parentViewController isKindOfClass:[UISearchController class]]) {
navigationController = self.parentViewController.presentingViewController.navigationController;
}
从那里您可以轻松地将某些内容推送到导航控制器上,动画正是您所期望的。