Highlight Navigation View At Start - macos

When I start my app, the start page is "Kunde" but the whole thing is not highlighted in blue in the navigation. It just turns blue (system color) when I click on it.
I want it to be highlighted blue when I open the app.
import SwiftUI
struct ContentView: View {
var body: some View {
NavigationView {
List {
NavigationLink(destination: ListView()) {
Text("Kunde")
}
}
ListView()
}
}
}
struct ListView: View {
var body: some View {
Text("Hello.")
}
}

you could try something like this approach:
struct ContentView: View {
#State var selection: String?
#State var listData = ["Kunde", "xxxx", "zzzz"]
var body: some View {
NavigationView {
List(listData, id: \.self) { item in
NavigationLink(destination: ListView()) {
Text(item)
}
.listRowBackground(selection == item ? Color.blue : Color.clear)
}
}
.onAppear {
selection = "Kunde"
}
}
}

Related

SwiftUI: animating tab item addition/removal in tab bar

In my app I add/remove a subview to/from a TabView based on some condition. I'd like to animate tab item addition/removal in tab bar. My experiment (see code below) shows it's not working. I read on the net that TabView support for animation is quite limited and some people rolled their own implementation. But just in case, is it possible to implement it?
import SwiftUI
struct ContentView: View {
#State var showBoth: Bool = false
var body: some View {
TabView {
Button("Test") {
withAnimation {
showBoth.toggle()
}
}
.tabItem {
Label("1", systemImage: "1.circle")
}
if showBoth {
Text("2")
.tabItem {
Label("2", systemImage: "2.circle")
}
.transition(.slide)
}
}
}
}
Note: moving transition() call to the Label passed to tabItem() doesn't work either.
As commented Apple wants the TabBar to stay unchanged throughout the App.
But you can simply implement your own Tabbar with full control:
struct ContentView: View {
#State private var currentTab = "One"
#State var showBoth: Bool = false
var body: some View {
VStack {
TabView(selection: $currentTab) {
// Tab 1.
VStack {
Button("Toggle 2. Tab") {
withAnimation {
showBoth.toggle()
}
}
} .tag("One")
// Tab 2.
VStack {
Text("Two")
} .tag("Two")
}
// custom Tabbar buttons
Divider()
HStack {
OwnTabBarButton("One", imageName: "1.circle")
if showBoth {
OwnTabBarButton("Two", imageName: "2.circle")
.transition(.scale)
}
}
}
}
func OwnTabBarButton(_ label: String, imageName: String) -> some View {
Button {
currentTab = label
} label: {
VStack {
Image(systemName: imageName)
Text(label)
}
}
.padding([.horizontal,.top])
}
}

Calling a sheet from a menu item

I have a macOS app that has to display a small dialog with some information when the user presses the menu item "Info".
I've tried calling doing this with a .sheet but can't get it to display the sheet. Code:
#main
struct The_ThingApp: App {
private let dataModel = DataModel()
#State var showsAlert = false
#State private var isShowingSheet = false
var body: some Scene {
WindowGroup {
ContentView()
.environmentObject(self.dataModel)
}
.commands {
CommandMenu("Info") {
Button("Get Info") {
print("getting info")
isShowingSheet.toggle()
}
.sheet(isPresented: $isShowingSheet) {
VStack {
Text("Some stuff to be shown")
.font(.title)
.padding(50)
Button("Dismiss",
action: { isShowingSheet.toggle() })
}
}
}
}
}
}
How would I display a sheet from a menu item?
However, if a sheet is not the way to do it (I think given the simplicity of what I need to show, it would be it), how would you suggest I do it? I tried creating a new view, like I did with the preferences window, but I can't call it either from the menu.
put the sheet directly on ContentView:
#main
struct The_ThingApp: App {
#State private var isShowingSheet = false
var body: some Scene {
WindowGroup {
ContentView()
// here VV
.sheet(isPresented: $isShowingSheet) {
VStack {
Text("Some stuff to be shown")
.font(.title)
.padding(50)
Button("Dismiss",
action: { isShowingSheet.toggle() })
}
}
}
.commands {
CommandMenu("Info") {
Button("Get Info") {
print("getting info")
isShowingSheet.toggle()
}
}
}
}
}

