Swiftui connect command to value in main view - swift

Does someone know how to connect commands to the rest of the project?
For example: I want to toggle the AddNew variable in the content view to show the add new item sheet by using the command.
struct SampleApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
.commands {
CommandGroup(after: CommandGroupPlacement.newItem) {
Button("Add new", action: {
self.AddNew.toggle() // should toggle variable in content View
})
}
}
}
}
struct ContentView: View {
#State var AddNew = false
var body: some View {
Button(action: {
self.AddNew.toggle()
}) {
Text("Show Detail")
}.sheet(isPresented: $AddNew) {
AddNew(dimiss: $AddNew)
}
}
}

A solution could be to have a #Published var in a class conforming to ObservableObject.
You would toggle the boolean in the class and access it from wherever you want (as an #EnvironmentObject for example).
Like this:
class AppModel: ObservableObject {
#Published var addNew: Bool = false
}
struct SampleApp: App {
#ObservedObject var model = AppModel()
var body: some Scene {
WindowGroup {
ContentView()
.environmentObject(model)
}
.commands {
CommandGroup(after: CommandGroupPlacement.newItem) {
Button("Add new", action: {
self.model.addNew.toggle()
})
}
}
}
}
struct ContentView: View {
#EnvironmentObject var model: AppModel
var body: some View {
Button(action: {
self.model.addNew.toggle()
}) {
Text("Show Detail")
}.sheet(isPresented: $model.addNew) {
AddNew(dimiss: $model.addNew)
}
}
}

Related

ConfirmationDialog cancel bug in swiftui

When I jump to the settings page and click Cancel in the pop-up dialog box, it will automatically return to the home page. How can I avoid this problem?
import SwiftUI
struct ContentView: View {
#State private var settingActive: Bool = false
var body: some View {
NavigationView {
TabView {
VStack {
NavigationLink(destination: SettingView(), isActive: $settingActive) {
EmptyView()
}
Button {
settingActive.toggle()
} label: {
Text("Setting")
}
}
.padding()
}
}
}
}
struct SettingView: View {
#State private var logoutActive: Bool = false
var body: some View {
VStack {
Button {
logoutActive.toggle()
} label: {
Text("Logout")
}
.confirmationDialog("Logout", isPresented: $logoutActive) {
Button("Logout", role: .destructive) {
}
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
This seems to be an issue with using TabView inside NavigationView.
You can solve this by moving TabView to be your top level object (where it really should be), or replacing NavigationView with the new NavigationStack.
Here's an implementation that also removes the deprecated NavigationLink method:
enum Router {
case settings
}
struct ContentView: View {
#State private var path = NavigationPath()
var body: some View {
TabView {
NavigationStack(path: $path) {
VStack {
Button {
path.append(Router.settings)
} label: {
Text("Setting")
}
}
.navigationDestination(for: Router.self) { router in
switch router {
case .settings:
SettingView(path: $path)
}
}
.padding()
}
}
}
}
struct SettingView: View {
#Binding var path: NavigationPath
#State private var logoutActive: Bool = false
var body: some View {
VStack {
Button {
logoutActive = true
} label: {
Text("Logout")
}
.confirmationDialog("Logout", isPresented: $logoutActive) {
Button("Logout", role: .destructive) {
}
}
}
}
}

Save onboarding in storage using Swift

So I need to utilize app storage to save the state of onboarding, but I can't seem to figure it out with a #Published variable which I have, so I wanted to reach out and see if anyone know what I can do to switch things up.
So here is the code:
class MainViewModel: ObservableObject {
#Published var showOnboarding = true
}
#main
struct MapGlider: App {
#StateObject private var model = MainViewModel()
var body: some Scene {
WindowGroup {
MainScreenContainer(
model: model,
showOnboarding: $model.showOnboarding
)
}
}
}
struct MainScreenContainer: View {
#ObservedObject var model: MainViewModel
#Binding var showOnboarding: Bool
var body: some View {
if showOnboarding {
OnboardingView(
model: model,
showOnboarding: $model.showOnboarding
)
} else {
MainView(model: model)
}
}
}
struct OnboardingView: View {
#Binding var showOnboarding: Bool
var body: some View {
NavigationStack {
ZStack(alignment: .top) {
VStack {
LazyVGrid(columns: [GridItem(), GridItem(), GridItem(alignment: .topTrailing)], content: {
Button(action: {
showOnboarding = false
}, label: {
Image(systemName: "xmark")
})
})
}
}
}
}
}
I want to be able to click on the button inside OnboardingView and then set the app storage of showOnboarding to false, so next time the app runs, it can check MainScreenContainer and go directly to MainView if the storage is set to false.
In your use case i would actually use #AppStorage property wrapper link which is a wrapper over UserDefaults, and i would get rid of that ViewModel. The code would look something like this:
struct MainScreenContainer: View {
#AppStorage("show_onboarding") var showOnboarding: Bool = false
var body: some View {
if showOnboarding {
OnboardingView(
model: model
)
} else {
MainView(model: model)
}
}
}
struct OnboardingView: View {
#AppStorage("show_onboarding") var showOnboarding: Bool = false
var body: some View {
NavigationStack {
ZStack(alignment: .top) {
VStack {
LazyVGrid(columns: [GridItem(), GridItem(), GridItem(alignment: .topTrailing)], content: {
Button(action: {
showOnboarding = false
}, label: {
Image(systemName: "xmark")
})
})
}
}
}
}
}

Dismiss Presentation View - SWIFTUI

I am trying to build a app. I am using presentation mode to show the view, then I switch to another but when I use the dismiss function it just takes me back to the first view.
struct ContentView: View {
#Environment(\.presentationMode) var presentationMode
#State private var isShowingBookingScreen:Bool = false
var body: some View {
Button {
isShowingBookingScreen = true
} label: {
Spacer()
Text("Book Now").foregroundColor(.white).padding().font(.headline)
Spacer()
}.sheet(isPresented: $isShowingBookingScreen) {
SecondView()
}.background(Color.accentColor).cornerRadius(10).padding()
}
}
struct SecondView: View {
var body: some View {
NavigationView{
NavigationLink(destination:ThirdView()) {
Text("Continue to Third").foregroundColor(.red).padding().cornerRadius(10).padding()
}
}
}
}
struct ThirdView: View {
#Environment(\.presentationMode) var presentationMode
var body: some View {
Button {
presentationMode.wrappedValue.dismiss()
} label: {
Text("Dismiss")
}
}
}

SwiftUI Conditional View Transitions are not working

Consider the following code:
class ApplicationHostingView: ObservableObject {
#Published var value: Bool
}
struct ApplicationHostingView: View {
// view model env obj
var body: some View {
Group {
if applicationHostingViewModel.value {
LoginView()
.transition(.move(edge: .leading)) // <<<< Transition for Login View
} else {
IntroView()
}
}
}
}
struct IntroView: View {
// view model env obj
var body: some View {
Button(action: { applicationHostingViewModel.value = true }) {
Text("Continue")
}
}
}
struct LoginView: View {
var body: some View {
Text("Hello World")
}
}
ISSUE
In this case, I see my transition from IntroView to LoginView work fine except for any of the animations. Animations inside IntroView based on the conditionals seem to be working fine but transitions that change the entire screen don't seem to work.
change group to ZStack
add animation somewhere.
class ApplicationHostingViewModel: ObservableObject {
#Published var value: Bool = false
}
struct ApplicationHostingView: View {
// view model env obj
#ObservedObject var applicationHostingViewModel : ApplicationHostingViewModel
var body: some View {
ZStack {
if applicationHostingViewModel.value {
LoginView()
.transition(.move(edge: .leading))
} else {
IntroView(applicationHostingViewModel:applicationHostingViewModel)
}
}
}
}
struct IntroView: View {
// view model env obj
#ObservedObject var applicationHostingViewModel : ApplicationHostingViewModel
var body: some View {
Button(action: {
withAnimation(.default){
self.applicationHostingViewModel.value = true} }) {
Text("Continue")
}
}
}
struct LoginView: View {
var body: some View {
Text("Hello World").frame(maxWidth: .infinity, maxHeight: .infinity)
}
}

Push, pop view controller equivalent in SwiftUI

What's equivalent to the Push and Pop of a view controller in SwiftUI?
Root :
window.rootViewController = UIHostingController(rootView: ContentView().environmentObject(Model()))
iOS version 13.1 :
class Model: ObservableObject {
#Published var pushed = false
}
struct ContentView: View {
#EnvironmentObject var model: Model
var body: some View {
NavigationView {
VStack {
Button("Push") {
self.model.pushed = true
}
NavigationLink(destination: DetailView(), isActive: $model.pushed) { EmptyView() }
}
}
}
}
struct DetailView: View {
#EnvironmentObject var model: Model
var body: some View {
Button("Bring me Back") {
self.model.pushed = false
}
}
}
Removing the default back button and adding our own will let us get through, until the bug gets fixed by Apple.
class Model: ObservableObject {
#Published var pushed = false
}
struct ContentView: View {
#EnvironmentObject var model: Model
var body: some View {
NavigationView {
VStack {
Button("Push") {
self.model.pushed = true
}
NavigationLink(destination: DetailView(), isActive: $model.pushed) { EmptyView() }
}
}
}
}
struct DetailView: View {
#EnvironmentObject var model: Model
var body: some View {
Button("Bring me Back") {
self.model.pushed = false
}
.navigationBarBackButtonHidden(true)
.navigationBarItems(leading: MyBackButton(label: "Back!") {
self.model.pushed = false
})
}
}
struct MyBackButton: View {
let label: String
let closure: () -> ()
var body: some View {
Button(action: { self.closure() }) {
HStack {
Image(systemName: "chevron.left")
Text(label)
}
}
}
}
more to refer