How to make Sheet toggle and the navigate to a View - swift

I have 3 Views A, BSheet, C.
When I click on a button inside A View, B Sheet becomes visible. I have a button in B Sheet. I want to press a button in BSheet View to close/toggle BSheet and then navigate to C View. Any ideas how I can accomplish that?
A.swift
struct A: View{
#State var displayBSheet = false
var body: some View{
NavigationView{
VStack{
Button(action: {self.displayBSheet.toggle()}){
Text("Navigate to BSheet")
}.sheet(isPresented: $displayBSheet){
BSheet()
}
NavgiationLink(destination: C()){
Text("Navigate To C")
}
}
}
}
}
BSheet.swift
#Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
struct MainView: View {
var body: some View {
Button(action:{
self.presentationMode.wrappedValue.dismiss()//This only sheet but I want to navigate to C View also
}){
Text("Close sheet and navigate to struct C")
}
}
}
C.swift
struct C: View {
var body: some View {
Text("C")
}
}

I cleaned up your codes for a good working one:
import SwiftUI
struct ContentView: View {
var body: some View {
HomeView()
}
}
struct HomeView: View {
#State var homeView: Bool = false
#State var displayViewB: Bool = false
#State var displayViewC: Bool = false
var body: some View{
ZStack {
VStack {
HStack {
Text("Home").font(Font.largeTitle.bold()).padding()
Spacer()
}
Spacer()
}
VStack {
Button(action: { homeView = true; displayViewB = true }) {
Text("open ViewB")
}
.sheet(isPresented: $homeView) {
if displayViewB
{
ViewB(homeView: $homeView, displayViewB: $displayViewB, displayViewC: $displayViewC)
}
if displayViewC
{
ViewC(homeView: $homeView, displayViewC: $displayViewC)
}
}
}
}
.onChange(of: homeView) { _ in
if displayViewB == false && displayViewC == true {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {homeView = true}
}
else if displayViewB == false && displayViewC == false {
homeView = false
}
}
}
}
struct ViewB: View {
#Binding var homeView: Bool
#Binding var displayViewB: Bool
#Binding var displayViewC: Bool
var body: some View {
Button(action:{
displayViewB = false
displayViewC = true
homeView = false
}){
Text("close ViewB and open Viewc")
}
}
}
struct ViewC: View {
#Binding var homeView: Bool
#Binding var displayViewC: Bool
var body: some View {
Button(action:{
displayViewC = false
homeView = false
}){
Text("close ViewC go to HomeView")
}
}
}

You can do this simple - in onDismiss of sheet
struct A: View{
#State var displayBSheet = false
#State var displayCSheet = false // << this !!
var body: some View{
NavigationView{
VStack{
Button(action: {self.displayBSheet.toggle()}){
Text("Navigate to BSheet")
}.sheet(isPresented: $displayBSheet, onDismiss: {
self.displayCSheet = true // << here !!
}){
BSheet()
}
NavgiationLink(destination: C(), isActive: $displayCSheet){ // << here !!
Text("Navigate To C")
}
}
}
}
}

Related

Textfield toggling Bool when clicked

