Latest Posts

macOS Storyboard Outlet-Like Connections Between View Controllers

Transitioning from Nibs to Storyboards poses a few challenges. One of them being creating outlets from a parent view controller to a child view controller. There is no such thing.

You could say this is the new idiomatic way to create interfaces, but I don’t quite agree with the consequences. How do you pass data to a view controller 4 levels deep in the hierarchy? It’s not obvious. And there are no “embed” segues like you have them on iOS to obtain references.

Just as I have beef with Segue’s, I don’t want my view controllers to be the central point of control. I want to tell them what to do. Eventually, I guess I’ll have to settle for a programmatic view, but that day is not today.

Turns out that NSViewController now sports a childViewControllers: [NSViewController] property which you can use to query for parent–child-relations.

As usual, my custom NSWindowController subclass also was the façade for the whole view hierarchy, implementing all view-related protocols to forward the display(foo:) commands to the child view controllers. That made the transition from Nib to Storyboard very easy, in fact, since there’s just a single point of change. High locality of change for the win! – If only the outdated child view controller outlets were easy to replace.

The best way I came up with is, upon windowDidLoad, traverse the view controller hierarchy and stop when a good match is found. Since even complex views won’t have hierarchies hundreds of levels deep, this doesn’t even take a noticeable amount of time.

This helper function looks for a match by type in a given view controller’s child collection, recursively:

func firstChildViewController<T: NSViewController>(
    _ parentViewController: NSViewController
    ) -> T? 
{
    for viewController in parentViewController.childViewControllers {
        if let match = viewController as? T { return match }
        if let subChild: T = firstChildViewController(viewController) { return subChild }
    }

    return nil
}

Since it’s generic, you just have to specify the desired type to look for so the compiler can figure out what you want:

let bananaVC: BananaViewController 
    = firstChildViewController(jungleViewController)

The only limitation is that this will stop on the first match. If you use the same type of NSViewController subclasses in multiple places, you to check for an additional criterion.

Inside my window controller, I use this function as follows:

class AmazingWindowController: NSWindowController {
    var bananaViewController: BananaViewController!
    var appleViewController: AppleViewController!
    
    override func windowDidLoad() {
        loadChildViewControllers()
        // ...
    }
    
    fileprivate func loadChildViewControllers() {
        self.bananaViewController = getChildViewController()
        self.appleViewController = getChildViewController()
    }
    
    fileprivate func getChildViewController<T: NSViewController>() -> T! {
        let firstViewController = self.window!.contentViewController!
        return firstChildViewController(firstViewController)
    }
}

Okay, so getChildViewController sounds like we’re back in Java-land. But it’s the best name I could come up with; “load” doesn’t fit since the view controller already is loaded. “Fetch” may work, or “find”. Whatever suits you.

With this in place, I am able to instantiate the child view controller reference properties in a fashion that resembles @IBOutlets closest.

And this, in turn, paves the way to extract the façade functionality from the window controller into another object. Nice!

NSSplitViewItem Vibrancy Is Not Added with Every Initializer

After yesterday’s wrap up of my fix for vibrant table views, I have discovered that NSSplitViewItem has 3 initializer variants since macOS 10.11:

  • init(contentListWithViewController: NSViewController)
  • init(sidebarWithViewController: NSViewController)
  • init(viewController: NSViewController)

The latter will not add vibrancy. Guess who, by accident, discovered his table view to be wrapped in a init(sidebarWithViewController:) call.

I’ll leave the other post up as reference for deactivating vibrancy and dealing with NSTableView text drawing intricacies.

Fixing NSTableView Cell Backgrounds by Deactivating NSSplitViewItem's Visual Effects

I discovered my own idiocy and found that I overlooked which initializer of NSSplitViewItem was called. Apparently, the sidebar-related one adds vibrancy by default. Dealing with un-vibrancifying a table view might still be interesting, so I leave this up.

