Search code examples
swiftmacoscocoadelegation

Delegation: How to Pass a Property Between Custom Views


In the code below, which (I hope) includes all that's relevant to my question, a mouseEntered/-Exited event in ChangerView is supposed to change the display in ChangingView. (ChangerView and ChangingView are displayed side-by-side and share a view controller.) As an OOP newbie, though, I'm seriously missing something about how to set up delegation between these views. Here's ChangerView (in which DoThis?.show = nil, despite that I thought I was setting it to true or false):

import Cocoa

protocol DoThis { var show: Bool { get set } }

class ChangerView: NSView {

    var changeDelegate: DoThis?

    // Set up for mouseEntered/-Exited
    override func mouseEntered(theEvent: NSEvent) { DoThis?.show = true }
    override func mouseExited(theEvent: NSEvent) { DoThis?.show = false }
}

And here's changing view:

import Cocoa

class ChangingView: NSView, DoThis {

    var show: Bool = false { didSet { needsDisplay = true } }

    // Draw into the view
    override func drawRect(dirtyRect: NSRect) {

        switch show {
        case true:  // Display setup contingent on show = true
        case false: // Display setup contingent on show = false
        }
        // Draw contingent display
    }
}

As I understand things, views should do their own basic display work, and view controllers should handle model-related and higher-level display changes. For that reason, and to keep things simple, I want ChangerView and ChangingView to communicate directly. Unfortunately, I couldn't find any explanations about delegation close enough to this situation—at least not that I could understand.

What am I missing (besides a properly functioning brain)?

Thanks!


Solution

  • It looks like there are two issues.

    In your ChangerView class, you should be using the delegate to set the show variable, like this:

    import Cocoa
    
    protocol DoThis { var show: Bool { get set } }
    
    class ChangerView: NSView {
    
        var changeDelegate: DoThis?
    
        // Set up for mouseEntered/-Exited
        override func mouseEntered(theEvent: NSEvent) { changeDelegate?.show = true }
        override func mouseExited(theEvent: NSEvent) { changeDelegate?.show = false }
    }
    

    You may want to make the delegate variable weak to prevent reference cycles

    The other issue is you've forgot the step where you assign the delegate. I (and I think everyone else) forget this often. Once you get used to setting up delegates you'll remember to check for it if things don't work at first.

    So at some point you need to set the changeDelegate var to an instance of the ChangingView class (this is often done in the viewDidLoad() function.

    It will look something like this:

    class SomeViewController: UIViewController {
        @IBOutlet weak var SomeChangerView: ChangerView!
        @IBOutlet weak var SomeChangingView: ChangingView!
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            SomeChangerView.changerDelegate = SomeChangingView
    }