I have a Bool value titled editMode which determines if the user can edit information on a current page. When this is toggled true and I enter a textfield, the Bool is flipped back to false. This only occurs on the first click into the textfield. On subsequent clicks into the textfield, it does not toggle (working fine)
How can I fix this issue?
struct RecipeControllerModal: View {
#Environment(\.dismiss) var dismiss // << dismiss view
#ObservedObject var rm = RecipeLogic()
#ObservedObject var ema = EditModeActive() // calling in next view
var body: some View {
NavigationView{
VStack{
VStack{
RecipeDashHeader(ema: ema) // <<passing editMode
.padding()
}
}
}
RecipeDashHeader // <<textfield view
struct RecipeDashHeader: View {
#State var recipeName = ""
#ObservedObject var ema: EditModeActive
#ViewBuilder
var body: some View {
if ema.editMode {
VStack{
TextField(recipeName, text: $recipeName)
.foregroundColor(!ema.editMode ? Color.black : Color.red)
.font(.title2)
.multilineTextAlignment(.center)
.padding()
.onChange(of: recipeName, perform: { _ in
ema.recipeTitle = recipeName
})
}
Published EditMode Variable
class EditModeActive: ObservableObject {
#Published var editMode: Bool = false
#Published var recipeTitle = ""
}
Try this approach using the ema.recipeTitle directly in your TextField and
using #StateObject var ema as mentioned in the comments.
Here is the example code I used in my tests:
struct ContentView: View {
var body: some View {
RecipeControllerModal()
}
}
struct RecipeControllerModal: View {
#Environment(\.dismiss) var dismiss // << dismiss view
// #ObservedObject var rm = RecipeLogic() // for testing
#StateObject var ema = EditModeActive() // <-- here
var body: some View {
NavigationView{ // <-- here should use NavigationStack
VStack{
VStack{
RecipeDashHeader(ema: ema) // <<passing editMode
.padding()
}
// for testing
Button(ema.editMode ? "save" : "edit") {
ema.editMode.toggle()
}
}
}
}
}
struct RecipeDashHeader: View {
// #State var recipeName = "" // <-- here not needed
#ObservedObject var ema: EditModeActive
var body: some View {
if ema.editMode {
VStack{
TextField(ema.recipeTitle, text: $ema.recipeTitle) // <-- here
.foregroundColor(!ema.editMode ? Color.black : Color.red) // <--- usesless here ema.editMode == true always
.font(.title2)
.multilineTextAlignment(.center)
.padding()
.border(.red)
// not needed
// .onChange(of: ema.recipeTitle, perform: { name in
// ema.recipeTitle = name
// })
}
} else {
Text(ema.recipeTitle)
}
}
}
class EditModeActive: ObservableObject {
#Published var editMode: Bool = false
#Published var recipeTitle: String = "recipe title" // <-- here
}

Button with NavigationLink and function call SwiftUI

I have an issue with NavigationLinks with conditions. It doesn't react what I'm expected. When a user click on the button the function "test" must be called and give a return value. If the return value is true the "SheetView" must be openend directly without clicking on the NavigationLink text. Please could someone give me a help on this one. Thanks in advance
I made a small (sample) program for showing the issue.
import SwiftUI
struct LoginView: View {
#State var check = false
#State var answer = false
var body: some View {
NavigationView {
VStack{
Text("it doesn't work")
Button(action: {
answer = test(value: 2)
if answer {
//if the return value is true then directly navigate to the sheetview
NavigationLink(
destination: SheetView(),
label: {
Text("Navigate")
})
}
}, label: {
Text("Calculate")
})
}
}
}
func test(value: Int) -> Bool {
if value == 1 {
check = false
} else {
print("Succes")
check = true
}
return check
}
}
struct SheetView: View {
var body: some View {
NavigationView {
VStack{
Text("Test")
.font(.title)
}
}
}
}
The answer from Yodagama works if you were trying to present a sheet (because you called your navigation destination SheetView), but if you were trying to navigate to SheetView instead of present a sheet, the following code would do that.
struct LoginView: View {
#State var check = false
#State var answer = false
var body: some View {
NavigationView {
VStack{
Text("it doesn't work")
NavigationLink(destination: SheetView(), isActive: $answer, label: {
Button(action: {
answer = test(value: 2)
}, label: {
Text("Calculate")
})
})
}
}
}
func test(value: Int) -> Bool {
if value == 1 {
check = false
} else {
print("Succes")
check = true
}
return check
}
}
struct SheetView: View {
var body: some View {
NavigationView {
VStack{
Text("Test")
.font(.title)
}
}
}
}
struct LoginView: View {
#State var check = false
#State var answer = false
var body: some View {
NavigationView {
VStack{
Text("it doesn't work")
Button(action: {
answer = test(value: 2)
//<= here
}, label: {
Text("Calculate")
})
}
.sheet(isPresented: $answer, content: { //<= here
SheetView()
})
}
}
...

Modify state in nested object

im new to Swift and i am having this issue. I want to make a play/pause button in the toolbar and i decided to move the toolbar code to its own object Toolbar. The button should change its image when pressed but when i press it the state doesn't change. What am i doing wrong?
struct ContentView: View {
var body: some View {
NavigationView {
List{
Text("asdf")
}
.toolbar {
Toolbar()
}
}
}
}
struct Toolbar: ToolbarContent {
#State var started = false
var body: some ToolbarContent {
ToolbarItem(id:"start-button", placement: .primaryAction) {
Button(action: {
self.started.toggle()
}) {
Image(systemName: self.started == true ? "pause.fill" : "play.fill")
.foregroundColor(.white)
}
}
}
}
Use two-way binding.
struct ContentView: View {
#State private var started = false //<-- Here
var body: some View {
NavigationView {
List{
Text("asdf")
}
.toolbar {
Toolbar(started: $started) //<-- Here
}
}
}
}
struct Toolbar: ToolbarContent {
#Binding var started: Bool //<-- Here
var body: some ToolbarContent {
ToolbarItem(id:"start-button", placement: .primaryAction) {
Button(action: {
self.started.toggle()
}) {
Image(systemName: self.started ? "pause.fill" : "play.fill")
.foregroundColor(.white)
}
}
}
}
#State only works inside Views, and ToolbarContent isn't a View.
You should keep the #State started inside ContentView, and pass in its wrapped value to the toolbar. Then, use a closure to update it.
struct ContentView: View {
#State var started = false
var body: some View {
NavigationView {
List{
Text("asdf")
}
.toolbar {
Toolbar(started: started) {
started.toggle() /// executed when `pressed` is called
}
}
}
}
}
struct Toolbar: ToolbarContent {
var started: Bool
var pressed: (() -> Void) /// closure here!
var body: some ToolbarContent {
ToolbarItem(id:"start-button", placement: .primaryAction) {
Button(action: {
pressed() /// call the closure
}) {
Image(systemName: self.started == true ? "pause.fill" : "play.fill")
.foregroundColor(.white)
}
}
}
}

Navigation Bar Buttons sometimes are not tappable

I have FavoritesView set with a navigationBarItem Button to display SettingsView as a modal sheet. However, both this button, and the Done button in the sheet only register and respond to some taps. I can't see any pattern to when the buttons will respond, but sometimes it can take 4 or 5 taps before the app responds! Any way to fix this behaviour?
FavoritesView:
import SwiftUI
enum ActiveSheet {
case details, settings
}
struct FavoritesView: View {
let speakers: [Speaker] = Bundle.main.decode("SpeakerTestData.json")
#State private var selectedModel: Speaker?
#State private var showingSheet = false
#State private var activeSheet: ActiveSheet = .settings
#EnvironmentObject var favorites: Favorites
#EnvironmentObject var settings: UserSettings
var filteredFavorites: [Speaker] {
let allSpeakers = speakers
var filteredItems: [Speaker] = []
for entry in allSpeakers {
if favorites.contains(entry) {
filteredItems.append(entry)
}
}
let sorted = filteredItems.sorted {
$0.model.localizedStandardCompare($1.model) == .orderedAscending
}
return sorted
}
var body: some View {
NavigationView {
List {
if filteredFavorites.count == 0 {
Text("Items you favourite will appear here.")
.foregroundColor(.secondary).padding(5)
} else {
Section(header: Text("Speakers")) {
ForEach(filteredFavorites) { speaker in
HStack {
Button(action: {
self.activeSheet = .details
self.selectedModel = speaker
self.showingSheet = true
}) {
SpeakerModelRow(speaker: speaker).contentShape(Rectangle())
}
.buttonStyle(PlainButtonStyle())
Spacer()
Button(action: {
if self.favorites.contains(speaker) {
self.favorites.remove(speaker)
} else {
self.favorites.add(speaker)
}
}, label: {
if self.favorites.contains(speaker) {
Image(systemName: "star.fill")
.foregroundColor(.blue)
.font(Font.title.weight(.ultraLight))
} else {
Image(systemName: "star")
.foregroundColor(.gray)
.font(Font.title.weight(.ultraLight))
}
}
).padding(5)
}
}
}
}
}
.navigationBarTitle("Favourites")
.navigationBarItems(trailing:
Button(action: {
self.activeSheet = .settings
self.showingSheet = true
}){
Image(systemName: "gear").font(Font.title.weight(.ultraLight)).padding(.trailing, 5).foregroundColor(.primary)
})
.sheet(isPresented: self.$showingSheet) {
if self.activeSheet == .details {
SpeakerDetailView(speaker: self.selectedModel!, showSheet: self.$showingSheet).environmentObject(self.favorites).environmentObject(self.settings)
} else {
SettingsView(showSheet: self.$showingSheet).environmentObject(self.settings)
}
}
}
}
}
SettingsView presented as a sheet:
struct SettingsView: View {
#EnvironmentObject var settings: UserSettings
#Binding var showSheet: Bool
#State var result: Result<MFMailComposeResult, Error>? = nil
#State var isShowingMailView = false
var body: some View {
NavigationView {
VStack {
//SettingsView Window
}.navigationBarTitle("Settings")
.navigationBarItems(leading: Button("Done") {
self.showSheet = false
})
}
}
}
It is known issue, try to use internal button content padding (either default or more), like
.navigationBarItems(leading: Button(action: {
self.showSheet = false
}) { Text("Done").padding() }

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