Don’t jump to conclusions, folks, and don’t copy faulty code over to test projects: if I had typed the NSSplitViewItems setup code instead of pasting it in, I would’ve caught the initializer’s parameter difference.


My table view items looked odd for a while and I couldn’t figure out why. Until I disabled “Reduce transparency” in my System Preferences’s “Accessiblity” pane.

The various kinds of weird background artifacts that turned out to be behind-window blendings.

This was 0% my own genius and 100% thanks to a beta tester. (Thanks, Michel!)

I couldn’t reproduce this in a simple test app, no matter the settings of the views. Until I looked at the implementation of the split view controller. In the real app, I manage the split view with a NSSplitViewController subclass. Using this controller class, all of a sudden NSSplitViewItems are being wrapped in _NSSplitViewItemViewWrapper, each containing a NSVisualEffectsView. The dividers become “vibrant”, too.

NSSplitViewController only wraps the main pane in a visual effects view.** If you have a single pane, that will be wrapped. If you have 2 or more, only the pane at index #1 will be wrapped. Sidebars to the left and right are not affected, no matter how many you have. This assumed #1 is not a sidebar, of course.

Here’s the list of subviews of the NSSplitView when managed by a NSSplitViewController:

▿ 7 elements
  - 0 : <_NSSplitViewItemViewWrapper: 0x6000001a46e0>
  - 1 : <_NSSplitViewItemViewWrapper: 0x6080001a32c0>
  - 2 : <_NSSplitViewItemViewWrapper: 0x6080001a3480>
  - 3 : <NSVibrantSplitDividerView: 0x6080001863f0>
  - 4 : <NSVibrantSplitDividerView: 0x6080001864c0>
  - 5 : <_NSSplitViewSpringLoadingView: 0x608000169540>
  - 6 : <_NSSplitViewSpringLoadingView: 0x608000169600>

Debugging the UI shows this in the view hierarchy breadcrumb list:

Actual and expected view hierarchy

When you don’t use NSSplitViewController and its insertSplitViewItem(_:at:) or addSplitViewItem(_:), the visual effect views won’t be added. As expected. There’s no documented opting-out of this, either.

The first step in supporting vibrancy in a view is to contain the view in an instance of NSVisualEffectView. It is typically best to add vibrancy only to leaf views, not container views, because it is difficult to turn vibrancy off once it is on.
—Apple Docs for NSVisualEffectsView

Gee, thanks for adding vibrancy to the container view, then!

Deactivating Vibrancy

Either you don’t use NSSplitViewController or you deactivate vibrancy in the split view again. I imagine this is wasting CPU cycles, though.

To deactivate, you have to use your own NSVisualEffectsView and set its state property to NSVisualEffectState.inactive. This also works from within interface builder:

  1. Embed the split view item’s contents in a NSVisualEffectsView;
  2. Set or leave “Material” as “Match Appearance” (that is, don’t change anything from the next effects view in the hierarchy);
  3. Set “Blending Mode” to “Within Window” – or else the artifacts will stat.
  4. Set “State” to “Inactive”;
  5. Set the view’s appearance from “Vibrant Dark” to “Aqua”. That’s at the bottom of the attributes inspector where nothing interesting happens most of the time. If you miss this step, the table view cells will adjust the cell color to match the supposedly dark background and use a white text color automatically.

Optional: Fixing Editable Cells not Drawing White Background

Maybe it is just me, but figuring out custom table view backgrounds and adjusting the table cells accordingly is a rather painful experience. Even when the table looks right, editing the table cell contents now didn’t display opaque backgrounds.

If you enable “Draw Background” on the table cell’s NSTextField, you always get a white background.

You have to provide a background color before the window’s field editor appears. (Or you have to provide a custom field editor from your NSWindowController, but that will make your window drawing super slow.)

My favorite solution so far: program editing a cell myself.

The NSTableView behavior is set to “none”. I handle double-clicks and the Enter key myself and call cellView.beginEditing() on the selection. The cell view is configured thus:

class ResultTableCellView: NSTableCellView, NSTextFieldDelegate {

