Append text to NSScrollView - Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value

Append text to NSScrollView - Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value

我正在做一个 Mac 应用程序,当我从另一个 class 调用函数时,我在将文本附加到 NSScrollView 时遇到问题。

我的 ViewController class:

上有这个功能
import Cocoa

class PopoverVC1: NSViewController {

let popover1 = NSPopover()

class func loadView() ->PopoverVC1 {
    let vc = NSStoryboard(name: NSStoryboard.Name(rawValue: "Main"), 
bundle: nil).instantiateController(withIdentifier: 
NSStoryboard.SceneIdentifier(rawValue: "Popover1")) as! PopoverVC1
    vc.popover1.contentViewController = vc
    return vc
}

override func viewDidLoad() {
    super.viewDidLoad()

    popover1.behavior = .transient
    popover1.contentViewController = self
}

func showPopover (view: NSView){
popover1.show(relativeTo: view.bounds, of: view, preferredEdge: .maxY)

}

@IBOutlet weak var radioOption1: NSButton!
@IBOutlet weak var radioOption2: NSButton!
@IBOutlet weak var radioOption3: NSButton!


@IBAction func clickOption(_ sender: NSButton) {
    switch sender {
    case radioOption1: popover1.performClose(sender)

    case radioOption2: let vc = ViewController()
        vc.myPrint(string: "This is a test")

    default: print ("hello")

    }
}
}

比起我有一个 PopoverVC1 class,这是一个 class 我正在使用的弹出窗口:

import Cocoa

class ViewController: NSViewController {


@IBOutlet weak var oneYes: NSButton!
@IBOutlet weak var oneNo: NSButton!
@IBOutlet weak var notesArea: NSScrollView!


override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view.

}

override var representedObject: Any? {
    didSet {
    // Update the view, if already loaded
    }
}

func myPrint (string: String){
    let mystring = string
    let myNotes = notesArea.documentView as? NSTextView
    let text = myNotes?.textStorage!
    let attr = NSAttributedString(string: mystring)
    text?.append(attr)

}

let popover1 = NSPopover()
@IBAction func oneClicked(_ sender: NSButton) {


    switch sender {
    case oneYes: let vc = PopoverVC1.loadView()
        vc.showPopover(view: sender)

    case oneNo:
    let myNotes = notesArea.documentView as? NSTextView
    let text = myNotes?.textStorage!
    let attr = NSAttributedString(string: "test")
    text?.append(attr)

    default: print ("")
    }
}
}

但是,当我按下应该调用函数 "myPrint" 并传递参数的单选按钮 "oneNo" 时出现错误。

线程 1:致命错误:在展开可选值时意外发现 nil

我做了一些测试,当我从 ViewCotroller class 中调用相同的函数 "myPrint" 时,它工作正常。

有什么想法吗?

您的问题在 clickOption 中,当您致电时:

let vc = ViewController()
vc.myPrint(string: "This is a test")

当您从代码中调用此方法并且 ViewController's UIViews 在故事板中设置时,不会建立与故事板的连接。这就是为什么当您调用函数 myPrintnotesArea 为 nil 的原因。在这种情况下,您正在创建 ViewController 的新副本,它不会与创建弹出窗口的副本相同。

有几种方法可以解决您要解决的问题。其中之一被称为 delegate。这是一种调用 ViewController's 方法的方法,就像您 popover 继承它们一样。您可以查看教程 here。我们的想法是,我们希望在您的 popover 中引用 ViewController,以便您可以调用 protocol 中的函数。那么符合protocolViewController将负责处理方法调用。

所以让我们创建一个名为 PrintableDelegateprotocol 并让您的 ViewController class 符合它。然后在你的 popover 中,你将能够将 ViewController 作为一个名为 delegateweak var 引用(你可以使用任何你想要的名称,但 delegate 是标准的)。然后我们可以通过简单地写delegate?.myPrint(string: "Test")来调用协议PrintableDelegate中描述的方法。我已经从我的示例中删除了一些与您无关的代码。

protocol PrintableDelegate {
    func myPrint(string: String)
}

class ViewController : UIViewController, PrintableDelegate {

    func myPrint (string: String){
        let mystring = string
        let myNotes = notesArea.documentView as? NSTextView
        let text = myNotes?.textStorage!
        let attr = NSAttributedString(string: mystring)
        text?.append(attr)
    }

    @IBAction func oneClicked(_ sender: NSButton) {
        let vc = PopoverVC1.loadView()
        // Set the delegate of the popover to this ViewController
        vc.delegate = self
        vc.showPopover(view: sender)
    }
}

class PopoverVC1: NSViewController {

    // Delegates should be weak to avoid a retain cycle
    weak var delegate: PrintableDelegate?

    @IBAction func clickOption(_ sender: NSButton) {
        // Use the delegate that was set by the ViewController
        // Note that it is optional so if it was not set, then this will do nothing
        delegate?.myPrint(string: "This is a test")
    }
}