我用了一种稍微不同的方法来解决这个问题。使用 Swift 5 编写的代码。
如果你想要创建一个从 .xib
加载的 NSView
,并且想要通过代码添加子视图和约束,这里有一个示例:
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文件,则代码将仅从代码创建类。如果有人想要以相同的方式从代码和xib文件创建视图,并保持代码组织良好,那么这是一个非常好的解决方案(在我看来)。
.xib文件名和类名必须相同:
在
.xib
文件中,您只应该有一个视图对象,并且这个对象必须设置类:
![enter image description here](https://istack.dev59.com/4wvDS.webp)
在类代码中,您只需要添加
instantiateView()
到
awakeAfter
即可,例如:
import Cocoa
internal class ExampleView: NSView {
internal override func awakeAfter(using coder: NSCoder) -> Any? {
return instantiateView()
}
internal override func awakeFromNib() {
super.awakeFromNib()
initialization()
}
}
extension ExampleView {
private func initialization() {
}
}
要在例如 ViewController 中实例化此视图,您可以创建以下视图:
let exampleView: ExampleView = .instantiateView()
或者
let exampleView: ExampleView = ExampleView.instantiateView()
但是 Swift 有时会出现这样的实例化问题:
let exampleView = ExampleView.instantiateView()
在您的控制器的 viewDidLoad()
中,您可以将此视图添加为子视图:
internal override func viewDidLoad() {
super.viewDidLoad()
exampleView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(exampleView)
NSLayoutConstraint.activate(
[exampleView.topAnchor.constraint(equalTo: view.topAnchor),
exampleView.leftAnchor.constraint(equalTo: view.leftAnchor),
exampleView.rightAnchor.constraint(equalTo: view.rightAnchor),
exampleView.bottomAnchor.constraint(equalTo: view.bottomAnchor)]
)
}