我看过很多关于如何在Interface Builder中创建自定义视图的教程。所有这些都是针对iOS的,但是MacOS应该是相似的,不是吗?我试过几种方法,但没有一种是完全成功的。init(coder:)
调用NIB实例化(通过Bundle.main.loadNibNamed
或NSNib
,后者反过来调用init(coder:)
,如果我将nib中的主视图类化为自定义类,则以无限递归结束
如果我使用一个标准类,那么让file的所有者成为我的自定义类,它工作得更好,但仍然是不正确的。
有没有使用AppKit创建自定义控件的示例?最接近的显示自定义控件,但自动布局设置都不起作用。
这一定很简单,但我还没弄明白。
这是我到目前为止所知道的:
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)
}
}
}
A nib based on NSView with contains a centered NSButton. The File's Owner class is set to MyControl, the top level view remains as NSView
The Main.storyboard has a Custom View classed as MyControl centered with height and width set.
When I view Main.storyboard it has a frame for the custom view but it is blank.
When I run the application the window that displays is blank.
发布于 2017-02-23 12:30:45
经过多次搜索和苹果支持人员的大量帮助,我发现在AppKit中创建和使用自定义控件非常容易。只是它就像锁上的钥匙,除非你做对了,否则你不会得到太多。
我已经创建了一个样例项目,并将其发布到以下位置的GitHub:https://github.com/ctgreybeard/SwiftCustomControl
这是一个小项目,我希望我已经对它进行了完整的注释,以便其他人能够理解它。
这个过程的要点是:
在接口生成器中的
File's Owner
的类更改为您的新类名。required init?(coder: coder)
let newNib = NSNib(nibNamed: myName, bundle: Bundle(for: type(of: self)))
加载nib,其中myName是required init?(coder: coder)
newNib.instantiate(withOwner: self, topLevelObjects: nil)
NSNibself
替换旧的NSView。在constraints
属性上的for循环中执行此操作。或者,您可以简单地为所有旧的顶级be.self.addSubview
创建已知的约束,这很容易在旧NSView. )中的subviews
数组上的subviews
循环中完成
你做完了..。自定义控件现在应该正确显示在界面生成器和应用程序中。
评论:这虽然很简单,但真的不应该是必要的。我应该能够简单地在XIB的顶级NSView中使用我的自定义类名,并完成它。初始化中的代码只是用我们的自定义视图替换了这个顶级NSView。
发布于 2018-07-16 01:59:12
下面是一些与Bill解决方案配套的代码:
我们可以创建一个协议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))
}
}
}
然后在我们的自定义视图类中:
class YourViewClass: NSView, LoadableNib {
@IBOutlet var contentView: NSView!
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
loadViewFromNib()
}
}
根据Bill的回答,不要忘记xib的设置(文件所有者的类是您的自定义视图类,容器NSView
用于保存视图内容并连接到contentView
插座)。
发布于 2018-11-21 16:50:35
基本步骤:
MyView.swift
如下所示:创建一个同名的xib文件,例如,MyView.xib
MyView
root view to
contentView
IBOutlet-
class MyView: NSView {
@IBOutlet var contentView: NSView!
override init(frame frameRect: NSRect) {
super.init(frame: frameRect)
setup()
}
required init?(coder decoder: NSCoder) {
super.init(coder: decoder)
setup()
}
private func setup() {
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)
addSubview(contentView)
// Autolayout code using cartograhpy library
constrain(self, contentView) { view, subview in
subview.edges == view.edges
}
}
}
它类似于Bill的步骤,但我认为Bill的步骤6可以简化。可以简单地将contentView
添加到自定义视图中,然后添加一个约束来填充。
也在我的blog post上。
https://stackoverflow.com/questions/42143122
复制相似问题