如何使用Swift 3从Xib加载NSView

时间:2016-10-26 07:59:53

标签: swift macos load xib nsview

如何正确加载Xib中的NSView?

我的代码:

var topLevelArray: NSArray? = nil
let outputValue = AutoreleasingUnsafeMutablePointer<NSArray>(&topLevelArray)

if Bundle.main.loadNibNamed("RadioPlayerView", owner: nil, topLevelObjects: outputValue) {
    let views = outputValue.pointee
    return views.firstObject as! RadioPlayerView
}

topLevelArray = nil
return nil

问题是“outputValue”是一个自动释放指针,一旦我从函数返回,程序就会崩溃,ACCESS_BAD_ADDRESS

2 个答案:

答案 0 :(得分:6)

我做了一个协议和扩展来做到这一点:

import Cocoa

protocol NibLoadable {
    static var nibName: String? { get }
    static func createFromNib(in bundle: Bundle) -> Self?
}

extension NibLoadable where Self: NSView {

    static var nibName: String? {
        return String(describing: Self.self)
    }

    static func createFromNib(in bundle: Bundle = Bundle.main) -> Self? {
        guard let nibName = nibName else { return nil }
        var topLevelArray: NSArray? = nil
        bundle.loadNibNamed(NSNib.Name(nibName), owner: self, topLevelObjects: &topLevelArray)
        guard let results = topLevelArray else { return nil }
        let views = Array<Any>(results).filter { $0 is Self }
        return views.last as? Self
    }
}

用法:

final class MyView: NSView, NibLoadable {
    // ...
}

//创建名为MyView.xib

的xib

// ......其他地方:

let myView: MyView? = MyView.createFromNib()

答案 1 :(得分:0)

我用稍微不同的方法解决了这个问题。 Swift 5中的代码。

如果您要创建从NSView加载到{。 addSubview和代码中的约束,下面是示例:

.xib

如果没有与类名同名的.xib文件,则代码将仅根据代码创建类。如果有人想以相同的方式从代码和xib文件创建视图,并保持代码井井有条,那么这是一个很好的解决方案(IMO)。

public static func instantiateView<View: NSView>(for type: View.Type = View.self) -> View { let bundle = Bundle(for: type) let nibName = String(describing: type) guard bundle.path(forResource: nibName, ofType: "nib") != nil else { return View(frame: .zero) } var topLevelArray: NSArray? bundle.loadNibNamed(NSNib.Name(nibName), owner: nil, topLevelObjects: &topLevelArray) guard let results = topLevelArray as? [Any], let foundedView = results.last(where: {$0 is Self}), let view = foundedView as? View else { fatalError("NIB with name \"\(nibName)\" does not exist.") } return view } public func instantiateView() -> NSView { guard subviews.isEmpty else { return self } let loadedView = NSView.instantiateView(for: type(of: self)) loadedView.frame = frame loadedView.autoresizingMask = autoresizingMask loadedView.translatesAutoresizingMaskIntoConstraints = translatesAutoresizingMaskIntoConstraints loadedView.addConstraints(constraints.compactMap { ctr -> NSLayoutConstraint? in guard let srcFirstItem = ctr.firstItem as? NSView else { return nil } let dstFirstItem = srcFirstItem == self ? loadedView : srcFirstItem let srcSecondItem = ctr.secondItem as? NSView let dstSecondItem = srcSecondItem == self ? loadedView : srcSecondItem return NSLayoutConstraint(item: dstFirstItem, attribute: ctr.firstAttribute, relatedBy: ctr.relation, toItem: dstSecondItem, attribute: ctr.secondAttribute, multiplier: ctr.multiplier, constant: ctr.constant) }) return loadedView } 文件名和类名必须具有相同的名称:

Xib file name and class name must have the same name

.xib文件中,您应该只有一个视图对象,并且该对象必须具有设置的类:

enter image description here

您需要在类代码中添加的只是.xib中的instantiateView(),例如:

awakeAfter

例如要实例化此视图您可以使用ViewController创建这样的视图:

import Cocoa internal class ExampleView: NSView { internal override func awakeAfter(using coder: NSCoder) -> Any? { return instantiateView() // You need to add this line to load view } internal override func awakeFromNib() { super.awakeFromNib() initialization() } } extension ExampleView { private func initialization() { // Preapre view after view did load (all IBOutlets are connected) } }

let exampleView: ExampleView = .instantiateView()

但是Swift有时会遇到这样的实例化问题:

let exampleView: ExampleView = ExampleView.instantiateView()

在控制器的let exampleView = ExampleView.instantiateView()中,您可以将此视图添加为子视图:

viewDidLoad()