I'm using a NavigationSplitView with two columns for a macOS app. The sidebar column shows a list of items and the right pane shows the details for the selected item. I'm using NavigationLink to create clickable sidebar items and pass my own View-type struct that displays the row information.
When I decided to add a slight background color to the rows & slight hover state, I noticed that the row view container is smaller than the NavigationLink when selected (I made the colors brighter to emphasize the issue - rows have a gray background & brighter gray hover state and blue is the built-in styling provided by NavigationLink).
My code for the NavigationSplitView which lives in the body of the main ContentView:
NavigationSplitView(columnVisibility: $columnVisibility) {
VStack{
if userSettings.cases.isEmpty {
Text("Press + to add your first case")
.font(.title2)
.foregroundColor(.gray)
.multilineTextAlignment(.center)
} else {
List(userSettings.cases, selection: self.$selectedCase) { caseData in
NavigationLink(value: caseData) {
CaseRow(caseData: caseData, deleteAction: { caseToDelete in
self.caseToDelete = caseToDelete
})
}
}
}
}
.frame(minWidth: 350, idealWidth: 500)
.sheet(isPresented: $showingAddCaseView) {
AddCaseView { caseName, caseId in
let newCase = Case(id: UUID(), name: caseName, caseId: caseId, caseStatus: "Checking...")
userSettings.cases.append(newCase)
updateCaseStatus(newCase)
showingAddCaseView = false
}
}
.alert(item: $caseToDelete) { caseToDelete in
Alert(title: Text("Delete Case"),
message: Text("Are you sure you want to delete this case?"),
primaryButton: .destructive(Text("Yes, Delete")) {
if let index = userSettings.cases.firstIndex(where: { $0.id == caseToDelete.id }) {
userSettings.cases.remove(at: index)
}
},
secondaryButton: .cancel())
}
.toolbar {
ToolbarItem(placement: .primaryAction) {
Button(action: {
showingAddCaseView = true
}) {
Image(systemName: "plus")
}
}
}
} detail: {
CaseDetailView(caseData: selectedCase)
.frame(minWidth: 100, maxWidth: .infinity, maxHeight: .infinity)
.background(Color(NSColor.textBackgroundColor))
.navigationTitle(selectedCase?.caseId ?? "No Case Selected")
}
.onAppear() {
columnVisibility = .all
}
}
Code for CaseRow.swift:
import SwiftUI
struct CaseRow: View {
let caseData: Case
let deleteAction: (Case) -> Void
var body: some View {
HStack {
VStack(alignment: .leading) {
Text(caseData.name)
.font(.headline)
Text(caseData.caseId)
.font(.subheadline)
}
Spacer()
Text(caseData.caseStatus)
Button(action: {
deleteAction(caseData)
}) {
Image(systemName: "trash")
.foregroundColor(.red)
}
}
.padding()
.padding(.horizontal, 4)
.textSelection(.enabled)
.rowBackground()
}
}
Code for RowModifier.swift (which applies the background to the row & changes it on hover):
import SwiftUI
struct RowModifier: ViewModifier {
@State private var isHovered = false
func body(content: Content) -> some View {
content
.background(Color.gray.opacity(isHovered ? 0.50 : 0.05))
.cornerRadius(8)
.onHover { hover in
withAnimation(.easeInOut(duration: 0.15)) {
isHovered = hover
}
}
}
}
extension View {
func rowBackground() -> some View {
self.modifier(RowModifier())
}
}
I tried using .buttonStyle(PlainButtonStyle()) or .buttonStyle(.plain) for the NavigationLink but it doesn't do anything. I did notice that if I apply .buttonStyle(PlainButtonStyle()), the buttons inside the row get new styling, but not the entire row.
I tried modifying my custom cell styling (RowModifier.swift) with different padding - didn't help either.
Another sign that something is off with my NavigationLink implementation is that I can't change the accent color from system blue. I tried using .accentColor(.green) and .tint(.green) on the content inside the sidebar and on the entire NavigationSplitView, but neither changed the row selection color - it stays system blue.
I tried creating a custom NavigationLink and applying the background color & hover color to it directly, but still unable to control the selected state in any way.
I can't find any way to control the system blue styling and its sizing. I just want the row view containers to be the same size in the selected & unselected states so it doesn't look like there's one cell inside another.
I know this could be accomplished by tracking the selected state through bindings, but I'm hoping to use as much of native NavigationSplitView & NavigationLink functionality as it can be ported to iOS later.
Post
Replies
Boosts
Views
Activity
I'm adding some sort options to my macOS app that shows a list of stuff.
I'm using a Menu inside .toolbar { ToolbarItemGroup() {} } like so:
Menu {
ForEach(SortOption.allCases, id: .self) { option in
Toggle(isOn: Binding(
get: { userSettings.sortOption == option },
set: { newValue in
if newValue {
userSettings.sortOption = option
}
})) {
Text(option.description)
}
}
}
label: {
Image(systemName: "arrow.up.arrow.down")
}
.menuIndicator(.hidden)
Here's the result:
As you can see, the hover state / container of the label image seems to be cut off. I suspect that it's caused by .menuIndicator(.hidden) which I'm using to remove the default chevron that shows up next to the icon (it looks ugly and is too close to the icon:
).
Is it possible to remove the menu indicator while leaving the hover state container intact?
I have a simple settings view for my MacOS app with a segmented picker for setting the light/dark/automatic(system) mode. It works just fine when switching between the dark and light modes, but when I set it to system (.preferredColorScheme(nil)), it looks like it initially colors half the app light and half dark. If I click again or click away, it all sets to the proper system mode. But it looks broken on initial selection:
Here's the picker from my settings view:
Picker("",selection: $userSettings.appearance) {
Text("Automatic").tag(0)
Text("Dark").tag(1)
Text("Light").tag(2)
}
.pickerStyle(SegmentedPickerStyle())
.labelsHidden()
It sets a value in my UserSettings:
class UserSettings: ObservableObject {
@AppStorage("appearance") var appearance: Int = 0
var selectedColorScheme: ColorScheme? {
switch appearance {
case 1:
return .dark
case 2:
return .light
default:
return nil
}
}
//other stuff
}
And then I set the actual mode on the outer-most view in my ContentView:
struct ContentView: View {
@StateObject private var userSettings: UserSettings
var body: some View {
NavigationSplitView {
//stuff
} detail: {
//stuff
}
.preferredColorScheme(userSettings.selectedColorScheme)
}
I've also tried to set the .preferredColorScheme in the main app struct but it doesn't make a difference.
From the last comment in this thread, I also got a sense that this could be a SwiftUI bug.
It all started an hour ago when I tried to download the Network Link Conditioner (I need to test the macOS application I'm developing with a slower network connection). I went to XCode > Open Developer Tool > More Developer Tool. It opens a Sing In page for apple developer. When I sign in with my account, it just refreshes the page and asks me to Sign In all over, stuck in a loop.
Otherwise, I'm able to Sign In to all the other parts of the developer website, I have an active membership, all license agreements are up to date. Apple's status site shows everything operational.
What I tried:
Clearing cookies & cache
Different browsers (including Safari)
Updating & restarting everything
Navigating from https://developer.apple.com/download/ to "More" tab on the website instead of going through XCode
Signing in with a passkey instead of a password
Tried using a VPN
Going to the Developer Support page (https://developer.apple.com/support/) and trying to press "Get Started" to reach out to support by email or phone, but that asks me to Sign In and puts me into another sign in loop.
I have no idea what to do and I need the tool to test my app. Apple is providing no informative errors here to even figure out what could be wrong...