Changing Selected Tab in TabViewController Programmatically

2.1k views Asked by At

I have an OS Mac app with two buttons and a Container View in my top level View Controller. I replaced the view controller linked to the Container View with a Tab View Controller. I would like to switch tabs using the buttons in the top level View Controller. The only way I've got it close to working is setting up the button actions as follows:

@IBAction func selectCompositions(_ sender: NSButton) {
  let tabViewController = self.storyboard?.instantiateController(withIdentifier: "tabViewController") as!NSTabViewController
  tabViewController.selectedTabViewItemIndex = 2
  let count = tabViewController.selectedTabViewItemIndex
  print(count)
}

The instantiate call appears to work because, when I added a third tab, the count changed from 1 to 2. This was before I added the instruction to set it. At any rate, the code doesn't change the tab. Instantiating the controller doesn't seem right because the view appears without clicking the button. Is the call only intended to return a link to it and not instantiate a new object? Is there a more straightforward way to do it?

I tried the answer from this question: Switch tab bar programmatically in Swift. It generated an error saying it tabViewController was unresolved. I have set the storyboard id of the TabViewController to tabViewController.

Update

I added:

self.selectedTabViewItemIndex = 0

to the viewDidLoad method in a class I created and linked to the TabViewController. When I run the app, the index is changed, but the last tab (2) is still shown. When I click on tab 0 in the window, the tab changes, but the display of the index remains the same. It seems like I may be calling the wrong method although none to others seem appropriate.

Progress

I changed my NSTabViewController class to:

class TabViewController: NSTabViewController {

  override func viewDidLoad() {
    super.viewDidLoad()
    // Do view setup here.
    print("In TabViewController")
    switchToDataTab()
  }

  func switchToDataTab() {
    Timer.scheduledTimer(timeInterval: 0.2, target: self, selector: #selector(switchToDataTabCont), userInfo: nil, repeats: false)
  }  

  func switchToDataTabCont(){
    self.selectedTabViewItemIndex = 2
  }
}

This is an answer from the solution linked above. It works fine in the controller linked to the control, but not in the main controller button action methods. It makes me think I've instantiated a second instance of the class.

2

There are 2 answers

1
curt On BEST ANSWER

Adding the timer fixed the immediate problem of switching tabs. I solved getting the buttons to do the switching by using a slightly hacked version of the Delegation pattern.

The code is shown in my my question about getting it to work: Obtaining a View Controller Reference

0
schmidt9 On

This is how I solved tabs switching (and (re)storation)

import Foundation
import Cocoa

class TabViewController: NSTabViewController {

    private var isLoading = false

    /// Use with Selected Index binding of NSTabView
    /// because setting binding direct to UserDefaults does not work
    @objc var selectedTabIndex: Int = 0 {
        didSet {
            if !isLoading {
                UserDefaults.standard.set(selectedTabIndex, forKey: "selectedTabIndex")
            }
        }
    }

    required init?(coder: NSCoder) {
        super.init(coder: coder)
        // set flag before selectedTabIndex binding fires to prevent setting overwrite
        isLoading = true
    }

    override func viewWillAppear() {
        super.viewWillAppear()

        if isLoading {
            selectedTabViewItemIndex = UserDefaults.standard.integer(forKey: "selectedTabIndex")
            isLoading = false
        }
    }
}