如何在Interface Builder中使用自定义NSView?

Bil*_*ner 5 macos appkit swift3 macos-sierra xcode8

我已经看过很多关于如何在Interface Builder中创建自定义视图的教程.一切都适用于iOS,但MacOS应该相似,不是吗?我尝试了一些方法但没有一个完全成功.该init(coder:)呼叫的NIB实例化(通过Bundle.main.loadNibNamedNSNib,反过来,调用init(coder:)与无限递归结束了,如果我类我笔尖的主要观点为我的自定义类

如果我使用标准类,那么使文件的所有者我的自定义类工作得更好,但仍然是不对的.

是否有一个使用AppKit创建自定义控件的示例?我最接近的是显示自定义控件但没有自动布局设置有效.

它必须相当简单,但我还没想出来.

这是我到目前为止:

  1. 一个新类MyControl


import Cocoa

@IBDesignable class MyControl: NSView {

@IBOutlet var customView: NSView!  // The top level NSView
@IBOutlet weak var insideButton: NSButton!  // The button inside the view

let myName: String

required init?(coder: NSCoder) {
    super.init(coder: coder)

    if Bundle.main.loadNibNamed("MyControl", owner: self, topLevelObjects: nil) {
        addSubview(customView)
    }
}
Run Code Online (Sandbox Code Playgroud)

}

  1. 基于NSView的nib包含一个居中的NSButton.的File's Owner类设置为MyControl,顶层视图保持为NSView

  2. Main.storyboard已定制视图归类为MyControl具有的高度和宽度设定为中心.

当我查看Main.storyboard它有自定义视图的框架但它是空白.

当我运行应用程序时,显示的窗口是空白的.

Bil*_*ner 11

经过Apple Support的大量搜索和大量帮助,我发现在AppKit中创建和使用自定义控件非常容易.只是它就像锁中的钥匙一样,除非你做对了,否则你根本不会得到太多.

我创建了一个示例项目并将其发布到GitHub:https://github.com/ctgreybeard/SwiftCustomControl

这是一个小项目,我希望我已经完全评论它,以便其他人可以理解它.

这个过程的要点是这样的:

  1. 在Interface Builder中,创建一个XIB和NSView的子类.它们应该是相同的名称,但这不是必需的.
  2. 对于XIB,将类更改为File's Owner新类的名称.
  3. 根据需要构建新的自定义控件.
  4. 在类中包含一个IBOutlet,引用XIB中的顶级NSView.还包括您的控件所需的任何其他操作或出口.
  5. 创建初始化程序 required init?(coder: coder)
  6. 在该初始化程序中:
    1. 加载时使用笔尖let newNib = NSNib(nibNamed: myName, bundle: Bundle(for: type(of: self)))其中MYNAME是厦门国际银行的名称.
    2. newNib.instantiate(withOwner: self, topLevelObjects: nil) 新的NSNib
    3. 重新创建旧的顶级NSView中的所有现有约束,替换旧的NSView self.在constraints属性的for循环中执行此操作.或者,您可以简单地创建您知道的约束.
    4. self.addSubview对于所有旧的顶级,subviews这很容易subviews在旧NSView中的数组的for循环中完成.
    5. 应用您在上面创建的新数组约束.

您已完成...现在,自定义控件应该在Interface Builder和应用程序中正确显示.

评论:这很简单,实际上不应该是必要的.我应该能够在XIB中的顶级NSView中使用我的自定义类名,并完成它.init中的代码只是用我们的自定义视图替换顶级NSView.


Osk*_*kar 5

以下是Bills解决方案的一些代码:

我们可以创建一个协议LoadableNib以使我们的自定义视图符合要求,并在满足以下条件时对其进行扩展以获取此功能:

protocol LoadableNib {
    var contentView: NSView! { get }
}

extension LoadableNib where Self: NSView {

    func loadViewFromNib() {
        let bundle = Bundle(for: type(of: self))
        let nib = NSNib(nibNamed: .init(String(describing: type(of: self))), bundle: bundle)!
        _ = nib.instantiate(withOwner: self, topLevelObjects: nil)

        let contentConstraints = contentView.constraints
        contentView.subviews.forEach({ addSubview($0) })

        for constraint in contentConstraints {
            let firstItem = (constraint.firstItem as? NSView == contentView) ? self : constraint.firstItem
            let secondItem = (constraint.secondItem as? NSView == contentView) ? self : constraint.secondItem
            addConstraint(NSLayoutConstraint(item: firstItem as Any, attribute: constraint.firstAttribute, relatedBy: constraint.relation, toItem: secondItem, attribute: constraint.secondAttribute, multiplier: constraint.multiplier, constant: constraint.constant))
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

然后在我们的自定义视图类中:

class YourViewClass: NSView, LoadableNib {

    @IBOutlet var contentView: NSView!

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        loadViewFromNib()
    }
}
Run Code Online (Sandbox Code Playgroud)

不要忘记按照Bill的回答(您的自定义视图类的File Owner的类,以及NSView用于容纳视图内容并连接到contentView插座的容器)设置xib 。