Big Sur Toolbar Items in the Sidebar - macos

In Big Sur, Xcode and Calendar have toolbar items that stay over the sidebar when open but remain visible on the left side when the sidebar's collapsed.
Sidebar open:
Sidebar collapsed:
In "Adopt the New Look of macOS" at 13:55, John says "items placed before the separator [sidebarTrackingSeparator] will appear over the full-height sidebar", just as they are in Xcode and Calendar. I haven't been able to make this work.
Here's a sample project that demonstrates the issue. I used the IB-defined "Window Controller with Sidebar" and added a toolbar item for toggling the sidebar. In a subclass of NSWindowController I insert .sidebarTrackingSeparator after the .toggleSidebar item:
override func windowDidLoad() {
// Sometimes the toolbar items aren't loaded yet--async is a quick and dirty way to prevent a crash
DispatchQueue.main.async {
self.window?.toolbar?.insertItem(withItemIdentifier: .sidebarTrackingSeparator, at: 1)
}
}
Sometimes this has no effect (the toggle button remains to the right of the sidebar). Sometimes the sidebar toggle get put in an overflow menu:
I haven't seen any discussion of implementing this toolbar design outside that WWDC session. Has anyone been able to get this to work?

This is a IB/Code timing disagreement. Interface Builder configures and installs the toolbar before you add the .sidebarTrackingSeparator toolbar item.
So you're doing the right thing, just too late. And too later with the dispatch. I think the important thing is to have the item in there before the toolbar is set on the window.
Unfortunately, that isn't really possible with IB, unless I believe, you create a whole new toolbar and reassign it. But that's a bad idea, because then you may run into trouble auto-saving the state of your toolbar.
The trick is to configure the separator in Interface Builder. If you look at the ObjC documentation for this constant, you'll see a longer name: NSToolbarSidebarTrackingSeparatorItemIdentifier.
The best we can do here is hope that the symbol's name is the same value as the identifier. If you really want to verify this, you can just print the symbol's value in the debugger:
(lldb) po NSToolbarSidebarTrackingSeparatorItemIdentifier
NSToolbarSidebarTrackingSeparatorItemIdentifier
If we create a custom toolbar item in IB, and add that according to John's video...
low and behold:

Related

Toolbar of NSScrollView inside SplitView [duplicate]

