SwiftUI: Toolbar jitters when toggling sidebar (macOS) - macos

When I toggle the sidebar in my macOS app from the toolbar, sometimes the share button on the upper right jitters (see video). This does not happen on every click, so it might take a few tries to reproduce with the example. The app I am working on has much more content, and there it happens more frequently.
I assume this has something to do with using two .toolbar modifiers: When I move the sidebar button to the content area and trigger from there, it seems like the jittering is gone.
Any idea how to fix this? I was not able to recreate this toolbar button layout without using two .toolbar modifiers. Code below video.
struct ContentView: View {
var body: some View {
NavigationView {
Text("Sidebar")
.frame(width: 300)
.toolbar {
sidebarButton
}
Text("Content")
.frame(minWidth: 500)
.toolbar {
shareButton
}
}
.frame(height: 500)
}
var sidebarButton: some View {
Button {
NSApp.keyWindow?.firstResponder?.tryToPerform(
#selector(NSSplitViewController.toggleSidebar(_:)), with: nil
)
} label: {
Image(systemName: "sidebar.left")
}
}
var shareButton: some View {
Button {
print("Share")
} label: {
Image(systemName: "square.and.arrow.up")
}
}
}

Related

Make Notes like collapsing/staying sidebar

In Apple's Notes App, you can drag the sidebar divider left/right and it will make the sidebar 'appear'/'disappear' based on it's width:
How is this achievable? I'm quite new to OSX development and I might simply not have the right terminology to search for this.
Addendum: the apple docs provide info to the API, but is there a more visual docs or kitchen sink like app for learning the API? I've found the api to tell me options (i.e., compact, compactUnified, etc) but doesn't really show them.
Well, you can simply use Navigation to achieve this. Just copy the following code:
struct ContentView: View {
var body: some View {
NavigationView {
Text("Sidebar View")
.padding()
.frame(minWidth: 100)
.toolbar {
ToolbarItem(placement: .navigation) {
Image(systemName: "list.bullet")
.resizable()
}
ToolbarItem(placement: .navigation) {
Image(systemName: "square.grid.2x2")
.resizable()
}
}
Text("Main View")
.padding()
}
}
}
And add some code to your XXXXXApp.swift file:
var body: some Scene {
WindowGroup {
ContentView()
}
.windowToolbarStyle(UnifiedCompactWindowToolbarStyle())
.windowStyle(HiddenTitleBarWindowStyle())
}

Change size of Navigation Bar in SwiftUI

Hello SO community 👋🏼
I'm trying to recreate NavigationBar from Contact tab Apple's Phone in my SwiftUI app.
I played around with .toolbar and modifications but really can't recreate it. I wanna replace TextField to SegmentPicker with saving all behavior of .navigationBarTitleDisplayMode(.inline). But don't know is it possible to use SwiftUI only to get it or need to dive into UIKit. I'm not the expert of UIKit and I will be glad for any help. I want use this NavBar in a exact screen in the app and if possible do not change my preferences of NavBar on other part of app. My code:
import SwiftUI
struct NavBar: View {
#State var pickerOptions: Int = 0
var body: some View {
NavigationView {
ScrollView(.vertical) {
VStack {
Text("Hello, World!")
}
}
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .principal) {
SegmentPicker
}
ToolbarItem(placement: .navigationBarTrailing) {
Image(systemName: "plus")
}
ToolbarItem(placement: .navigationBarLeading) {
Text("Groups")
}
}
}
}
private var SegmentPicker: some View {
VStack {
Text("Contacts").font(.body.weight(.semibold))
Picker("Options", selection: $pickerOptions) {
Text("Cons").tag(0)
Text("Prons").tag(1)
}
.pickerStyle(.segmented)
.frame(width: 200)
}
}
}

Placing the toggle sidebar button on the sidebar toolbar