    func beginEditing() {

        guard let textField = self.textField else { return }

        textField.isEditable = true
        textField.isSelectable = true

        textField.selectText(nil)

        if let fieldEditor = textField.currentEditor() {

            fieldEditor.drawsBackground = true
            fieldEditor.backgroundColor = NSColor.white
            fieldEditor.textColor = NSColor.black
        }
    }

    private func endEditing() {

        guard let textField = self.textField else { return }

        textField.isEditable = false
        textField.backgroundColor = NSColor.clear
        textField.isSelectable = false

        textField.needsDisplay = true
    }

    override func controlTextDidEndEditing(_ obj: Notification) {
        
        endEditing()
    }
}

But that wasn’t quite enough. The field editor appearance was still mangled by default. So I had to stop the NSTextFieldCell from changing the field editor’s configuration:

class ResultTableTextFieldCell: VerticallyCenteredTextFieldCell {
    override func setUpFieldEditorAttributes(_ textObj: NSText) -> NSText {
        // Doing nothing is necessary to keep the editor white.
        return textObj
    }
}

And then, finally!, the text field obtains a white background when editing. When the user is not editing, the underlying NSTableRowView’s background can shine through.

Adding last(where:) in Swift

For quite a while I didn’t notice Sequence.first(where:) exists. It’s like first, only with a condition. Proposed and implemented by Russ Bishop, by the way. I have now happily migrated from my self-baked findFirst to this method – only to find out today that there’s not last(where:) equivalent.

Makes sense at first, since Sequence is not stride-able backwards. But BidirectionalCollection is, and thus Array.

In a huge collection of stuff, it’s probably too costly to simple call array.reversed().first(where: myPredicate). Here, not even lazy would help since reversed() returns an Array in the new order instead of a ReversedBidirectionalCollection<LazyCollection<Whatever>> or similar. So I’m cautious and prefer to enumerate backwards.

BidirectionalCollection has an indices property. In case of arrays, that’s a CountableRange<Int> which can be reversed far more cheaply. And the Indices associated type of BidirectionalCollection supports reversed(), too, so we can generalize to this:

extension BidirectionalCollection
where Self.Indices.Iterator.Element == Self.Index {

    func last(
        where predicate: (Self.Iterator.Element) throws -> Bool
        ) rethrows -> Self.Iterator.Element? {

        for index in self.indices.reversed() {
            let element = self[index]
            if try predicate(element) {
                return element
            }
        }

        return nil
    }
}

This iterates backwards without reversing the whole collection at first. Works with arrays and other interesting types like Ole Begemann’s SortedArray.

By the way: why on earth would BidirectionalCollection.Indices.Iterator.Element be allowed to ever not equal BidirectionalCollection.Index? Without the “where” clause, the compiler will complain about the subscript: “Cannot subscript a value of type ‘Self’ with an index of type ‘Self.Indices.Iterator.Element’”.

The 3 RxSwift Building Blocks of UI Components

So here’s what I learned so far about the building blocks of reactive UI components from peeking at the RxCocoa source.

The 3 Building Blocks

UI components in general can have properties (read/write), input ports (read), and output ports (write). Classic UIKit/AppKit output ports would be delegate calls; classic input ports would be commands like display(banana:) that you probably write every day or so.

Translated to the world of RxSwift:

  • Observable is the basic output sequence
  • Observer is the basic consumer of input event sequences
  • a combination of both fits mutable properties, the like Variable type, for example

Then there are special “traits” for UI bindings which guarantee to work on the main queue.

  • ControlEvent is an Observable trait
  • UIBindingObserver is an Observer trait
  • ControlProperty has both traits’s attributes and is an Observable sequence as well as an Observer

Translating Known UIControl/NSControl Properties to RxSwift

For example, NSTextField and UITextField expose .rx.text which is a ControlProperty<String>. That means it’s read-write. You can bind other sequences to this property and have the text field update its content; and you can observe user-generated changes as well.

