【问题标题】:Programmatic creation of NSView in Cocoa在 Cocoa 中编程创建 NSView
【发布时间】:2016-10-12 11:09:43
【问题描述】:

我习惯于为 iOS 编程,我已经非常习惯于UIViewController。现在,我正在创建一个 OSX 应用程序,我有一些关于最佳实践的一般性问题。

UIViewController 中,我通常在-(void)viewDidLoad 方法中设置我的视图-除非确实需要,否则我实际上不会为UIViewController 创建自定义UIView-因此UIViewController 将视图添加到其自己的观点,删除它们,动画它们等等 - 首先,是好的做法吗?

对于我的主要问题 - OSX 的最佳实践是什么?我喜欢以编程方式创建接口,并且更喜欢这种方式。如果我说创建一个新的自定义窗口并想要管理它的视图。最好的方法是什么,我在哪里最好地实例化用户界面?

总结:如何以编程方式构建自定义视图并在 OSX 中的视图和控制器之间建立最佳实践关系?使用视图控制器在其视图中创建视图是否被认为是一种好习惯?

亲切的问候

【问题讨论】:

  • OSX 包含很多可能容易出错的东西,比如自动布局、最小化、最大化、调整大小。所以尝试通过xib和IB创建。然而,你可以使用代码,但对于初创公司来说,xib 是一个额外的糖。
  • 嘿,感谢您的评论。事情是,我对 Objective-c 并不是很陌生,并且我更喜欢以编程方式管理 autoresizingMask 等。您是说使用xib和IB通常更好吗?否则我仍然很想知道以编程方式做所有事情的最佳实践是:)
  • 没关系,有了 IB,只需点击几下即可完成。但是对于复杂的事情,我们甚至需要编程。
  • 不过,我的问题不仅仅是能够创建视图。我想知道纯代码方法的最佳实践是什么,不一定是创建 UI 的最简单方法。
  • 我同意@David 的观点——以编程方式创建所有内容比使用 IB 更容易、更快、可维护且不易出错。 自动布局完全消除了调整视图大小的需要,这是过去以编程方式创建视图最困难的部分。

标签: ios objective-c macos cocoa


【解决方案1】:

要在 NSViewController 的代码中构造视图,请覆盖 loadView 并确保设置视图变量。 不要调用 super 的实现,因为它会尝试从 NSViewController 的 nibName 和 nibBundle 属性中加载一个 nib。

-(void)loadView
{
    self.view = [[NSView alloc] init];
    //Add buttons, fields, tables, whatnot
}

对于 NSWindowController,过程非常相似。您应该在 loadWindow 的实现结束时调用 windowDidLoad。如果窗口为 nil,窗口控制器也不会调用 loadWindow,因此您需要在初始化期间调用它。 NSWindowController 似乎假设您将在创建控制器之前在代码中创建窗口,除非从笔尖加载。

- (id)initWithDocument:(FFDocument *)document
                   url:(NSURL *)url
{
    self = [super init];
    if (self)
    {
        [self loadWindow];
    }

    return self;
}    
- (void)loadWindow
{
    self.window = [[NSWindow alloc] init];
    //Content view comes from a view controller
    MyViewController * viewController = [[MyViewController alloc] init];
    [self.window setContentView:viewController.view];
    //Your viewController variable is about to go out of scope at this point. You may want to create a property in the WindowController to store it.
    [self windowDidLoad];
}

一些可选的幻想(10.9 和更早版本)

在 10.10 之前,NSViewControllers 不在 OSX 的第一响应者链中。当响应者链中存在项目时,菜单将自动为您启用/禁用菜单项。您可能希望使用 NSViewController 属性创建自己的 NSView 子类,以允许它将控制器添加到响应者链中。

-(void)setViewController:(NSViewController *)newController
{
    if (viewController)
    {
        NSResponder *controllerNextResponder = [viewController nextResponder];
        [super setNextResponder:controllerNextResponder];
        [viewController setNextResponder:nil];
    }

    viewController = newController;

    if (newController)
    {
        NSResponder *ownNextResponder = [self nextResponder];
        [super setNextResponder: viewController];
        [viewController setNextResponder:ownNextResponder];
    }
}

