【问题标题】:How to initialize main window objects from custom class during program start?如何在程序启动期间从自定义类初始化主窗口对象?
【发布时间】:2015-04-27 02:09:16
【问题描述】:

我有一个带有几个弹出按钮的主窗口。我想清除它们,然后从自定义类中的方法加载列表。我已经让我的视图控制器工作了,我知道自定义类 (newRequest) 中的方法正在工作,因为我添加了一个 NSLog 命令来在方法执行时打印“Test”。在 AppDelegate 中,我通过以下方式调用该方法: [polyAppRequest 新请求];。 正如我所说,我知道该方法正在执行。为什么我不能从这个自定义类方法的弹出按钮中删除 allitems? 谢谢 基思

【问题讨论】:

    标签: objective-c cocoa


    【解决方案1】:

    我读到您应该使用 NSWindowController 来管理窗口。见这里:

    Windows and window controllers

    Adding views or windows to MainWindow

    如果你的窗口变得足够复杂,NSWindowController 可以使用各种 NSViewController 来管理窗口的各个部分。

    无论如何,我在回答中使用了 NSWindowController。

    下图显示了文件所有者的出口,即我的MainWindowController

    我在Xcode6.2 中通过以下方式创建了 MainWindowController .h/.m:

    1. 选择文件>新建>文件>OS X - 源代码 - Cocoa 类
    2. Subclass of: 选择 NSWindowController
    3. 检查also create .xib file for user interface

    然后我删除了默认 MainMenu.xib 中的窗口(不是菜单),并将通过上述步骤创建的 MainWindowController.xib 的名称更改为 MainWindow.xib。

    以下代码适用于我(但我是 Cocoa 初学者!):

    //
    //  AppDelegate.m
    //  PopUpButtons
    
    #import "AppDelegate.h"
    #import "MainWindowController.h"
    
    @interface AppDelegate ()
    
    @property(strong) MainWindowController* mainWindowCtrl;
    
    @end
    
    
    @implementation AppDelegate
    
    
    - (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
        // Insert code here to initialize your application
    
        [self setMainWindowCtrl:[[MainWindowController alloc] init]];
        [[self mainWindowCtrl] showWindow:nil];
    }
    
    - (void)applicationWillTerminate:(NSNotification *)aNotification {
        // Insert code here to tear down your application
    }
    
    @end
    

    ...

    //
    //  MainWindowController.m
    //  PopUpButtons
    //
    
    #import "MainWindowController.h"
    #import "MyData.h"
    
    @interface MainWindowController ()
    
    @property(strong) MyData* data;
    @property(weak) IBOutlet NSPopUpButton* namePopUp;
    @property(weak) IBOutlet NSPopUpButton* agePopUp;
    
    @end
    
    
    @implementation MainWindowController
    
    -(id)init {
    
        if (self = [super initWithWindowNibName:@"MainWindow"]) {
            _data = [[MyData alloc] init];  //Get data for popups
        }
    
        return self;
    }
    
    - (void)windowDidLoad {
        [super windowDidLoad];
    
        // Implement this method to handle any initialization after your window controller's window has been loaded from its nib file.
    
        [[self namePopUp] removeAllItems];
        [[self namePopUp] addItemsWithTitles:[[self data] drinks]];
        [[self agePopUp] removeAllItems];
        [[self agePopUp] addItemsWithTitles:[[self data] extras]];
    }
    
    @end
    

    ...

    //
    //  MyData.h
    //  PopUpButtons
    //
    
    #import <Foundation/Foundation.h>
    
    @interface MyData : NSObject
    
    @property NSArray* drinks;
    @property NSArray* extras;
    
    @end
    

    ...

    //
    //  MyData.m
    //  PopUpButtons
    //
    
    #import "MyData.h"
    
    @implementation MyData
    
    - (id)init {
        if (self = [super init]) {
            _drinks = @[@"coffee", @"tea"];
            _extras = @[@"milk", @"sugar", @"honey"];
        }
    
        return self;
    
    }
    
    @end
    

    我希望这会有所帮助。如果您需要更多截图,请告诉我。

    编辑1:

    我想我明白你在问什么。虽然我认为这不是一个很好的方法,但如果我将代码更改为:

    //
    //  MyData.h
    //  PopUpButtons
    //
    
    #import <Cocoa/Cocoa.h>
    
    @interface MyData : NSObject
    
    @property (copy) NSArray* drinks;
    @property (copy) NSArray* extras;
    
    -(void)newRequest;
    
    @end
    

    ...

    //
    //  MyData.m
    //  PopUpButtons
    //
    
    #import "MyData.h"
    
    @interface MyData()
    
    @property (weak) IBOutlet NSPopUpButton* drinksPopUp;
    @property (weak) IBOutlet NSPopUpButton* extrasPopUp;
    
    @end
    
    
    @implementation MyData
    
    - (id)init {
        if (self = [super init]) {
            _drinks = @[@"coffee", @"tea"];
            _extras = @[@"milk", @"sugar", @"honey"];
        }
    
        return self;
    
    }
    
    
    -(void)newRequest {
        [[self drinksPopUp] removeAllItems];
        [[self drinksPopUp] addItemsWithTitles:[self drinks]];
    
        [[self extrasPopUp] removeAllItems];
        [[self extrasPopUp] addItemsWithTitles:[self extras]];
    }
    
    @end
    

    我无法填充 NSPopUpButtons。这就是我所做的:

    1. 我将一个对象从对象库拖到 IB 的停靠栏中,然后在身份检查器中,我将对象的类更改为 MyData。

    2. 然后我点击 Connections Inspector,MyData 中的两个实例变量drinksPopUp 和 extrasPopUp 被列在 Outlets 中。

    3. 我从插座拖到相应的 NSPopUpButtons。

    我想我和你一样认为,当我的程序运行时,NSPopUpButtons 将被分配给实例变量drinksPopUp 和 extrasPopUp——但情况似乎并非如此。根据Apple docs,您应该可以这样做:

    应用程序通常会在其自定义之间设置出口连接 控制器对象和用户界面上的对象,但它们可以是 在可以表示为实例的任何对象之间创建 界面生成器,...

    编辑2:

    我可以将 MainWindowController 中的 NSPopUpButtons 传递给 newRequest 方法,并且可以使用 newRequest 中的 NSPopUpButtons 成功填充数据。

    编辑3:

    我知道自定义类 (newRequest) 中的方法有效,因为 我添加了一个 NSLog 命令来在方法执行时打印“Test”。

    但是当您记录指向 NSPopUpButtons 的变量时会发生什么?使用Edit1 中的代码,我得到的变量为 NULL,这意味着 NSPopUpButtons 从未被分配给变量。

    编辑4

    如果我将 awakeFromNib 方法添加到 MyData,并在 awakeFromNib 中记录 Edit1 中代码的 NSPopUpButton 变量,我会得到非 NULL 值。这告诉我 MainWindowController 的 windowDidLoad 方法在 MyData 的 awakeFromNib 方法之前执行,因此您不能在 MainWindowController 的 windowDidLoad 方法中调用 newRequest,因为 MyData 尚未完全初始化。

    编辑5

    好的,我让 Edit1 中的代码工作了。 Apple docs 这么说:

    关于顶级对象

    当您的程序加载一个 nib 文件时,Cocoa 会重新创建整个图形 您在 Xcode 中创建的对象。该对象图包括所有 窗口、视图、控件、单元格、菜单和自定义对象 笔尖文件。顶级对象是这些对象的子集 没有父对象[在 IB 中]。顶级对象通常 仅包括窗口、菜单栏和自定义控制器对象 您添加到 nib 文件 [如 MyData 对象]。 (文件的所有者等对象,首先 Responder 和 Application 是占位符对象,不予考虑 顶级对象。)

    通常,您使用 File’s Owner 对象中的 outlet 来存储 对 nib 文件的顶级对象的引用。 如果您不使用 但是,您可以从 笔尖加载例程直接。您应该始终保留指向 这些对象在某处,因为您的应用程序负责 使用完毕后释放它们。有关更多信息 加载时 nib 对象的行为,请参阅管理 来自 Nib 文件的对象。

    按照上面加粗的那一行,我在MainWindowController.m中修改了这个声明:

    @interface MainWindowController ()
    
    @property(strong) MyData* data;
    ...
    
    @end
    

    到这里:

    @interface MainWindowController ()
    
    @property(strong) IBOutlet MyData* data;
    ...
    
    @end
    

    然后,在 IB 中,我将连接从 MainWindowController data 出口拖到 MyData 对象(我之前从对象库拖到文档中的对象)。

    我猜这会导致 MyData 从 .xib 文件中取消归档并在 MainWindowController 之前初始化。

    【讨论】:

    • 您好,我已经为您的 cmets 做好了编辑 3 的准备(感谢您的详细回复)。我没有将任何变量信息传递给我的自定义类中的方法。我在方法中分配变量,并通过 NSViewController 建立了从自定义类到主寡妇的 IBOutlet 连接。我试图从主程序(AppDelegate)中做的就是运行该方法并让它完成更新弹出按钮中的项目的所有工作。谢谢,基思
    • 嗨 Keith,1) 我相信专家会认为这是一种糟糕的组织方式。在 MVC 模型中,模型(您的自定义类)不允许直接与视图(窗口)通信。相反,控制器应该从模型(您的自定义类)中检索数据,然后将数据发送到视图。我认为我最初的例子(在我的答案的底部)是组织事物的方式。但是,我仍然有兴趣弄清楚为什么我的 Edit1 代码(我认为它与您的代码相似)不起作用。在 Edit4 中,我描述了我认为正在发生的事情......
    • 这是你的控制器有一个指针的自定义类实例,直到 windowDidLoad 执行之后才会被初始化。换句话说,直到 windowDidLoad 执行之后,指向 NSPopUpButtons 的指针才会分配给自定义类的实例变量。在 Edit5 中,Apple 文档中引用了一段话,这表明窗口的所有者,即 IB 中的文件所有者(我认为是您的 ViewController)应该有一个指向您的自定义类实例的出口......跨度>
    • 如果我更改 WindowController 中指向我的自定义类实例的变量的声明,以将 IBOutlet 添加到声明中;然后从 Connections Inspector 中的那个插座拖到我的自定义类实例中,代码就可以工作了。换句话说,我的自定义类中的实例变量被分配了 NSPopUpButtons before windowDidLoad 执行。我认为在 Controller 和自定义类实例之间创建连接会使自定义类实例成为 Controller 的子对象,然后必须在 Controller 的...
    • 窗口可以加载。但是,再一次,我不认为你应该这样组织你的代码。
    猜你喜欢
    • 2014-08-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-06-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-24
    相关资源
    最近更新 更多