The alpha value or translucency of a view component is a UIBindingObserver; similarly, isEnabled is a UIBindingObserver. Look at UIControl+Rx of RxCocoa for details.

The reasoning I came up with: you usually want to change the alpha value or enabled state in reaction to some event, so it needs to be an Observer (or “sink”) of sorts. You don’t expect any view component to generate changes to these on its own terms. These things are toggles that a controller usually manipulates.

So even though the underlying property of the UIKit/AppKit component is a readwrite (or var) property, it does not make too much sense to expose a ControlProperty in these cases.

That doesn’t mean you couldn’t, thanks to KVO. Here’s a working but conceptually bad example of how to expose a view property in a reactive way that I wrote:

public extension Reactive where Base: NSScrollView {
    public var backgroundColor: ControlProperty<NSColor> {
        let source = self.observeWeakly(NSColor.self, "backgroundColor", options: [.initial, .new])
            // Skip nil values (which in practice does not happen, but KVO observe returns Optional)
            .filter { $0 != nil }.map { $0! }
            .takeUntil(deallocated)

        // `base` is a property of `Reactive` and, here, of type `NSScrollView`
        let observer = UIBindingObserver(UIElement: base) { (scrollView, newColor: NSColor) in
            scrollView.backgroundColor = newColor
        }

        return ControlProperty(values: source, valueSink: observer)
    }
}

As you see, the ControlProperty is composed of a UIBindingObserver and a regular Observable sequence. But there’s a catch: ControlProperty requires your source sequence to adhere to a few conventional criteria. These criteria are not enforced through the types in this case. ControlProperty takes responsibility of adhering to certain characteristics but it’s your job to make them happen. Let’s talk about these rules in detail.

The Rules of Using These 3 RxCocoa Types Correctly

ControlEvent

The implementer (you!) has to provide an Observable that’s safe and auto-completes on deallocation of the object. ControlEvent itself takes care of main queue scheduling if needed.

Quoting the Swift source of RxCocoa v3.5, ControlEvent

  • it never fails
  • it won’t send any initial value on subscription
  • it will Complete sequence on control being deallocated
  • it never errors out
  • it delivers events on MainScheduler.instance

Most Observable sequences you build don’t send initial values, but there are some that have replay behavior you should be aware of.

For example, a Variable("initial") sends .next("initial") when subscription starts. A BehaviorSubject<String>("inital") does, too. But a PublishSubject<String>() doesn’t send anything upon subscription. You can see that it doesn’t require an initial value. Observable.from(["initial"]) will start right away, too.

ControlProperty

The requirements of ControlProperty are similar to these of ControlEvent, but an initial value is expected, so the source has to shareReplay(1).

Again, ControlProperty itself takes care of main queue scheduling.

  • it never fails
  • shareReplay(1) behavior
    • it’s stateful, upon subscription (calling subscribe) last element is immediately replayed if it was produced
  • it will Complete sequence on control being deallocated
  • it never errors out
  • it delivers events on MainScheduler.instance

UIBindingObserver

The “soft” attributes (that is, not enforced through the typing system) of UIBindingObserver are simpler: it does not bind errors (errors will merely be logged). And if the incoming sequence is not on the main queue already, UIBindingObserver dispatches to the main queue asynchronously.

From the call site, you don’t have to do anything.

It’s pretty easy to use:

public extension Reactive where Base: BananaView {
    public var size: UIBindingObserver<BananaView, Size> {
        // `base` is a property of `Reactive` and of type `BananaView`
        return UIBindingObserver(UIElement: base) { (bananaView, newSize) in
            bananaView.growVisibleBanana(to: newSize)
        }
    }
}

You pass in an object to UIBindingObserver.init that you want to change for incoming events. UIBindingObserver does the weak–strong-dance for you. It keeps a weak reference to the element only, so you don’t end up with a retain cycle. When a .next event reaches the observer, and if the weakly referenced element still exists, a reference to the element is passed back into the closure where you can mutate the underlying property.

