user4992124
user4992124

Reputation: 1594

How to add `toggleSidebar` NSToolbarItem in Catalyst?

In my app, I added a toggleSidebar item to the NSToolbar.

#if targetEnvironment(macCatalyst)
extension SceneDelegate: NSToolbarDelegate {
    func toolbarDefaultItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] {
        return [NSToolbarItem.Identifier.toggleSidebar, NSToolbarItem.Identifier.flexibleSpace, AddRestaurantButtonToolbarIdentifier]
    }
}
#endif

However, when I compile my app to Catalyst, the button is disabled. Does anybody know what else I need to do to hook it up?

Upvotes: 3

Views: 1698

Answers (5)

Jay Lieske
Jay Lieske

Reputation: 4858

The easiest way to use the toggleSidebar toolbar item is to set primaryBackgroundStyle to .sidebar, as answered by @Craig Scrogie. That has the side effect of enabling the toolbar item and hiding/showing the sidebar.

If you don't want to use the .sidebar background style, you have to implement toggling the sidebar and validating the toolbar item in methods on a class in your responder chain. I put these in a subclass of UISplitViewController.

@objc func toggleSidebar(_ sender: Any?) {
  UIView.animate(withDuration: 0.2, animations: {
    self.preferredDisplayMode =
      (self.displayMode == .secondaryOnly) ?
        .oneBesideSecondary : .secondaryOnly
  })
}

@objc func validateToolbarItem(_ item: NSToolbarItem)
    -> Bool {
  if item.action == #selector(toggleSidebar) {
    return true
  }
  return false
}

Upvotes: 0

Craig Scrogie
Craig Scrogie

Reputation: 11

When configuring your UISplitViewController, set the primaryBackgroundStyle to .sidebar

let splitVC: UISplitViewController = //your application's split view controller
splitVC.primaryBackgroundStyle = .sidebar

This will enable your NSToolbarItem with the system identifier .toggleSidebar and it will work automatically with the UISplitViewController in Mac Catalyst without setting any target / action code.

Upvotes: 1

Hassan ElDesouky
Hassan ElDesouky

Reputation: 334

This answer is mainly converting @malhal's answer to the latest Swift version

  1. You will need to return [.toggleSidebar] in toolbarDefaultItemIdentifiers.
  2. In toolbarWillAddItem you will write the following (just like the previous answer suggested):
  func toolbarWillAddItem(_ notification: Notification) {
    let userInfo = notification.userInfo!
    if let addedItem = userInfo["item"] as? NSToolbarItem {
      let itemIdentifier = addedItem.itemIdentifier
      if itemIdentifier == .toggleSidebar {
        addedItem.target = self
        addedItem.action = #selector(toggleSidebar)
      }
    }
  }
  1. Finally, you will add your toggleSidebar method.
  @objc func toggleSidebar() {
    let splitController = self.window?.rootViewController as? MainSplitController
    UIView.animate(withDuration: 0.2) {
      splitController?.preferredDisplayMode = (splitController?.preferredDisplayMode != .primaryHidden ? .primaryHidden : .allVisible)
    }
  }

A few resources that might help:

  1. Integrating a Toolbar and Touch Bar into Your App
  2. Mac Catalyst: Adding a Toolbar

Upvotes: 0

malhal
malhal

Reputation: 30746

The target needs changed to self, this is shown in this Apple sample where it is done for the print item but can easily be changed to the toggle split item as I did after the comment.

/** This is an optional delegate function, called when a new item is about to be added to the toolbar.
    This is a good spot to set up initial state information for toolbar items, particularly items
    that you don't directly control yourself (like with NSToolbarPrintItemIdentifier).
    The notification's object is the toolbar, and the "item" key in the userInfo is the toolbar item
    being added.
 */
func toolbarWillAddItem(_ notification: Notification) {
    let userInfo = notification.userInfo!
    if let addedItem = userInfo["item"] as? NSToolbarItem {
        let itemIdentifier = addedItem.itemIdentifier
        if itemIdentifier == .print {
            addedItem.toolTip = NSLocalizedString("print string", comment: "")
            addedItem.target = self
        }
        // added code
        else if itemIdentifier == .toggleSidebar {
            addedItem.target = self
        }
    }
}

And then add the action to the scene delegate by adding the Swift equivalent of this:

- (IBAction)toggleSidebar:(id)sender{
    UISplitViewController *splitViewController = (UISplitViewController *)self.window.rootViewController;
    [UIView animateWithDuration:0.2 animations:^{
        splitViewController.preferredDisplayMode = (splitViewController.preferredDisplayMode != UISplitViewControllerDisplayModePrimaryHidden ? UISplitViewControllerDisplayModePrimaryHidden : UISplitViewControllerDisplayModeAllVisible);
    }];
}

Upvotes: 1

rmaddy
rmaddy

Reputation: 318924

If you look at the documentation for .toggleSidebar/NSToolbarToggleSidebarItemIdentifier you will see:

The standard toolbar item identifier for a sidebar. It sends toggleSidebar: to firstResponder.

Adding that method to your view controller will enable the button in the toolbar:

Swift:

@objc func toggleSidebar(_ sender: Any) {
}

Objective-C:

- (void)toggleSidebar:(id)sender {
}

Your implementation will need to do whatever you want to do when the user taps the button in the toolbar.

Normally, under a real macOS app using an NSSplitViewController, this method is handled automatically by the split view controller and you don't need to add your own implementation of toggleSidebar:.

Upvotes: 1

Related Questions