Enabling and Disabling CommandGroup Menu Items

I have a very simple sample macOS application with one custom menu command just in order to test my ideas as follows.
import SwiftUI
#main
struct MenuMonsterMacApp: App {
#State var fileOpenEnabled: Bool = true
var body: some Scene {
WindowGroup {
ContentView()
.frame(width: 480.0, height: 320.0)
}.commands {
CommandGroup(after: .newItem) {
Button {
print("Open file, will you?")
} label: {
Text("Open...")
}
.keyboardShortcut("O")
.disabled(false)
}
}
}
}
And I want to enable and disable this command with a click of a button that is placed in ContentView. So I've created an ObservableObject class to observe the boolean value of the File Open command as follows.
import SwiftUI
#main
struct MenuMonsterMacApp: App {
#ObservedObject var menuObservable = MenuObservable()
#State var fileOpenEnabled: Bool = true
var body: some Scene {
WindowGroup {
ContentView()
.frame(width: 480.0, height: 320.0)
}.commands {
CommandGroup(after: .newItem) {
Button {
print("Open file, will you?")
} label: {
Text("Open...")
}
.keyboardShortcut("O")
.disabled(!fileOpenEnabled)
}
}.onChange(of: menuObservable.fileOpen) { newValue in
fileOpenEnabled = newValue
}
}
}
class MenuObservable: ObservableObject {
#Published var fileOpen: Bool = true
}
In my ContentView, which virtually runs the show, I have the following.
import SwiftUI
struct ContentView: View {
#StateObject var menuObservable = MenuObservable()
var body: some View {
VStack {
Button {
menuObservable.fileOpen.toggle()
} label: {
Text("Click to disable 'File Open'")
}
}
}
}
If I click on the button, the boolean status of the menu command in question won't change. Is this a wrong approach? If it is, how can enable and disable the menu command from ContentView? Thanks.
To enable and disable the command with a click of a button that is placed in ContentView,
try the following approach, using passing environmentObject and a separate View for the
menu Button.
import SwiftUI
#main
struct MenuMonsterMacApp: App {
#StateObject var menuObservable = MenuObservable()
var body: some Scene {
WindowGroup {
ContentView().environmentObject(menuObservable)
.frame(width: 480.0, height: 320.0)
}.commands {
CommandGroup(after: .newItem) {
OpenCommand().environmentObject(menuObservable)
}
}
}
}
struct OpenCommand: View {
#EnvironmentObject var menuObservable: MenuObservable
var body: some View {
Button {
print("Open file, will you?")
} label: {
Text("Open...")
}
.disabled(!menuObservable.fileOpen)
.keyboardShortcut("O")
}
}
class MenuObservable: ObservableObject {
#Published var fileOpen: Bool = true
}
struct ContentView: View {
#EnvironmentObject var menuObservable: MenuObservable
var body: some View {
VStack {
Button {
menuObservable.fileOpen.toggle()
} label: {
Text("Click to disable 'File Open'")
}
}
}
}

SwiftUI List how to identify what item is selected on macOS