If you create your objects using Variable type properties, you don’t need much of this stuff, of course. But if you want to provide a reactive extension to a regular view component, this is how you can do.

WebcamSnap Open Source Library Released

The picture cropping sheet in action

Another month goes by, another little macOS component was released.

This time, I wanted to have a very simple drop-in view component that takes pictures with the iSight camera of MacBooks or any other connected USB camera device. And if the user wants to crop the picture, I wanted to add that as post-processing.

Thought this would be a common need, but Google wasn’t helping much. So I coded this thing myself.

Have a look: https://github.com/CleanCocoa/WebcamSnap

How Taking Pictures with AVFoundation Works

Before I got it working, all the AVFoundation code looked pretty scary. But it’s simple, really, when you think about the possibilities of audio and video capturing and processing.

These are the requirements to take a photo with a USB camera using AVFoundation:

  1. you need a AVCaptureSession that controls the lifetime of the audio and/or video input and/or output;
  2. you specify output ports of type AVCaptureOutput, like the AVCaptureStillImageOutput I use here to grab a single image (instead of video or audio);
  3. you specify input ports of type AVCaptureInput, like the AVCaptureDeviceInput that takes a AVCaptureDevice, which in this case is of type AVMediaTypeVideo;
  4. you add a Core Animation layer (CALayer) to a preview view using AVCaptureVideoPreviewLayer(session:) so users see what the camera is showing before they hit the “Take Picture” button.

If things work out well, you have a session that can read video data from your camera device and streams the video signal to the preview. You can shoot photos, also known as “request still images” from the video stream, using captureStillImageAsynchronously on your AVCaptureStillImageOutput.

All of this setup is encapsulated in the Webcam object of my library. (links to the version of v1.0.0, no the latest, to prevent dead links in the future).

Hope it helps!

Do You Need to Use Action Creators in ReSwift to Conditional Action Dispatching? (No)

This is an answer to a StackOverflow question titled “When, why and how to use Action Creators in redux?” that is really targeted at ReSwift. The real question is phrased a bit differently, though:

For example, I have a button, after user pressed it I want to start some process if I’m in state A. So, I have to write an action creator, which will check current state and then return correct action, or not action at all. Then dispatch this action from the same place.

Redux.js and ReSwift behave quite differently. Redux recommendations may not apply in all cases.

Clearing up Redux.js/ReSwift confusions

Action Creators in ReSwift

“Action creators” is a rather specialized term in Redux.js, but the type is defined like this in ReSwift as of v4:

public typealias ActionCreator = (_ state: State, _ store: Store) -> Action?

So contrary to general advice, you do have access to the state at the point of dispatching the action. You don’t need a Thunk implementation, although Thunks or Epics can help in more complex cases.

“Action Creators help encapsulate action creation details”

A common benefit of Action Creators in Redux is their function as a factory.

Redux.js actions are object literals. Redux actions, if you were to write them in Swift, would be more like Dictionarys. If you come from an Objective-C background, you know both the good and bad of dictionaries: they are flexible, but you can cause trouble with typos, and the compiler won’t catch changes to values or keys if they are stringified. That’s how Action Creators in Redux.js provide the convenience of factories (as in “Factory”, the Gang of Four design pattern).

Here’s one as an example:

function addTodo(text) {
    return {
        type : "ADD_TODO",
        text
    }
}

// Somewhere else
dispatch(addTodo(text))

The ReSwift.Action type is usually implemented in custom value types (structs), though. ReSwift does not suffer from that problems of Redux.js actions. That means by creating a custom action type in ReSwift, the benefit of centralizing action creation in one function goes away. The initializer of your type does provide this already.

That paints a totally different picture.

And that’s probably why ReSwift.Store.ActionCreator passes in the state: to provide any benefit at all. At the cost of a different kind of API than Redux.

Application to the Question

Recall the question:

For example, I have a button, after user pressed it I want to start some process if I’m in state A. So, I have to write an action creator, which will check current state and then return correct action, or not action at all. Then dispatch this action from the same place.

