首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >如何在界面生成器中使用自定义NSView?

如何在界面生成器中使用自定义NSView?
EN

Stack Overflow用户
提问于 2017-02-10 01:26:47
回答 5查看 6.8K关注 0票数 8

我看过很多关于如何在Interface Builder中创建自定义视图的教程。所有这些都是针对iOS的,但是MacOS应该是相似的,不是吗?我试过几种方法,但没有一种是完全成功的。init(coder:)调用NIB实例化(通过Bundle.main.loadNibNamedNSNib,后者反过来调用init(coder:),如果我将nib中的主视图类化为自定义类,则以无限递归结束

如果我使用一个标准类,那么让file的所有者成为我的自定义类,它工作得更好,但仍然是不正确的。

有没有使用AppKit创建自定义控件的示例?最接近的显示自定义控件,但自动布局设置都不起作用。

这一定很简单,但我还没弄明白。

这是我到目前为止所知道的:

  1. A新类MyControl

代码语言:javascript
复制
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.
EN

回答 5

Stack Overflow用户

回答已采纳

发布于 2017-02-23 12:30:45

经过多次搜索和苹果支持人员的大量帮助,我发现在AppKit中创建和使用自定义控件非常容易。只是它就像锁上的钥匙,除非你做对了,否则你不会得到太多。

我已经创建了一个样例项目,并将其发布到以下位置的GitHub:https://github.com/ctgreybeard/SwiftCustomControl

这是一个小项目,我希望我已经对它进行了完整的注释,以便其他人能够理解它。

这个过程的要点是:

在接口生成器中的

  1. 创建XIB和NSView的子类。它们应该具有相同的名称,但这不是必需的。用于XIB的
  2. File's Owner的类更改为您的新类名。
  3. 按您希望的方式构建新的自定义控件。
  4. 在您的类中包含一个引用XIB中顶级NSView的IBOutlet。还包括您控制的任何其他操作或出口,needs.
  5. Create初始化程序required init?(coder: coder)
  6. Within:
  7. ,使用let newNib = NSNib(nibNamed: myName, bundle: Bundle(for: type(of: self)))加载nib,其中myName是required init?(coder: coder)
  8. Within的名称,新的XIB.
  9. newNib.instantiate(withOwner: self, topLevelObjects: nil) NSNib
  10. Recreate旧的顶级NSView中的所有现有约束,用self替换旧的NSView。在constraints属性上的for循环中执行此操作。或者,您可以简单地为所有旧的顶级be.
  11. self.addSubview创建已知的约束,这很容易在旧NSView.
  12. Apply (即您创建的新约束数组above.

)中的subviews数组上的subviews循环中完成

你做完了..。自定义控件现在应该正确显示在界面生成器和应用程序中。

评论:这虽然很简单,但真的不应该是必要的。我应该能够简单地在XIB的顶级NSView中使用我的自定义类名,并完成它。初始化中的代码只是用我们的自定义视图替换了这个顶级NSView。

票数 17
EN

Stack Overflow用户

发布于 2018-07-16 01:59:12

下面是一些与Bill解决方案配套的代码:

我们可以创建一个协议LoadableNib,使我们的自定义视图符合要求,并扩展它以获得此功能:

代码语言:javascript
复制
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))
        }
    }
}

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

代码语言:javascript
复制
class YourViewClass: NSView, LoadableNib {

    @IBOutlet var contentView: NSView!

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        loadViewFromNib()
    }
}

根据Bill的回答,不要忘记xib的设置(文件所有者的类是您的自定义视图类,容器NSView用于保存视图内容并连接到contentView插座)。

票数 12
EN

Stack Overflow用户

发布于 2018-11-21 16:50:35

基本步骤:

  1. MyView.swift如下所示:创建一个同名的xib文件,例如,MyView.xib
  2. Set
  3. ‘s Owner > Class > MyView
  4. Connect

root view to

  1. contentView IBOutlet

-

代码语言:javascript
复制
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上。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/42143122

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档