- (void)setNextResponder:(NSResponder *)newNextResponder
{
    if (viewController)
    {
        [viewController setNextResponder:newNextResponder];
        return;
    }

    [super setNextResponder:newNextResponder];
}

最后,当我使用自定义视图时,我使用了一个自定义的 NSViewController 来覆盖 setView 来设置 viewController 属性。

-(void)setView:(NSView *)view
{
    [super setView:view];
    SEL setViewController = @selector(setViewController:);
    if ([view respondsToSelector:setViewController])
    {
        [view performSelector:setViewController withObject:self];
    }
}

- (BOOL)acceptsFirstResponder
{
    return YES;
}

【讨论】:

  • 感谢您提供非常好的答案。现在,在-(void)loadView 中,我应该在哪里构建 VC 视图的子视图?还是我应该在自定义视图-(id)initWithFrame: 中执行此操作?如果在NSViewController 中,我如何处理我不可能知道我想要创建的视图框架的事实?
  • 我从不指定帧大小。自动布局为我做了这一切。窗口控制器会自动调整 contentView 的大小。如果您在子视图中使用 viewController 视图,则需要添加调整大小掩码或自动布局约束。编辑答案。
  • 是的,loadView 是您添加子视图的地方。我建议不要在init 中进行子视图填充。这使您有机会在创建控制器和访问视图之间设置可用于视图填充的属性(例如,如果您使用核心数据,则为托管对象上下文)。
  • 谢谢!但是,如果您要创建具有多个子视图的自定义 VC,这些子视图位于基于视图边界的布局中,该怎么办?
  • 如果您将 VC 的视图放入容器视图(如 NSSplitView、NSScrollView、NSTabViewItem 等),容器视图将自动设置 VC 的视图边界。如果要放置 VC 视图的视图只是某物的子视图,则只需获取现有视图的边界,删除现有视图,获取 VC 视图,将 VC 视图边界设置为旧视图边界,然后添加VC 视图作为子视图。
【解决方案2】:

对于我的主要问题 - OSX 的最佳实践是什么?我喜欢 以编程方式创建接口,并且更喜欢这种方式。如果 我说创建一个新的自定义窗口并想要管理它的视图。什么是 最好的方法,以及我在哪里实例化用户界面 最好的?

所有这些都在awakeFromNibinit 中完成。

以下代码创建了许多窗口并将它们存储在数组中。您可以为每个窗口添加视图。每个视图都可能包含您希望拥有的所有控件。

self.myWindow= [[NSWindow alloc] initWithContentRect:NSMakeRect(100,100,300,300)
                                                   styleMask:NSTitledWindowMask
                                                     backing:NSBackingStoreBuffered
                                                       defer:NO];


[self.myWindowArray addObject:self.myWindow];

for (NSWindow *win in self.myWindowArray) {
    [win makeKeyAndOrderFront:nil];
}

【讨论】:

  • 谢谢。到目前为止,我对这一切都很失望:) 但是,我有兴趣知道我在哪里构建由 NSWindowController 或 NSViewController 管理的实际视图。假设我有一个带有几个文本字段的视图 - 我在哪里实例化它们并将它们放在视图层次结构中?在控制器管理的视图类中或在 init/awakeFromNib 中或在例如加载视图?
  • 取决于awakeFromNib is called when the controller itself is unarchived from a nib. viewDidLoad is called when the view is created/unarchived. This distinction is especially important when the controller's view is stored in a separate nib file.
  • 那么,如果我决定不使用 nib 文件呢?请注意,我想避免使用 nib 文件,因为我正在绘制大量视图,并为其中一个自定义 UI 组件。此外,“NSViewController”在 Cocoa 中没有“viewDidLoad”选择器。
  • @David:如果对您有帮助,请检查此代码。 keepandshare.com/doc/5604837/detailsview-zip-85k?da=y
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-07-06
  • 1970-01-01
  • 2010-09-25
  • 2014-07-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多