一种方法是使用 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
自己一个:IBOutlet
NSBox
@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
添加一个:IBAction
SODWindowController
- (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
它们:SODWindowController
SODWindowController.h
@interface SODWindowController : NSWindowController
- (void)goForward;
- (void)goBack;
@end
然后在和MainMenu.xib
的文件菜单中添加两个菜单项。在声明两个:Forward
Back
SODAppDelegate.h
IBActions
- (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。