I have a three column layout macOS application with the first being the sidebar. I have a button that toggles that enabling the user to hide the sidebar.
On Xcode and other macOS, the toggle sidebar button resides on the toolbar on top of the sidebar, and becomes part of the main toolbar when the sidebar is hidden.
For example, open sidebar on Xcode:
And when you hide the sidebar:
I have added the toolbar with the toggle sidebar to the view containing my sidebar, and another toolbar to the second column, but still toggle sidebar appears on the main toolbar, on top of the second column.
Am I missing anything? Here's the code:
// sidebar view, first of three columns
struct ContentView: View {
#State var selection: Set<Int> = [0]
var body: some View {
NavigationView {
List(selection: self.$selection) {
NavigationLink(destination: AllData()) {
Label("All Data", systemImage: "note.text")
}
.tag(0)
Label("Trash", systemImage: "trash")
}
.listStyle(SidebarListStyle())
.toolbar {
ToolbarItem(placement: .navigation) {
Button(action: toggleSidebar, label: {
Image(systemName: "sidebar.left") }).help("Toggle Sidebar")
}
}
}
}
func toggleSidebar() {
NSApp.keyWindow?.firstResponder?.tryToPerform(#selector(NSSplitViewController.toggleSidebar(_:)), with: nil)
}
}
// second column view, with the rest of the toolbar
struct AllData: View {
var body: some View {
NavigationView {
List(filteredData) {
// list items here.
}
.toolbar {
ToolbarItem(placement: .automatic) {
Button("Press Me") {
print("Pressed")
}
}
ToolbarItem(placement: .automatic) {
Button("Press Me too") {
print("Pressed too")
}
}
}
}
}
}
If you use the .frame modifier on the sidebar then you are able to set constraints for minimum width, ideal width and maximum width.
I find a minWidth: 148 ensures the sidebar cannot be collapsed to the point where SwiftUI shuffles the toolbar button off to the trailing edge of the nav toolbar and behind (need to click) the double chevron expander.
So your code might look like this...
...
var body: some View {
NavigationView {
List(selection: self.$selection) {
NavigationLink(destination: AllData()) {
Label("All Data", systemImage: "note.text")
}
.tag(0)
Label("Trash", systemImage: "trash")
}
.listStyle(.sidebar) //<-- from iOS 14 and macOS 10.15
.frame(minWidth: 148, idealWidth: 160, maxWidth: 192, maxHeight: .infinity)
.toolbar {
ToolbarItem(placement: .primaryAction) {
Button(action: {
toggleSidebar
}, label: {
Image(systemName: "sidebar.left")
})
.help("Toggle Sidebar")
}
}
}
}
...
PS. I note that the .primaryAction and .status modifiers both place the sidebar button in a similar position, although I've not tested this thoroughly or completed any research.
try this:
ToolbarItem(placement: .primaryAction) {

Customizing the macOS toolbar with Swift UI

I'm working on a macOS app, with the view layers written in SwiftUI. I know that iOS toolbars can have the background color changed at least, but when I try to do this in macOS, it doesn't behave as I'd expect.
Here's a (simplified) example:
struct ContentView: View {
var body: some View {
NavigationView {
Collections()
.layoutPriority(0)
Photos()
.frame(maxWidth: .infinity, minHeight: 300, maxHeight: .infinity)
.background(Color.Alt.black)
.layoutPriority(1)
}
.toolbar {
Toolbar().background(Color.red500)
}
}
}
struct Toolbar: View {
var body: some View {
Group {
Slider(value: 250, in: 150...400) {
Text("Toolbar.PreviewSize")
} minimumValueLabel: {
Image(systemName: "photo").resizable().scaledToFit().frame(width: 15)
} maximumValueLabel: {
Image(systemName: "photo").resizable().scaledToFit().frame(width: 23)
} onEditingChanged: { _ in
// do nothing
}.frame(minWidth: 200)
Spacer()
Text("Toolbar.SelectionCount")
Spacer()
AddPhotosButton()
}
}
}
Which produces something like this, which as you can see, doesn't apply the background color to the entire toolbar, just to the items in the toolbar:
I'm guessing I could make my own WindowToolbarStyle style, but there's no documentation on the protocol!
If I make my own toolbar as a View rather than with the .toolbar modifier, I can't read the safe area insets for the window traffic buttons when the sidebar is collapsed, resulting in a complete mess:
Thanks for any help!
I recommend
{
.toolbar {
Toolbar()
}.toolbarBackground(Color.gray)
}

SwiftUI: Perpetual Diagnostic Error When Building NavigationBar

I'm new to SwiftUI, and I'm trying to build this nav bar using Xcode 12.4:
Here is the entirety of my view:
struct PreferencesView: View {
var body: some View {
NavigationView {
ZStack {
//Background Color
Color("DosDark")
.edgesIgnoringSafeArea(.all)
Text("Hey.")
//Nav bar styles
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .principal) {
VStack {
Text("Preferences")
.navBarTitleDark()
}
}
}
.navigationBarItems(
leading: NavClose(), //<-- This is where the trouble starts
trailing: NavAbout()
)
}
}
}
}
struct NavClose: View {
var body: some View { //<-- Inexplicable error here
Button(action: {
print("Close...")
}){
Image("close-blue")
}
}
}
struct NavAbout: View {
var body: some View {
Button(action: {
print("Show about stuff...")
}) {
Image("about-blue")
}
}
}
I can get the title to render okay, but as soon as I add the .navigationBarItems bit, I see an error endlessly on my struct that I'm trying to pull in:
When I try putting the Button directly in .navigationBarItems (without using an external struct) I still see the error on that line:
Failed to produce diagnostic for expression; please file a bug report
Am I doing something wrong? Is there a way to make Xcode give me a real error message?
Works fine with Xcode 12.1 / iOS 14.1, but .navigationBarItems was deprecated for the preference of toolbar and probably you have newer version where they are already conflicted.
The solution is to use only toolbar with corresponding placements, like
.toolbar {
ToolbarItem(placement: .navigationBarLeading) {
NavClose()
}
ToolbarItem(placement: .navigationBarTrailing) {
NavAbout()
}
ToolbarItem(placement: .principal) {
VStack {
Text("Preferences")
.navBarTitleDark()
}
}
}

Resources