当一个新的视图控制器被推送到导航控制器时,将一个子视图保持在屏幕上的同一位置?

Posted

技术标签:

【中文标题】当一个新的视图控制器被推送到导航控制器时,将一个子视图保持在屏幕上的同一位置?【英文标题】:Keep one subview in the same place on the screen while a new viewcontroller is pushed to the navigation controller? 【发布时间】:2012-05-12 20:43:36 【问题描述】:

我有一个带有许多图像按钮的“主屏幕”。点击其中一个按钮后,一个详细视图控制器被推到导航控制器上。但是,从视觉上看,我希望按钮中的图像保持在屏幕上的同一位置,而新的视图控制器会过渡到它后面的位置。

模态控制器的一个例子是将照片添加到电子邮件或消息中。您在图库中选择的照片会变大(好像它正在向您移动),而撰写消息视图在后台滑动到位,然后照片缩小到消息视图上的适当位置。

我想到的几个选项:

点击按钮后,将图像放置在屏幕上与 UIWindow 的子视图相同的位置。做动画/过渡,然后使图像成为新视图控制器视图的子视图。

与上述相同,但具有清晰背景的模态视图控制器

让 UINavigationController 成为子视图,并使用 UINavigationController 的父视图执行上述操作

上面的所有选项都是相似的,但我很好奇是否有更好的方法来做到这一点,以及代码的位置。我应该继承 UINavigationController 并将转换代码放在那里吗?或者可能是 AppDelegate(不过,这似乎是错误的)?

【问题讨论】:

【参考方案1】:

我会向导航控制器或窗口添加子视图,但需要一些视图层次结构。

您需要为masterdetail 视图控制器提供一种方法来引用imageView 一个ivar 应该没问题。

// PSMasterViewController.m
@interface PSMasterViewController ()

@property (nonatomic, strong) UIImageView *imageView;

@end

@implementation PSMasterViewController

@synthesize imageView = _imageView;

// ...
@end

我已将其放入实现中,因为我不想将其公开给主视图控制器,但您的需求可能会有所不同。

detailViewController 也一样

// PSDetailViewController.h
@interface PSMasterViewController : UIViewController

@property (nonatomic, strong) UIImageView *imageView;

@end

// PSDetailViewController.m
@implementation PSDetailViewController

@synthesize imageView = _imageView;

// ...
@end

现在假设 imageView 已经创建并呈现在主视图控制器中,我们需要在即将推送时实现一些视图体操

- (void)methodThatPushesTheNextViewController;

    CGRect destinationFrame = [self.view convertRect:self.imageView.frame toView:self.navigationController.view];
    [self.imageView removeFromSuperview];
    [self.navigationController.view addSubview:self.imageView];
    self.imageView.frame = destinationFrame;

    PSDetailViewController *detailViewController = [[PSDetailViewController alloc] init];
    detailViewController.imageView = self.imageView;
    self.imageView = nil; // The detailViewController should own this now
    [self.navigationController pushViewController:detailViewController animated:YES];

现在在细节视图控制器中,我们需要做与我们刚刚做的相反的事情,viewDidAppear: 是一个好地方,尤其是你提到你可能想要移动和缩放

- (void)viewDidAppear:(BOOL)animated;

    [super viewDidAppear:animated];

    CGRect startFrame = [self.view convertRect:self.imageView.frame fromView:self.navigationController.view];
    [self.imageView removeFromSuperview];
    [self.view addSubview:self.imageView];
    self.imageView.frame = startFrame;

    [UIView animateWithDuration:0.25f
                     animations:^
                         self.imageView.frame = [self someNewFrame];
                     ];

【讨论】:

我做了一些小改动,但是效果很好……谢谢! 我一直想做这件事。 @Paul.s 技术不再适用于 ios7+。所以对于 7 岁以上的人,我们需要使用自定义转换。在 animateTransition 方法中,我们通常将 toViewController.view 作为子视图添加到 containerView,然后将 imageView 作为子视图添加到 containerView。【参考方案2】:

选项 1

你也可以在没有 ModalViewController 的情况下做到这一点。首先要记住,您必须删除图像并再次将其移至 pushViewController,因为如果发生视图转换,则会再次添加新的 viewController,并且您之前的视图会消失。 因此,通过向被推送的 viewController 发送 NSNotification 并创建具有相同帧的视图来执行此操作。还记得在推送新的 ViewController 之前从 superview 中删除视图。

选项2

如果你所有需要相关视图的viewcontrollers都继承自一个ViewController子类,你可以调用[super init]方法创建视图并添加到viewDidLoad方法中。

【讨论】:

【参考方案3】:

您可以按照自己的设想在视图上进行“UIView Animation”,也可以使用 iCarousel 来获得更清晰的动画icarousel SourceCode

【讨论】:

以上是关于当一个新的视图控制器被推送到导航控制器时,将一个子视图保持在屏幕上的同一位置?的主要内容,如果未能解决你的问题,请参考以下文章

如何防止子视图重叠标签栏?

当 hidesBottomBarWhenPushed = YES 状态恢复时,导航控制器不隐藏标签栏

当 UISearchController 处于活动状态时呈现 UIAlertController

导航栏在推送到新视图控制器时会缩小

屏幕左侧的 UIButton(在 iOS 7 导航滑动区域中)未突出显示

转到应用程序中的第一个视图控制器