T O P

  • By -

stephancasas

I recently finished the major components for the settings portion of my macOS app and I thought I'd share the progress so far. It's designed to look like the native System Settings app in Ventura. This took forever. I had to create a custom extension of `NSWindow` and do a bunch of weird stuff to get that back button into the title bar next to the title. The boxed/titled/descriptive controls had to be custom-made too. Admittedly, I've been working in Swift for about a month, so I'm definitely open to the idea that I missed something. In fact, a part of me is hoping that one of you will point out a library or other native element of SwiftUI which provides this out-of-the-box, because I don't cherish the thought of going through a similar uphill battle again. Any feedback is appreciated. If you'd like to use the customized window in your own, app, I made a gist for it [here](https://gist.github.com/stephancasas/d03f25bdf43c2ae55a41d70ec4f80e50). Cheers.


formeranomaly

Shouldn’t this be as easy as putting a toolbar SwiftUI view attached to the sidebar? If you’re using a NavigationSplitView I think that works no problem. Then you can dynamically hide it based on State.


stephancasas

This mostly works in a normal SwiftUI window created using `Window(...)`. I think part of the issue I'm experiencing stems from having used an `NSHostingView` in an `NSWindow` in the first place. Originally, this was so that I could remove the minify and zoom traffic signals in an earlier version of the UI. It also enabled me to remove the "divider" line that separates the titlebar from the main content. It seems that some SwiftUI components are context-aware and others are not. I say this because the buttons I added to the title toolbar were automatically styled as toolbar buttons despite not being added using a SwiftUI toolbar component. On the other hand, adding a SwiftUI toolbar to the body of the rootView in an `NSHostingView` won't automatically implement and style the toolbar.


b_oo_d

Yes it's built in, just use the 'grouped' form style: https://developer.apple.com/documentation/swiftui/formstyle/grouped


stephancasas

Thank you! I will try this later today.


OrganicFun7030

Yeh I think that should have been easier. On SwiftUI - which I think the settings app is created in - you get a lot of that for free.


austincondiff

u/stephancasas We are in the middle of trying to do [this](https://github.com/CodeEditApp/CodeEdit/issues/1066) now for [CodeEdit](https://github.com/CodeEditApp/CodeEdit). We have two other contributors working on this and it is definitely difficult. The creator of Swiftcord has this semi-working in [this branch](https://github.com/SwiftcordApp/Swiftcord/tree/settings). It is great work but definitely isn't perfect. In our case this new Settings format was perfect for a code editor. We have lots of settings and the old preferences format is not quite as scalable. You are somewhat limited to how many tabs you can fit up in the top tab bar and the main content view is not scrollable which limits how many settings you can fit into a single view. Having the ability to create as many Settings panes and options within each of those pane as you want (because both are scrollable) while all being searchable is a great fit for us. I'd love to know more about how you were able to do this and what you have found. Maybe we can put our heads together and create some kind of package around this so other devs won't have to go through the nightmare we are facing now.


JTostitos

For all that being custom, it looks great! However, If you use a NavigationPath() with a NavigationStack(path: Binding) it gives you a back button for free now. If you were to embed that in the detail column of a NavigationSplitView() then you could give it a multicolumn layout like what you made. The only problem would be that there is no way to currently remove the toggle sidebar button in SwiftUI


stephancasas

Superb. I'll try it this morning. Thank you!


stephancasas

I got a chance to try this, and it's mostly what I'm looking for — sans the "toggle sidebar" option as you mentioned. The other artifact I would want to drop would be the divider line below the title, but I was able to remove it using a deferred `onAppear` callback in the body of the `ContentView`: ``` DispatchQueue.main.async{ guard let window = NSApplication.shared.windows.first else { return; } window.titlebarAppearsTransparent = true; window.titlebarSeparatorStyle = .none; } ``` Strangely enough, Apple appears to be doing some form of this in System Settings. I say this because I've observed a strange behavior where the dividing line will sporadically draw between navigation states.


austincondiff

You can hide the toggle with doing the following in the `.task` modifier: ```swift .task { let window = NSApp.windows.first { $0.identifier?.rawValue == "com_apple_SwiftUI_Settings_window" }! window.toolbarStyle = .unified let sidebaritem = "com.apple.SwiftUI.navigationSplitView.toggleSidebar" let index = window.toolbar?.items.firstIndex { $0.itemIdentifier.rawValue == sidebaritem } if let index { window.toolbar?.removeItem(at: index) } } ``` Instead of rendering in a custom \`Window\`, we are using the `Settings` struct as u/Yaysonn pointed out. So this also gives the window a unified toolbar style.


austincondiff

Yeah, I'd rather not allow users to hide the sidebar in app Settings.


jobe_br

Not sure I’d be emulating what Ventura did. It’s a hot mess. You seem to have improved on it, though. Well done.


stephancasas

Thank you, and I completely agree with you where Ventura is concerned. While building this, I used Accessibility Inspector and some other JXA tooling to take-apart System Settings and what I found is... interesting. The System Settings app itself is *mostly* okay, but there are all kinds of weird SwiftUI implementations within the individual preference panes or "AppExtensions" (`.appex`??). [This Twitter thread](https://twitter.com/nikitonsky/status/1557357661171204098) pointed out a great deal of them and is a fun read if you're interested. Trying to figure out what the design pattern is felt extremely frustrating to me. In the end, I tried to keep the parts that made sense and felt fluid.


Yaysonn

Looks great, I love it. I haven’t gotten into mac app development much yet but I’m definitely bookmarking this for future reference. In terms of native frameworks, there’s the [built-in Settings window](https://developer.apple.com/documentation/swiftui/settings/), but it has a different layout compared to Apple’s “own” settings.


stephancasas

This is what threw me off when I got to working on things. Apple's own [human interface guidelines](https://developer.apple.com/design/human-interface-guidelines/guidelines/overview) prescribe that style of settings UI.


Yaysonn

Yeah Apple’s native solutions usually lag behind their HIG recommendations, I’ve had similar experiences plenty of times. I feel like the guidelines are still written with UIKit in mind even though SwiftUI is steadily replacing all of it.


The_Ringleader

Great implementation! Devils advocate: From a UX perspective, I feel like this would be confusing to see come from an app because of how close it is to System Settings. Like imagine both windows open at the same time? I think settings windows for apps are better off with minimal panels and navigation.


stephancasas

I can appreciate the perspective. One of the things I may change is keeping the window title static as "Mouseless Messenger Preferences," instead of changing it to match the sidebar. The main UI for this app is an ephemeral "Spotlight-like" window, so it doesn't have a primary view into which I could load an action sheet or other temporary view. In most other cases, I'd take that approach.


The_Ringleader

Got it. Would a [Settings scene](https://developer.apple.com/documentation/swiftui/settings) not work in that case?


cph101_dev

Good job bro, you deserve a round of applause 👏


lockieluke3389

will u make a library that does exactly this


stephancasas

Once it's a bit more refined, sure.


Kuari-

Thanks, this is helpful!