Here is what I have based on this answer. The code currently allows the user to select a cell but I cannot distinguish which cell is selected or execute any code in response to the selection. In summary, how can I execute code based on the selected cell's name and execute on click. The cell currently highlights in blue where clicked, but I want to identify it and act accordingly based on that selection. Note: I am not looking to select the cell in editing mode. Also, how can I programmatically select a cell without click?
struct OtherView: View {
#State var list: [String]
#State var selectKeeper = Set<String>()
var body: some View {
NavigationView {
List(list, id: \.self, selection: $selectKeeper) { item in
Text(item)
}
}
}
}
Here is a gif demoing the selection
I found a workaround, but the text itself has to be clicked- clicking the cell does nothing:
struct OtherView: View {
#State var list: [String]
#State var selectKeeper = Set<String>()
var body: some View {
NavigationView {
List(list, id: \.self, selection: $selectKeeper) { item in
Text(item)
.onTapGesture {
print(item)
}
}
}
}
}
List selection works in Edit mode, so here is some demo of selection usage
struct OtherView: View {
#State var list: [String] = ["Phil Swanson", "Karen Gibbons", "Grant Kilman", "Wanda Green"]
#State var selectKeeper = Set<String>()
var body: some View {
NavigationView {
List(list, id: \.self, selection: $selectKeeper) { item in
if self.selectKeeper.contains(item) {
Text(item).bold()
} else {
Text(item)
}
}.navigationBarItems(trailing: HStack {
if self.selectKeeper.count != 0 {
Button("Send") {
print("Sending selected... \(self.selectKeeper)")
}
}
EditButton()
})
}
}
}
To spare you the labour pains:
import SwiftUI
struct ContentView: View {
#State private var selection: String?
let list: [String] = ["First", "Second", "Third"]
var body: some View {
NavigationView {
HStack {
List(selection: $selection) {
ForEach(list, id: \.self) { item in
VStack {
Text(item)
}
}
}
TextField("Option", text: Binding(self.$selection) ?? .constant(""))
}
.frame(minWidth: 100, maxWidth: .infinity, minHeight: 100, maxHeight: .infinity)
}
}
}
This solution deals with the problem #Isaac addressed.
screenshot
my 2 cents for custom selection and actions
works in swift 5.1 / iOS14:
see:
https://gist.github.com/ingconti/49497419e5edd84a5f3be52397c76eb4
I left a lot of debug "print".. to better understand.
You can react to the selected item by using onChange on your selection variable.
You can set the selection manually by simply setting the variable. This will also trigger onChange.
Here's some example code that will run directly in Playgrounds:
import SwiftUI
import PlaygroundSupport
struct OtherView: View {
#State var list: [String] = ["a", "b", "c"]
#State var selection: String?
var body: some View {
NavigationView {
VStack {
List(list, id: \.self, selection: $selection) { item in
Text(item)
}
.onChange(of: selection) {s in
// React to changes here
print(s)
}
Button("Select b") {
// Example of setting selection programmatically
selection = "b"
}
}
}
}
}
let view = OtherView()
PlaygroundPage.current.setLiveView(view)

Transition from one view to another SwiftUI

How can I transition from one View to another? Is there push / pop like in UIKit? Do I have to use a NavigationView and if so how?
Using a NavigationView
NavigationViews are tied to NavigationButton (AFAIK that is the only way to trigger a segue). Here is a simple example where the main view can transition to a detail view.
struct DetailView : View {
let value: String
var body : some View {
Text("Full View: \(value)")
}
}
struct MainView : View {
var body : some View {
NavigationView {
NavigationButton(destination: DetailView(value: "Hello World"),
label: { Text("Click Me") })
}
}
}
This will automatically handle transitions and add a back button.
Using State
Another approach is to use a stateful variable to determine if the child view is displayed. Here is another simple example:
struct DetailView : View {
let value: String
let onDone: () -> Void
var body : some View {
VStack {
Text("Full View: \(value)")
Button(action: onDone, label: { Text("Back") })
}
}
}
struct MainView : View {
#State var displaySubview = false
var body : some View {
VStack {
if displaySubview {
DetailView(value: "Hello World") {
self.displaySubview = false
}
} else {
Button(action: {
self.displaySubview = true
}, label: { Text("Click Me") })
}
}
}
}
This approach requires you to implement more of the UI elements, but it also allows for more control over views.
It works for me
struct ContentView : View {
var body : some View {
NavigationView {
NavigationLink(destination: DetailView(), label: { Text("To Detail")})
}
}}

Resources