一种方法是使用 NSBox。 此演示的完整代码在 github 上。请参阅底部的链接。
入门
使用 Cocoa Application 模板在 Xcode 中启动一个新项目。使用适合您的前缀。我将 SOD 用于 StackOverflowDemo。
在做任何其他事情之前,对作为模板的一部分自动添加到项目中的文件进行一些小的更改:
MainMenu.xib: 删除 NSWindow。
- SODAppDelegate.h:删除行:
@property (assign) IBOutlet NSWindow *window;
为了简化您的示例,假设您希望在左侧有一个带有一列可单击项目(为简单起见,假设为按钮)的单个窗口。
SOD窗口控制器
NSWindowController首先向您的项目添加一个子类。请务必包含相应的XIB. 打开SODWindowController.xib,在窗口左侧添加四个按钮(我使用方形按钮)并添加一个NSBox,填充窗口的其余部分。在这一点上,你会有一些像这样的东西。

我将框的 contentView 设置为图层托管视图,其图层的背景颜色为[NSColor greenColor]. 我这样做是因为,否则你根本看不到盒子。该框已配置为仅用于交换视图进出。要像这样配置您的框,请在 Interface Builder 中选择 ,NSBox然后在 Attributes Inspector 中,将 设置Title Position为None并将 设置Border Type为None。
现在SODWindowController.m,将存根替换为initWithWindow:
- (id)initWithWindow:(NSWindow *)window
{
self = [super initWithWindow:window];
if (self) {
// Initialization code here.
}
return self;
}
使用不同的指定初始化程序:
- (id)init
{
self = [super initWithWindowNibName:@"SODWindowController"];
{
}
return self;
}
这将使您避免重新输入(并可能错误地输入 XIB 的名称。
SODWindowController此外,在该行之后添加一个类扩展
#import "SODWindowController.h"
在,给SODWindowController.m自己一个:IBOutletNSBox
@interface SODWindowController ()
@property (nonatomic, strong, readwrite) IBOutlet NSBox *box;
@end
在 Interface Builder 中,打开SODWindowController.xib并设置 box outlet 指向NSBox您添加的。
将 SODWindowController 添加到 SODAppDelegate
现在,添加SODWindowController到SODAppDelegate:将行添加到SODAppDelegate.m正下方#import "SODAppDelegate.h":
#import "SODWindowController.h"
@interface SODAppDelegate ()
@property (nonatomic, strong, readwrite) SODWindowController *windowController;
@end
现在我们可以将窗口设置为在以下位置创建-[NSApplication applicationDidFinishLaunching:]:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
self.windowController = [[SODWindowController alloc] init];
//notice that we don't have to do anything awkward like use initWithWindowNibName:
[self.windowController showWindow:nil];
}
此时,当我们运行应用程序时,我们将看到我们的窗口。
很好,但是四个不同按钮的不同视图和历史记录如何让用户返回?
SODViewControllers
为四个视图中的每一个创建一个 NSViewController 子类。我会打电话给我SODCoffeeViewController的SODTeaViewController,SODJavaViewController和SODMeViewController。
在它们中的每一个中,替换自动生成的指定初始化程序存根
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Initialization code here.
}
return self;
}
和
- (id)init
{
self = [super initWithNibName:@"SODCoffeeViewController" bundle:nil];
if (self) {
}
return self;
}
尽管当然要为每个替换适当的 NibName 参数。和以前一样,这将阻止您在与该 Nib 关联的文件之外的任何位置键入`@"MyNibName"。
将 SODViewControllers 添加到 SODWindowController
现在,将所有四个视图控制器导入SODWindowController:添加行
#import "SODCoffeeViewController.m"
#import "SODTeaViewController.m"
#import "SODJavaViewController.m"
#import "SODTeaViewController.m"
我们SODWindowController需要这些对象中的每一个。向我们的类扩展添加NSArray属性最简单的事情SODWindowController
@property (nonatomic, strong, readwrite) NSArray *viewControllers;
并在执行中-[SODWindowController init]添加行
- (id)init
{
self = [super initWithWindowNibName:@"SODWindowController"];
if (self) {
NSMutableArray *mutableViewControllers = [NSMutableArray array];
[mutableViewControllers addObject:[[SODCoffeeViewController alloc] init]];
[mutableViewControllers addObject:[[SODTeaViewController alloc] init]];
[mutableViewControllers addObject:[[SODJavaViewController alloc] init]];
[mutableViewControllers addObject:[[SODMeViewController alloc] init]];
self.viewControllers = [mutableViewControllers copy];
}
return self;
}
现在,我们为要呈现的四个视图中的每一个都有一个视图控制器,并且它们在一个数组中排序,就像它们是窗口上的四个按钮一样。更重要的是,它们的排序方式与我们四个按钮上的标签相同。...在我们设置他们的标签之后,就是这样。
在 Interface Builder 中打开SODWindowController.xib并将顶部按钮的标记设置为0,从顶部开始的第二个到1等等。
回到,在类扩展中SODWindowController.m添加一个:IBActionSODWindowController
- (IBAction)showPane:(id)sender;
在下面定义这个方法SODWindowController @implemention:
- (void)showPane:(NSButton *)sender
{
[self showPaneAtIndex:sender.tag];
}
就是这样!
...当然除了定义-[SODWindowController showPaneAtIndex:]. 但在此之前,将四个按钮的操作设置SODWindowController.xib为showPane:on File's Owner。
完成后,定义showPaneAtIndex:如下:
- (void)showPaneAtIndex:(NSInteger)index
{
NSViewController *viewController = (NSViewController *)self.viewController[(NSUInteger)index];
[self.box setContentView:viewController.view];
}
现在,每当按下任何按钮时,相应的视图控制器的视图都将设置为框的内容视图。
SODCoffeeViewController可能,我们希望窗口一出现就显示最顶层的视图控制器的视图( 's)。要做到这一点,只需将当前定义替换-windowDidLoad为
- (void)windowDidLoad
{
[super windowDidLoad];
[self showPaneAtIndex:0];
}
历史
历史实际上是最简单的部分。我们要做的是跟踪我们后面的一个或两个窗格(在浏览器中的后退按钮的意义上)和在我们前面的窗格(在前进按钮的意义上)。为此,我们将在SODWindowController类扩展中添加两个可变数组属性:
@property (nonatomic, strong, readwrite) NSMutableArray *panesBehind;
@property (nonatomic, strong, readwrite) NSMutableArray *panesBefore;
并将它们初始化为-init:
...
self.viewControllers = [mutableViewControllers copy];
self.panesBehind = [NSMutableArray array];
self.panesBefore = [NSMutableArray array];
}
return self;
}
现在,我们需要将我们访问的每个新窗格的索引推送到 上self.panesBehind,所以添加行
[self.panesBehind addObject:[NSNumber numberWithInteger:index]];
根据你的定义-showPaneAtIndex:。
这允许我们定义-goBack,-goForward和帮助器-canGoBack,-canGoForward如下:
- (BOOL)canGoBack
{
return self.panesBehind.count > 0;
}
- (void)goBack
{
if (self.canGoBack) {
NSNumber *presentPane = self.panesBehind.lastObject;
[self.panesBehind removeLastObject];
[self.panesBefore insertObject:presentPane atIndex:0];
NSNumber *previousPane = self.panesBehind.lastObject;
[self.panesBehind removeLastObject];
[self showPaneAtIndex:previousPane.integerValue];
}
}
- (BOOL)canGoForward
{
return self.panesBefore.count > 0;
}
- (void)goForward
{
if (self.canGoForward) {
NSNumber *nextPane = [self.panesBefore objectAtIndex:0];
[self.panesBefore removeObjectAtIndex:0];
[self showPaneAtIndex:nextPane.integerValue];
}
}
不过,这有一个小问题。如果我们从窗格 A 到 B 到 C 到 D 前进,那么self.panesBehind看起来像
A-->B-->C-->D
并且self.panesBefore是空的。但是如果我们返回两个窗格,self.panesBehind看起来像
A-->B
看起来self.panesBefore像
C-->D
如果此时我们现在转到窗格 E,self.panesBehind看起来像
A-->B-->E
正如我们所期望的那样,但self.panesBefore仍然看起来像
C-->D
这不是我们想要的。出于这个原因,我们添加了这一行
[self.panesBefore removeAllObjects];
到结束-showPane:。
要尝试这些方法,请继续在 in 中公开声明@interface它们:SODWindowControllerSODWindowController.h
@interface SODWindowController : NSWindowController
- (void)goForward;
- (void)goBack;
@end
然后在和MainMenu.xib的文件菜单中添加两个菜单项。在声明两个:ForwardBackSODAppDelegate.hIBActions
- (IBAction)forward:(id)sender;
- (IBAction)back:(id)sender;
并返回MainMenu.xib,将 Forward 菜单项的操作设置为forward:on First Responder,将 Back 菜单项的操作设置为back:on First Responder。最后,定义这两个方法-[SODAppDelegate forward:]并-[SODAppDelegate back:]调用相应的方法SODWindowController:
- (IBAction)forward:(id)sender
{
[self.windowController goForward];
}
- (IBAction)back:(id)sender
{
[self.windowController goBack];
}
要下载完整源代码,请访问https://github.com/natechan/ViewSwappingDemo。