It's easy to enable the "inspector bar" for text views so that a bar appears at the top of the screen with various formatting buttons. (Although I had some confusion until I learned to make sure I was selecting the text view in a scroll view, and not the scroll view itself). I can either programmatically use [textView setUsesInspectorBar:YES] or go to the Attributes Inspector and check the "Inspector Bar" box in the "Uses" section.
My question is, how can I further control the inspector bar? I'm having trouble finding information on it in the XCode documentation or online. I'd like to be able to position it in a different place on the screen. Being able to pick and choose which specific controls are in the bar would be great too.
The answer is, you aren't meant to further control the inspector bar. There's nothing in the documentation because, well, there's nothing. Apple's saying, use it or don't use it.
However, if you dig into it a bit, you will find that the inspector bar is a very interesting control. It's not displayed as part of the text view, but rather (privately) embedded in the "window view" itself. When I say "window view," I mean the superview of the content view.
If you list the subviews of that "window view":
NSLog(#"%#", [self.testTextView.window.contentView superview].subviews);
You end up with:
2012-08-02 15:59:30.145 Example[16702:303] (
"<_NSThemeCloseWidget: 0x100523dc0>", // the close button
"<_NSThemeWidget: 0x100525ce0>", // the minimize button?
"<_NSThemeWidget: 0x100524e90>", // the maximize button?
"<NSView: 0x100512ad0>", // the content view
"<__NSInspectorBarView: 0x100529d50>", // the inspector view
"(<NSToolbarView: 0x10054e650>: FD2E0533-AB18-4E7E-905A-AC816CB80A26)" // the toolbar
)
As you can see, AppKit puts the inspector bar at the same level as other top level window controls. Now this is getting into the land of private APIs, but simply tinkering with the "window view" shouldn't get any apps rejected.
You can try to get a reference to the __NSInspectorBarView from here. It seems like it is always the subview right after the content view, so something like this may work:
NSArray *topLevelViews = [self.testTextView.window.contentView superview].subviews;
NSUInteger indexOfContentView = [topLevelViews indexOfObject:self.testTextView.window.contentView];
if (indexOfContentView + 1 < topLevelViews.count) {
NSView *inspectorBar = [topLevelViews objectAtIndex:indexOfContentView + 1];
NSLog(#"%#", inspectorBar);
}
NSLog(#"%#", topLevelViews);
Since this immediately breaks if Apple changes the ordering of the top level views, it may not be a good idea for an application for production. Another idea is:
NSView *inspectorBarView = nil;
for (NSView *topLevelView in topLevelViews) {
if ([topLevelView isKindOfClass:NSClassFromString(#"__NSInspectorBarView")]) {
inspectorBarView = topLevelView;
}
}
NSLog(#"%#", inspectorBarView);
I don't know if the use of NSClassFromString() will pass App Store review guidelines, however, since once again, it's dependent on private APIs.
That being said, once you get a reference to the inspector bar view, things still don't work too well. You can try repositioning it at the bottom:
if (inspectorBarView) {
NSRect newFrame = inspectorBarView.frame;
newFrame.origin = NSZeroPoint;
[inspectorBarView setAutoresizingMask:NSViewMaxYMargin | NSViewMaxXMargin];
[inspectorBarView setFrame:newFrame];
}
But you end up with a misdrawn toolbar, so more work would be necessary there:
My ideas would be to try to shift the content view's height up to cover up the gray left-over area (which would have to be done every time the window is resized, maybe tinkering with autoresizing masks may make it easier) and custom draw a background for the inspector bar at the bottom.
EDIT
Oh, and you should file a feature request for this too. bugreport.apple.com
This is four years late, but I feel like someone on the internet may benefit from this in the future. I spent way too long trying to figure this out.
The inspector bar class, as the others have pointed out, seems to be a private class (__NSInspectorBarView). Therefore, it's probably not recommended to modify.
Nevertheless! The curious have to know. The inspector bar is inserted, at the time of this post (April 2016) into the window's accessory bar. You can get a list of accessory views as of OS X 10.10 using the array property in NSWindow called titlebarAccessoryViewControllers[].
Here's some Swift 2.0 code to do just that, assuming you haven't inserted any other accessory views into the window beforehand.
if window.titlebarAccessoryViewControllers.count > 0 {
let textViewInspectorBar = self.titlebarAccessoryViewControllers[0].view
let inspectorBarHeight: CGFloat = textViewInspectorBar!.frame.height // 26.0 pt
}
It's worth noting that accessory views are handled differently in full screen mode apps: https://developer.apple.com/library/mac/documentation/General/Conceptual/MOSXAppProgrammingGuide/FullScreenApp/FullScreenApp.html
I personally would not attempt to move an accessory view, as they are special kinds of views designed to stay in the toolbar (if I fully understood what I have read).
NSTitlebarAccessoryViewController Reference:
https://developer.apple.com/library/mac/documentation/AppKit/Reference/NSTitlebarAccessoryViewController_Class/
Another 3 years on, but I suspect some will find this useful. My specific problem was in having a window fully filled by a tabView - ideal for setting various kinds of user defaults. Only one of these tab pages had a couple of text views for which I wanted the inspector bar visible. Tabbing to that page made the inspector bar appear, and pushed the whole lot down, ruining my carefully planned layouts. Tabbing away from the page did not hide it again.
The obvious thing was to get the inspector bar to appear on the relevant tab page only. Having got hold of it ("on the shoulders of giants" - thanks to giant Vervious) it is relatively easy to reposition it in the view hierarchy. You are still left with the problem of space for an empty toolbar pushing the content down. The window's view hierarchy changes radically when the inspector bar first appears, and I gave up on trying to do anything with it.
My solution is to increase the content view's height. (Why height and not origin I can't say.)
func tabView(_ tabView: NSTabView, didSelect tabViewItem: NSTabViewItem?) {
if let inspectorBar = window!.titlebarAccessoryViewControllers.first(where:
{$0.view.className == "__NSInspectorBarView"} )?.view {
// move content view back to where it should be
var sz = window!.contentView!.frame.size
sz.height = window!.frame.size.height - 21
window!.contentView?.setFrameSize(sz)
// put the inspector where we want it
inspectorBar.removeFromSuperview()
let y = textPage.frame.size.height - inspectorBar.frame.size.height - 10
inspectorBar.setFrameOrigin(NSPoint(x: 0, y: y))
textPage.subviews.insert(inspectorBar, at: 0)
}
}
The code belongs in a NSTabViewDelegate which I made my window controller conform to, remembering to set the tabView's delegate to File's Owner in the xib, and is called whenever a new tab is selected. textPage is the view inside the relevant tabViewItem.
There are some arbitrary constants found by trial and error. The function only need run once. Repeated calls are harmless, but you could put in a flag to make an early return from subsequent calls.
You cannot do anything to position this thing.
Clearly, the corruption noted by #Vervious is real, but only if you do not have an NSToolBar.
You see, this inspectorBar is sadly a mostly private and mostly (publicly) undocumented but awesome tool. And it is very much intended for use in a window that has an NSToolBar visible... go figure.
After you have a toolbar added to your view
Still with a toolbar but hidden, and inspector bar is cool
(as in via the view menu or the method it invokes, which is toggleToolBarShown: and is an NSResponder friendly message )
So it is obvious, no you cannot do much with this. It's design is poorly documented. It works as intended as a pseudo accessory view bar under the place an NSToolbar goes (which is also not adjustable)

View Controller Gets Smaller When I Copy/Paste It (Xcode 7, Swift 2)

I would like to point out that I am aware of this question: How to copy or duplicate a ViewController in Interface Builder?
and that I followed its instructions.
When I do copy/paste said ViewController, the new "pasted" one is smaller than the original.
The two ViewControllers: One bigger and the other smaller
Another discrepancy between the two is that I can't seem to select the "pasted" ViewController's Navigation Bar to change its title.
What shows up in the Utilities area when I select the Pasted's nav bar instead of the "Title", "Prompt" and "Back Button" I get when I select the original's nav bar: Pasted's nav Bar.
Is there any way to get it to be the same size as it's counterpart and/or let me select it's Navigation Bar so that I can change its title?
Sorry if the question is a bit strange but bear with me please...
So there are two separate issues:
When you copy/pasted that VC, it no longer inherited from a segue its estimated size, so if you had for whatever reason a different size specified for the view it segued from, that is the size for the old VC, but the new one just gets the default size. If this is indeed the case, you can always change the size of the new VC by under Size Inspector, choosing Freeform and specifying it.
To get the navigation bar back, drag a Navigation Item from the Storyboard Object library onto your new VC and you can re-title it (and delete that button you added).

How can I get a two-row toolbar like in Mail.app and Xcode?

I'm trying to add a "second row" after my NSToolbar in my app, that remains part of the title bar. As an example, Mail has a thin gray divider line below the NSToolbar with some extras items below that. Very specifically, when the window is put into fullscreen mode, that second "row" stays attached to the title bar as it slides down under the system menu bar. Xcode has a similar story.
I tried setting my NSWindow to textured and placing my second row controls directly in the content view of the window. While this mostly looks correct in windowed mode, those controls of course won't appear attached to the toolbar when it slides down in fullscreen mode. So how can I achieve the same behavior that Mail and Xcode do? I've looked at a lot of toolbar customization code but none of them really cover this specific case.
fullScreenAccessoryView is deprecated in macOS 10.10
In order to do this in recent versions of macOS, use the addTitlebarAccessoryViewController method on your NSWindow and pass in a subclass of NSTitlebarAccessoryViewController.
For example:
NSTitlebarAccessoryViewController *accessoryViewController = [[NSStoryboard storyboardWithName:#"Main" bundle:nil] instantiateControllerWithIdentifier:#"AccessoryViewController"];
[self.mainWindowController.window addTitlebarAccessoryViewController:accessoryViewController];
What I needed to do was call [NSToolbar setFullScreenAccessoryView:] on the view below my toolbar. This results in the behavior I was aiming for. See the NSToolbar documentation for this method.
First one is normal toolbar. For second toolbar you can create a separate view of your desired height and add it in the main landing-window.

NSTextField with automatic NSNumberFormatter in Interface Builder

I've been making iOS apps for awhile, but I'm trying my hand at MacOS development. I'm adding an NSTextField to my UI and I noticed in Xcode that one of the options in the graphical widgets is "NSTextField with NSNumberFormatter" which implies to me that I'll be able to restrict the input of the field to numbers and configure the formatter in some way.
When I add the NSTextField with NSNumberFormatter to my UI, I can see it has a formatter outlet which appears to be kind of linked to an NSNumberFormatter (although the name is a little grayed out). However, I can't figure out any way to interact with or configure that NSNumberFormatter.
Any help?
To access the NSNumberFormatter, you have to select it in the dock (that list of objects on the left side of the XCode 4 Interface Builder [IB] window).
If the dock isn't in outline view, e.g., it just shows about 4 icons, click the triangle-in-a-square-button at the bottom of the dock. The dock should now show a "Placeholders" section and an "Objects" section; the objects are your UI objects in a hierarchical outline view.
In the IB window, click your NSTextField; that'll highlight the corresponding Text Field Cell in the outline (you may have to twiddle down some disclosure triangles to see it). The Text Field Cell should have a disclosure triangle; twiddle it down to reveal the Number Formatter. Select it, and you should now be able to manipulate it in the Inspector panel.
(There are a lot of things non-obvious like that in XCode. When in doubt, examine your UI object in the Dock's outline view, or prowl the menus with that object selected. It's amazing--and often useful--what you can discover lurking there!
to configure the number formatter, you can ( after you've selected the formatter ) open the Attributes inspector, select the behavior you want and customize the formatter. At least that worked for me in XCode 4.
– moritz

How to access an NSNumberFormatter in a text field cell?

In XCode 3.2.x's Interface Builder this was simple enough, selecting the text cell displayed a small icon for the formatter that could be selected to configure the formatter in the attributes pane.
Now in XCode 4, the formatter icon is gone! If I drag an instance of NSTextField with an NSTextFormatter from the library OR drop and NSTextFormatter into an existing cell there is no way I can figure out to select the formatter to configure it!
Add to this the fact that Core Data entities UI prototype assistant was removed and I would say IB integration into XC4 is half baked at best!
Any luck with this anyone?
you can access the formatter from the jump bar at the top of the window. if you select the component you added the formatter to, click and hold and it will turn into a drop down menu. move to the sub menu and the formatter should show up where you can select it.
it seems that you can treat the whole ui like a cascading set of menus that way.
now removing the formatter, that's a different question...
... which after further looking revealed:
over on the left side of the IB window in xcode4 is a 'dock' containing the icons for file owner, first responder etc. this column is expandable, and when you do so will show a hierarchical view of the nib file. through which you can navigate like the finder.
also, see this article:
http://developer.apple.com/library/mac/#documentation/ToolsLanguages/Conceptual/Xcode4UserGuide/InterfaceBuilder/InterfaceBuilder.html%23//apple_ref/doc/uid/TP40010215-CH6-SW3

Resources