There are a couple of ways to achieve that.

If you have access to the store variable to call dispatch, you also have access to its current state property. You can ask the store for the state the app is in and act accordingly. Usually, you’d write store subscribers to get “push notifications” of store changes, but in cases like this you can also ask the store.

That means the following would be a totally valid implementation:

let currentState = store.state
let action: Action = {
    if currentState.someSubstate == "A" {
        return ActionWhenInStateA()
    } else {
        return ActionWhenNotInStateA()
    }
}
store.dispatch(action)

Since ReSwift Stores are not supposed to receive dispatch commands from different threads, you can rely on the state from line 1 to be the same in the last line, where you dispatch.

TL;DR: you do not need ActionCreator to achieve this. But you can if you like to write “east-oriented” code, leaning on callbacks instead of property queries:

store.dispatch { state, _ in
    if state.someSubstate == "A" {
        return ActionWhenInStateA()
    } else {
        return ActionWhenNotInStateA()
    }
}

Adding RxSwift Ports to Resolve UI Inconsistencies

I am currently struggling to write my user interface layer (Mac) in a way that works well with RxSwift (the reactive library). Meanwhile, the whole app’s state is maintained by ReSwift (the unidirectional data flow library). Both work well together, but I made some weird choices initially.

For example, I’d write Presenters to subscribe to store changes, assemble a static View Model, then pass that on to the View. That’s all nice and imperatively object-oriented.

But then I wrote the actual view controller (which implements the View protocol) to simply update the UI components like I always did. That worked well, it seemed, up until it suddenly began to cause trouble. At the same time, I’d use RxSwift to publish user-initiated changes as events.

Input and output qdhered to different paradigms. In the case of a NSTextField, you can rely on the .rx.text property to produce a change signal when the user types. But when you set the stringValue manually, it won’t. That was a very nice “feature,” by accident!, because now programmatically changing the text field didn’t fire a change event again. Hooray!

Weeks later, I run into problems with edge cases where sometimes the text field should not be updated for some reason, and sometimes where events should not be fired. The mixture of imperative and reactive input VS output got in the way. The solution, in this case, was to use a naive implementation of a reactive view model. I’d keep both the imperative display(string:) input port and the reactive searchTermChange output port around. But instead of relying on the text field as mediator, I’d add the view model as the local source of truth.

  • When the presenter wants to display(...) something, the view controller receives the value and either changes a RxSwift.Variable or call .on(next(...)) for a RxSwift.PublishSubject. Both types are great to pipe changes through, each with a different set of strengths.
  • Either way the value change will be observed and trigger an update to the text field’s contents in all cases that apply.
  • Typing into the text field triggers a searchTermChange, as already mentioned.
  • Text field changes that are identical to the view model, though, will be ignored! This way the binding of view model to text field will not emit a change event.

Ignoring identical values from two sources typically looks like this (in my view controllers):

// Typing is the source stream of itnerest
textField.rx.text
    
    // Combine typed and programmatically changed 
    // values into a single stream
    .withLatestFrom(viewModel) { ($0, $1) }
    
    // Select typed-in values if they are different
    .filter { new, old in
        new != old }
    .map { new, _ in new }
    .distinctUntilChanged()
    
    // Emit change event
    .bindTo(searchTermChange)
    .addDisposableTo(disposeBag)

It’s awkward that I had to add another RxSwift-based property at first, but in the end it made sense to unify input and output so I can combine both kinds of streams and work with them more easily.

Figuring out when an observable emits events and why or how combinations of streams produce results still is very hard. I think I am developing a fist kind of intuition, though, and when the refactoring has proven to be useful, I’ll distill the lessons into blog posts, of course.

ReSwift v4 Released

I totally forgot to bring this up: ReSwift version 4 was released 13 days ago!

The coolest part (in my opinion) is to skip duplicate states in subscriptions if you want to. The state has to be equatable somehow to do this with the default subscription measurements. But then again you’re using this anyway right now with ReSwift 3.x, only for every subscriber.

