【问题标题】:Add (addsubview) custom/reusable view from XIB OS X App从 XIB OS X App 添加(addsubview)自定义/可重用视图
【发布时间】:2017-12-26 18:12:01
【问题描述】:

这是我的自定义视图的代码。我还有一个自定义 XIB 文件,其中包含 mainView 视图。我需要在视图控制器的 stackView 中使用 3 次。我该怎么做?

class PlatformView: NSView {

    @IBOutlet weak var mainView: NSView!
    @IBOutlet weak var currentPriceLabel: NSTextField!

    //Here is the button
    @IBAction func testButtonPressed(_ sender: Any) {
    }

    override func draw(_ dirtyRect: NSRect) {
        super.draw(dirtyRect)
        // Drawing code here.
    }

    required init?(coder decoder: NSCoder) {
        super.init(coder: decoder)
        NSNib(nibNamed: NSNib.Name(rawValue: "PlatformView"), bundle: nil)?.instantiate(withOwner: self, topLevelObjects: nil)
        addSubview(mainView)
        self.mainView.frame = self.bounds
    }
}

这是我的 mainViewController 的代码

class ViewController: NSViewController {

    @IBOutlet weak var platformStackView: NSStackView!

    var platformView1:PlatformView!
    var platformView2:PlatformView!
    var platformView3:PlatformView!

    override func viewDidLoad() {
        super.viewDidLoad()
        //This part is not working in a macOS app
        if let tempView = Bundle.main.loadNibNamed(NSNib.Name(rawValue: "PlatformView"), owner: self, topLevelObjects: nil)?.first as? PlatformView {
            self.platformView1 = tempView
            self.platformView1.currentPriceLabel = "$1.35"
            self.platformStackView.addArrangedSubview(self.platformView1)
            self.platformStackView.addSubview()
            self.platformStackView.addView(tempView, in: self)
            //Which one of these three methods should I use?

        }
    }
}

我想通过 IBAction 插座连接平台视图中的按钮。我将如何处理这些?

我还添加了三种将视图添加到堆栈视图的方法。哪个最好用?

它还说.loadNibNamed 将返回一个 Bool 而不是视图的实例。如何在此视图控制器中多次加载视图?我显然还需要在 VC 的整个生命周期中进行更改,因此我需要保留该视图的实例。

【问题讨论】:

    标签: swift macos swift4


    【解决方案1】:

    首先你应该使用:

    self.platformStackView.addArrangedSubview(self.platformView1)
    

    因为这会将 self.platformView1 添加为子视图(根据 addSubView)并将其添加到堆栈视图排列的视图列表中。

    其次,XIB 文件可以包含多个顶级对象,因此不能指望它返回单个对象。 Bool 返回表示整个 XIB 文件是否已成功加载,顶层对象的内容将放入 topLevelObjects 参数中,该参数是一个数组。

    所以你做这样的事情:

    var topLevelObjects: NSArray?
    if Bundle.main.loadNibNamed(NSNib.Name(rawValue: "TestView"), owner: self, topLevelObjects: &topLevelObjects) {
        // Use the objects as you need including searching for a specific one you may require.
    }
    

    您也可以这样做:

    if let nib = NSNib(nibNamed: NSNib.Name(rawValue: "TestView"), bundle: nil) {
        if nib.instantiate(withOwner: self, topLevelObjects: &temp) {
            // Use the objects as you need including searching for a specific one you may require.
        }
    }
    

    使用时结果如下:

    这是一个从 XIB 文件加载自定义类并将其添加到当前视图控制器视图 3 次的工作示例。按钮操作将加载 XIB 文件实例化它的三个副本,并将它找到的 TestView 类添加到视图控制器主视图(在 0、110 和 220 处):

    @IBAction func buttonAction(_ sender: Any) {
        var objectArray: NSArray?
        if let nib = NSNib(nibNamed: NSNib.Name(rawValue: "TestView"), bundle: nil) {
            if nib.instantiate(withOwner: self, topLevelObjects: &objectArray),
                let topLevelObjects = objectArray {
                for object in topLevelObjects {
                    if let testView = object as? TestView {
                        self.view.addSubview(testView)
                        testView.frame = CGRect(x: 0, y: 0, width: 100, height: 100)
                        testView.testLabel.stringValue = "Test Label 1"
                    }
                }
            }
    
            if nib.instantiate(withOwner: self, topLevelObjects: &objectArray),
                let topLevelObjects = objectArray {
                for object in topLevelObjects {
                    if let testView = object as? TestView {
                        self.view.addSubview(testView)
                        testView.frame = CGRect(x: 110, y: 0, width: 100, height: 100)
                        testView.testLabel.stringValue = "Test Label 2"
                    }
                }
            }
    
            if nib.instantiate(withOwner: self, topLevelObjects: &objectArray),
                let topLevelObjects = objectArray {
                for object in topLevelObjects {
                    if let testView = object as? TestView {
                        self.view.addSubview(testView)
                        testView.frame = CGRect(x: 220, y: 0, width: 100, height: 100)
                        testView.testLabel.stringValue = "Test Label 3"
                    }
                }
            }
        }
    }
    

    TestView 代码如下所示:

    class TestView: NSView {
        @IBOutlet var testLabel: NSTextField!
    
        required init?(coder decoder: NSCoder) {
            super.init(coder: decoder)
        }
    
        override func awakeFromNib() {
            super.awakeFromNib()
            self.testLabel.stringValue = "Init"
        }
    
        override func draw(_ dirtyRect: NSRect) {
            super.draw(dirtyRect)
    
            // Drawing code here.
        }
    }
    

    自定义视图没有什么太复杂的地方。它有一个 NSTextField 设置正确,因此可以设置它的字符串值。

    这一切都对我有用,所以应该是要走的路(这是我实际完成的第一个 MacOS 应用程序,因为我通常是 iOS 人)。

    【讨论】:

    • 我收到此错误Cannot convert value of type 'NSArray?' to expected argument type 'AutoreleasingUnsafeMutablePointer<NSArray?>?'
    • 我得到了它,但它仍然在这里崩溃NSNib(nibNamed: NSNib.Name(rawValue: "PlatformView"), bundle: nil)?.instantiate(withOwner: self, topLevelObjects: nil)& 的目的是什么?
    • '&' 是必需的,因为您需要传递属性的地址指针而不是属性本身。然后,该数组将填充 XIB 文件中的对象列表。如果你没有它,你会得到转换错误。您在使用第二个版本时遇到了什么崩溃?
    • 我刚刚添加了一张我遇到的崩溃的图片
    • 对不起,我错误地将其添加到您的答案中。我打算将其添加到我的问题中
    猜你喜欢
    • 1970-01-01
    • 2013-04-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-02-04
    • 2019-03-12
    • 2016-08-08
    • 1970-01-01
    相关资源
    最近更新 更多