2
  • 我的目标

制作一个只有一个窗口的应用程序。窗口的根视图将是一对带有 4 个按钮的图像。这 4 个按钮中的每一个都将显示不同的(主)视图。每个主视图将显示其他(子)视图,并且每个主视图和子视图应该能够“返回”到根视图。

  • 我做了什么(它基于苹果的 ViewController 项目)

    • 我用他们受人尊敬的 XIB 文件创建了 1NSWindowController和 4 。NSViewController

    • AppDelegate 分配/初始化一个NSWindowController对象,其 nib 包含窗口和根视图,然后showWindow在其上调用方法。

    • 我的子类NSWindowController有两个 ivars:一个NSView代表绑定在 IB 中的根视图,一个NSViewController代表当前视图控制器。
    • 我将 4 个按钮绑定到根据按钮标签更改视图的方法。

这是 Apple 的 ViewController 项目如何实现它http://pastie.org/private/zmqpzgnudgovagwigal8dq

尽管如此,这对我不起作用,它只是在根视图之上添加视图。

如您所见,我有点迷茫,请随时分享您的想法!

4

1 回答 1

9

一种方法是使用 NSBox。 此演示的完整代码在 github 上。请参阅底部的链接。

入门

使用 Cocoa Application 模板在 Xcode 中启动一个新项目。使用适合您的前缀。我将 SOD 用于 StackOverflowDemo。

在做任何其他事情之前,对作为模板的一部分自动添加到项目中的文件进行一些小的更改:

  • MainMenu.xib: 删除 NSWindow。
  • SODAppDelegate.h:删除行:@property (assign) IBOutlet NSWindow *window;

为了简化您的示例,假设您希望在左侧有一个带有一列可单击项目(为简单起见,假设为按钮)的单个窗口。

SOD窗口控制器

NSWindowController首先向您的项目添加一个子类。请务必包含相应的XIB. 打开SODWindowController.xib,在窗口左侧添加四个按钮(我使用方形按钮)并添加一个NSBox,填充窗口的其余部分。在这一点上,你会有一些像这样的东西。

第1步

我将框的 contentView 设置为图层托管视图,其图层的背景颜色为[NSColor greenColor]. 我这样做是因为,否则你根本看不到盒子。该框已配置为仅用于交换视图进出。要像这样配置您的框,请在 Interface Builder 中选择 ,NSBox然后在 Attributes Inspector 中,将 设置Title PositionNone并将 设置Border TypeNone

现在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

现在,添加SODWindowControllerSODAppDelegate:将行添加到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 子类。我会打电话给我SODCoffeeViewControllerSODTeaViewController,SODJavaViewControllerSODMeViewController

在它们中的每一个中,替换自动生成的指定初始化程序存根

- (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.xibshowPane: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

于 2012-10-16T05:15:07.257 回答