首先,这是解决方案的示例 GitHub 项目:click。我不确定您是想交换视图还是简单地将第二个视图推送到众所周知的堆栈中,所以我采用了推送/弹出方案。如果你想交换视图,你应该可以通过跳过堆栈存储来相当容易地做到这一点。
本质上,我们有我们的“主机”NSViewController,其中包含一个容器视图(CV)。该主机实际上并没有手动管理 CV 目前显示的视图控制器。这样做的方式是通过一种嵌套的视图控制器,然后管理您将要显示/隐藏/推送/弹出/交换/等的所有其他视图控制器。 (注意:您可能可以稍微移除分层,但在 iOS 术语中,我将情节提要屏幕截图中的“子视图控制器管理器”视为UINavigationController)。
我们还利用了一些自定义转场/转场动画师,以便能够在情节提要中完成更多工作。
您只需要告诉内容视图管理器视图控制器以这样的方式操作其子视图,即保留您想要“返回”到的旧视图(在这种情况下,使用NSMutableArray)等新视图具有正确的frame 或正确设置了它们的约束。
这是情节提要的屏幕截图: 您在情节提要上看到的自定义类型(如下所示 -> { } )的每个 segue 都是示例项目中的 SegueBetweenEmbedded 类型。按下按钮执行转场,标记为“Pop”的按钮在NSViewController 上执行dismissController:(这在情节提要中完成)。
这是一些代码(其中有很多,所以我建议改为查看示例项目):
ViewController.h
#import <Cocoa/Cocoa.h>
#import "ContentManagerViewController.h"
@class ContentManagerViewController;
@protocol ContentManagerViewControllerHolder <NSObject>
-(ContentManagerViewController*)retreiveContentManagerController;
@end
@interface ViewController : NSViewController <ContentManagerViewControllerHolder>
@end
ViewController.m
#import "ViewController.h"
#import "ContentManagerViewController.h"
#import "BackForwardViewController.h"
@interface ViewController ()
@property ContentManagerViewController *vcController;
-(IBAction)pushViewController:(id)sender;
-(IBAction)popViewController:(id)sender;
-(IBAction)popToRootViewController:(id)sender;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
}
-(void)prepareForSegue:(NSStoryboardSegue *)segue sender:(id)sender {
if ([[segue destinationController] class] == [ContentManagerViewController class]) {
self.vcController = segue.destinationController;
}
}
-(ContentManagerViewController*)retreiveContentManagerController {
return self.vcController;
}
-(IBAction)pushViewController:(id)sender {
// note: this works, but then pop is broken via dismissController: since it wasn't done with a segue.
// Better way is to rig up a manual segue and execute the segue.
//BackForwardViewController *viewController = [[NSStoryboard storyboardWithName:@"Main" bundle:nil] instantiateControllerWithIdentifier:@"BackForwardStoryboardID"];
//[self.vcController push:viewController];
[self performSegueWithIdentifier:@"CustomSegueToBackForward" sender:self];
}
-(IBAction)popViewController:(id)sender {
[self.vcController pop];
}
-(IBAction)popToRootViewController:(id)sender {
[self.vcController popToRoot];
}
@end
SegueBetweenEmbedded.h
#import <Cocoa/Cocoa.h>
@interface SegueBetweenEmbedded : NSStoryboardSegue
@end
SegueBetweenEmbedded.m(对嵌套类不抱歉)
#import "SegueBetweenEmbedded.h"
#import "ContentManagerViewController.h"
#import "ViewController.h"
@interface SegueAnimator : NSObject <NSViewControllerPresentationAnimator>
- (void)animatePresentationOfViewController:(NSViewController *)viewController fromViewController:(NSViewController *)fromViewController;
- (void)animateDismissalOfViewController:(NSViewController *)viewController fromViewController:(NSViewController *)fromViewController;
@end
@implementation SegueAnimator
- (void)animatePresentationOfViewController:(NSViewController *)viewController fromViewController:(NSViewController *)fromViewController {
NSViewController *parent = [fromViewController parentViewController];
if (parent && [parent class] == [ContentManagerViewController class]) {
ContentManagerViewController *manager = (ContentManagerViewController*)parent;
[manager push:viewController];
}
else if ([fromViewController conformsToProtocol:@protocol(ContentManagerViewControllerHolder)]) {
id<ContentManagerViewControllerHolder> holder = (id<ContentManagerViewControllerHolder>)fromViewController;
[[holder retreiveContentManagerController] push:viewController];
}
}
- (void)animateDismissalOfViewController:(NSViewController *)viewController fromViewController:(NSViewController *)fromViewController {
NSViewController *parent = [viewController parentViewController];
if ([parent class] == [ContentManagerViewController class]) {
ContentManagerViewController *manager = (ContentManagerViewController*)parent;
[manager pop];
}
}
@end
@implementation SegueBetweenEmbedded
- (void)perform {
SegueAnimator *animator = [[SegueAnimator alloc] init];
[self.sourceController presentViewController:self.destinationController
animator:(id<NSViewControllerPresentationAnimator>)animator];
}
@end
ContentManagerViewController.h
#import <Cocoa/Cocoa.h>
@interface ContentManagerViewController : NSViewController
-(void)push:(NSViewController*)viewController;
-(void)pop;
-(void)popToRoot;
@end
ContentManagerViewController.m
#import "ContentManagerViewController.h"
#import "BackForwardViewController.h"
@interface ContentManagerViewController ()
@property (weak) IBOutlet NSView *subViewControllerManager;
@property NSViewController *currentViewController;
@property NSMutableArray<NSViewController*> *viewControllerStack;
@end
@implementation ContentManagerViewController
-(instancetype)init {
self = [super init];
self.viewControllerStack = [NSMutableArray array];
return self;
}
-(instancetype)initWithCoder:(NSCoder *)coder {
self = [super initWithCoder:coder];
self.viewControllerStack = [NSMutableArray array];
return self;
}
-(instancetype)initWithNibName:(NSNibName)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
self.viewControllerStack = [NSMutableArray array];
return self;
}
- (void)viewDidLoad {
[super viewDidLoad];
}
-(void)showViewController:(NSViewController*)viewController {
[self addChildViewController:viewController];
viewController.view.frame = self.currentViewController.view.frame;
[self.view addSubview:viewController.view];
self.currentViewController = viewController;
}
-(void)removeCurrentViewControllerFromView {
[self.currentViewController.view removeFromSuperview];
[self.currentViewController removeFromParentViewController];
}
-(void)push:(NSViewController*)viewController {
[self removeCurrentViewControllerFromView];
[self.viewControllerStack addObject:viewController];
[self showViewController:viewController];
}
-(void)pop {
if (self.viewControllerStack.count > 1) {
[self removeCurrentViewControllerFromView];
[self.viewControllerStack removeLastObject];
NSViewController *viewController = [self.viewControllerStack lastObject];
[self showViewController:viewController];
}
}
-(void)popToRoot {
while (self.viewControllerStack.count > 1) {
[self pop];
}
}
-(void)prepareForSegue:(NSStoryboardSegue *)segue sender:(id)sender {
// this will be called on the initial embed to set up the first view controller
self.currentViewController = segue.destinationController;
[self.viewControllerStack addObject:segue.destinationController];
}
@end
BackForwardViewController.h
#import <Cocoa/Cocoa.h>
@interface BackForwardViewController : NSViewController
@end
BackForwardViewController.m
#import "BackForwardViewController.h"
@interface BackForwardViewController ()
@end
@implementation BackForwardViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do view setup here.
}
@end