How to disable lazy loading in NSTabViewController?

431 views Asked by At

I am designing a SwiftUI wrapper for NSTabViewController with the toolbar style. I want it to be a drop-in replacement for TabView. TabView uses a modifier tabItem(_:) to specify the tab name and icon. So I designed a similar modifier for my own ToolbarTabView:

extension View {
    func toolbarTabItem(_ label: LocalizedStringKey, nsImage: NSImage? = nil, tooltip: LocalizedStringKey? = nil) -> some View {
        self.preference(key: ToolbarTabItemPreferenceKey.self, value: ToolbarTabItemPreference(label: label, nsImage: nsImage, tooltip: tooltip))
    }
}

I wrap each View in a NSHostingController and create a NSTabViewItem. Then I use onPreferenceChange to set the NSTabViewItem's label and image property. Finally, I have a NSViewControllerRepresentable to pass my array of NSTabViewItem to a NSTabViewController. This all works well except for the following issue.

By design NSTabViewController will only load its first tab. This loads the first NSHostingController which lays out the first View. That calls onPreferenceChange and sets the label for the first tab. However, the remaining tabs are not loaded and therefore the label remains unset.

I know that I can re-design my APIs to pass in the labels and images explicitly and that works, but then how does Apple implement their TabView? They must have the same issue with the views being lazy loaded because the macOS implementation of TabView looks like NSTabViewController.

I think a workaround would be to force all the tabs to load, which is the title of this question, but I am open to other ideas as well.

Reference:

https://github.com/utmapp/UTM/blob/dev/Platform/macOS/ToolbarTabView.swift

https://github.com/utmapp/UTM/blob/dev/Platform/macOS/ToolbarTabViewController.swift

1

There are 1 answers

0
osy On

Here is the dumb workaround I came up with

public class UTMTabViewController: NSTabViewController {
    public override func viewDidAppear() {
        super.viewDidAppear()
        for i in self.tabViewItems.indices {
            self.selectedTabViewItemIndex = i
        }
        self.selectedTabViewItemIndex = 0
    }
}

Basically I force load every tab once the view appears. I really hope there's a better answer than this but I'll leave it here just in case.