store.subscribe(subscriber) {
  $0.select {
    ($0.testValue, $0.otherState?.name)
  }.skipRepeats {
    return $0 == $1
  }
}

The skipRepeats in the subscription builder/configuration block will be used automatically if the selected state conforms to Equatable. But you can use your own skipRepeats implementation if needed.

In theory, you could add map and filter and whatnot to the underlying Subscription type that’s being configured in the block you pass to subscribe(), though I don’t see much value in this at the moment.

Drawing Custom Alternating Row Backgrounds in NSTableViews with Swift

This is an old hat in Cocoa: when you change the appearance of NSTableRowViews, they will indeed look different – but the enclosing table view itself will still draw the system default background to fill the space below the last row.

Similarly, when you have scrolling elasticity enabled and scroll above the topmost row, whatever is being drawn there won’t match your custom styled rows, either.

NSTableViews themselves draw the row backgrounds that visually match the NSTableRowView backgrounds, but only in default settings. When you change row backgrounds, you end up with colorful content rows – and regular white & light gray rows above and below the content, filling the gap to the edges of the table’s clip view.

As I said, this is an old hat. But now we work with Swift and all, so I figured I’d share the Swift code I use to draw custom alternating row backgrounds above and below the visible content:

public class CustomTableView: NSTableView {
    
    var alternateBackgroundColor: NSColor = // ...
    
    public override func drawBackground(inClipRect clipRect: NSRect) {

        super.drawBackground(inClipRect: clipRect)

        guard usesAlternatingRowBackgroundColors else { return }

        drawTopAlternatingBackground(inClipRect: clipRect)
        drawBottomAlternatingBackground(inClipRect: clipRect)
    }

    fileprivate func drawTopAlternatingBackground(inClipRect clipRect: NSRect) {

        guard clipRect.origin.y < 0 else { return }

        let backgroundColor = self.backgroundColor
        let alternateColor = self.alternateBackgroundColor

        let rectHeight = rowHeight + intercellSpacing.height
        let minY = NSMinY(clipRect)
        var row = 0

        while true {

            if row % 2 == 0 {
                backgroundColor.setFill()
            } else {
                alternateColor.setFill()
            }

            let rowRect = NSRect(
                x: 0,
                y: (rectHeight * CGFloat(row) - rectHeight),
                width: NSMaxX(clipRect),
                height: rectHeight)
            NSRectFill(rowRect)

            if rowRect.origin.y < minY { break }
            
            row -= 1
        }
    }

    fileprivate func drawBottomAlternatingBackground(inClipRect clipRect: NSRect) {

        let backgroundColor = self.backgroundColor
        let alternateColor = self.alternateBackgroundColor

        let rectHeight = rowHeight + intercellSpacing.height
        let maxY = NSMaxY(clipRect)
        var row = rows(in: clipRect).location

        while true {

            if row % 2 == 1 {
                backgroundColor.setFill()
            } else {
                alternateColor.setFill()
            }

            let rowRect = NSRect(
                x: 0,
                y: (rectHeight * CGFloat(row)),
                width: NSMaxX(clipRect),
                height: rectHeight)
            NSRectFill(rowRect)

            if rowRect.origin.y > maxY { break }
            
            row += 1
        }
    }
}

I don’t attempt to draw row backgrounds in my NSTableView here. I leave that to the NSTableRowView itself. And its color is set using the NSTableViewDelegate.

func tableView(_ tableView: NSTableView, didAdd rowView: NSTableRowView, forRow row: Int) {
    
    guard let tableView = tableView as? CustomTableView else { return }
    
    rowView.backgroundColor = row % 2 == 1
        ? tableView.backgroundColor
        : tableView.alternateBackgroundColor
}

If you don’t just want a solid background but draw custom bezeled borders or something even fancier instead, putting everything into the table view drawing code may pay off because you can leave the drawing code in that single place. Your row views will then have to be transparent.

Browse the archive

Subscribe via RSS