SwiftUI - dismiss NavigationLink destination View and update main NavigationView - EXC_BAD_ACCESS (code=2 - binding

I want tu update main NavigationView after dismissing subview from NavigationLink, but from one subview with Success and from another with "EXC_BAD_ACCESS (code=2..." in XCode.
I do it by #Binding var which I change its value from subview. I think, the problems are related to CoreData, but I dont know where and why.
Any idea?
MainView:
struct ProgramView: View {
#State var preliminaryViewed: Bool = Program2s.shared.program2s.preliminaryViewed
#State var programs = ProgramStatess.shared.programStats
#State var states: [Int] = [0, 0, 0, 0]
func programState(number: Int){
if programs[number].startDate != nil {
if programs[number].completed {
states[number] = 2
}
else{
states[number] = 1
}
}
else{
states[number] = 0
}
}
var body: some View {
NavigationView {
VStack(alignment: .leading){
ForEach(programs, id: \.self){ program in
Group {
NavigationLink(destination:
{
VStack{
if program.order == 0 {
if self.states[Int(program.order)] == 1 {
Program1View(state: self.$states[Int(program.order)])
.navigationBarTitle(Text("\(program.name!)"), displayMode: .inline)
}
else {
Program1ResultView()
.navigationBarTitle(Text("\(program.name!)(Splněno) - shrnutí"), displayMode: .inline)
}
}
else if program.order == 1 {
if self.states[Int(program.order)] == 1 {
if !self.preliminaryViewed {
AnyView(Program2PreliminaryView(preliminaryViewed: self.$preliminaryViewed))
.navigationBarTitle(Text("\(program.name!)"), displayMode: .inline)
}
else {
AnyView(Program2View(state: self.$states[Int(program.order)]))
.navigationBarTitle(Text("\(program.name!)"), displayMode: .inline)
}
}
else{
AnyView(Program2View(state: self.$states[Int(program.order)]))
.navigationBarTitle(Text("\(program.name!)"), displayMode: .inline)
}
}
else {
if self.states[Int(program.order)] == 1 {
AnyView(Program1View(state: self.$states[Int(program.order)]))
.navigationBarTitle(Text("\(program.name!)"), displayMode: .inline)
}
else{
AnyView(Program1View(state: self.$states[Int(program.order)]))
.navigationBarTitle(Text("\(program.name!)"), displayMode: .inline)
}
}
} // end of VStack
}()
) {
VStack(alignment: .leading){
if self.states[Int(program.order)] == 0 {
Text("\(program.name!)")
.fontWeight(.bold).font(.headline)
Divider()
Text("Closed")
}
else if self.states[Int(program.order)] == 1 {
Text("\(program.name!)")
.fontWeight(.bold).font(.headline)
.foregroundColor(Color(.systemYellow))
Divider()
Text("Opened")
}
else {
Text("\(program.name!)")
.fontWeight(.bold).font(.headline)
.foregroundColor(Color(UIColor.systemGray))
Divider()
Text("Finished")
}
}
.padding()
} // end of navlink
.buttonStyle(PlainButtonStyle())
.disabled(self.states[Int(program.order)] == 0)
Spacer()
} // end of group
} // end of foreach
} // end of VStack
.padding()
.navigationBarTitle("Program")
.onAppear(){
for value in 0...3 {
print("self.states[\(value)]=\(self.states[value])")
}
}
} // end of Navigationview
.onAppear(perform: {
self.changeColor()
for value in 0...3 {
self.programState(number: value)
}
})
}
SubView with Success after change "self.state = 2"
struct Program2View: View {
#Binding var state: Int
#State private var page: Int = 0
#State private var disableSwipe: Bool = false
#State private var pages: Int = 7
#State var progressValue: Float = 1.0/7.0
#State var progressChange: Float = 1.0/7.0
#State var showingAlert: Bool = false
#Environment(\.presentationMode) var presentationMode
let program2 = Program2s.shared
let programStates = ProgramStatess.shared
#ObservedObject var stopWatch = StopWatch()
var body: some View {
ZStack{
VStack{
ProgressBar(value: $progressValue).frame(height: 20)
.padding()
PageView([
AnyView(
VStack{
Text("Bla bla bla.")
}
.padding()
.onAppear(){
self.progressValue = Float(self.page+1)*self.progressChange
}
),
AnyView(
VStack{
Text("Bla bla bla.")
}
.padding()
.onAppear(){
self.progressValue = Float(self.page+1)*self.progressChange
}
),
AnyView(
VStack{
Text("Bla bla bla.")
}
.padding()
.onAppear(){
self.progressValue = Float(self.page+1)*self.progressChange
}
),
AnyView(
VStack{
Text("Bla bla bla.")
}
.padding()
.onAppear(){
self.progressValue = Float(self.page+1)*self.progressChange
}
),
AnyView(
VStack{
Text("Bla bla bla.")
}
.padding()
.onAppear(){
self.progressValue = Float(self.page+1)*self.progressChange
}
),
AnyView(
VStack{
Text("Bla bla bla.")
}
.padding()
.onAppear(){
self.progressValue = Float(self.page+1)*self.progressChange
}
),
AnyView(
VStack{
Spacer()
Text("Bla bla bla.")
.fixedSize(horizontal: false, vertical: true)
.padding(.bottom)
Text("Bla bla bla.")
Spacer()
}
.padding()
.onAppear(){
self.progressValue = Float(self.page+1)*self.progressChange
}
)
], currentPage: $page, disableSwipe: $disableSwipe)
} // end of VStack
VStack{
Spacer()
HStack{
Spacer()
if (page-1 > -1 ){
Button(action: ({
self.page=self.page-1
})){
Image(systemName: "arrow.left")
.foregroundColor(Color(.systemGray))
.padding()
}
}
if (page+1 < pages ){
Button(action: ({
self.page=self.page+1
})){
Text("Continue")
}
}
if (page == 6 ){
Button(action: ({
if self.program2.program2s.programCompleted == nil {
self.showingAlert = true
self.stopWatch.pause()
}
else {
self.presentationMode.wrappedValue.dismiss()
}
})){
Text("Finish")
}
}
} // end of HStack
} //end of VStack
.padding()
} // end of ZStack
.alert(isPresented: $showingAlert, content:{
Alert(title: Text("Bla bla bla"),
message: Text("Bla bla bla"),
primaryButton: .default(Text("Continue")) {
if self.program2.program2s.programCompleted == nil{
self.program2.setProgram2(relaxDurationInSec: Double(self.stopWatch.stopWatchSeconds))
self.programStates.setProgramStateCompleted(program: 2)
}
self.state = 2 // HERE IT IS
self.presentationMode.wrappedValue.dismiss()
},
secondaryButton: .cancel(Text("Back"))
)
})
.onAppear(){
// START STOPWATCH
if self.program2.program2s.programCompleted == nil {
if self.stopWatch.stopWatchSeconds == 0 {
self.stopWatch.start()
}
}
}
.onDisappear(){
// STOP STOPWATCH
if !self.stopWatch.isPaused() {
self.stopWatch.pause()
self.stopWatch.reset()
}
}
}
SubView with ERROR after change "self.state = 2"
struct Program1View: View {
#Binding var state: Int
#EnvironmentObject var sliderData: SliderData
#State private var page: Int = 0
#State private var disableSwipe: Bool = true
#State private var pages: Int = 6
#State var progressValue: Float = 1.0/6.0
#State var progressChange: Float = 1.0/6.0
#State var showingAlert: Bool = false
#State var target: String = ""
#State var target_saveChangesInstantly: Bool = false
#State var completion: String = ""
#State var completion_saveChangesInstantly: Bool = false
#State var satisfiability: Float = 0
#State var reason: String = ""
#State var reason_saveChangesInstantly: Bool = false
#State var deadline: String = ""
#State var deadline_saveChangesInstantly: Bool = false
#State var summarizedTarget: String = ""
#State var summarizedTarget2: String = ""
#Environment(\.presentationMode) var presentationMode
let program1 = Program1s.shared
let programStates = ProgramStatess.shared
var nextIsDisabled: Bool {
switch self.page {
case 0 : do {
return target == "" ? true : false
}
case 1: do {
return completion == "" ? true : false
}
case 2: do {
return satisfiability < 6.0 ? true : false
}
case 3: do {
return reason == "" ? true : false
}
case 4: do {
return deadline == "" ? true : false
}
case 5: do {
return summarizedTarget == "" ? true : false
}
default: do {
return true
}
}
}
var nextButtonColor: Color {
return nextIsDisabled ? Color(.systemGray) : app_green
}
var submitButtonColor: Color {
return summarizedTarget == "" ? Color(.systemGray) : app_green
}
var body: some View {
ZStack {
VStack {
ProgressBar(value: $progressValue).frame(height: 20)
.padding()
PageView([
AnyView(
VStack{ // Slide 1
HStack{
Text("Blablabla")
Spacer()
}
HStack{
MultilineTextView(text: $target, text_saveChangesInstantly: $target_saveChangesInstantly)
}
.keyboardAdaptive(posun: 245)
.padding()
.onAppear(){
self.progressValue = Float(self.page+1)*self.progressChange
}
),
AnyView(
VStack{ // Slide 2
HStack{
Text("Blablabla")
Spacer()
}
HStack{
MultilineTextView(text: $completion, text_saveChangesInstantly: $completion_saveChangesInstantly)
}
}
.keyboardAdaptive(posun: 245)
.padding()
.onAppear(){
if self.target == "" {
self.page = 0
}
self.progressValue = Float(self.page+1)*self.progressChange
}
),
AnyView( // Slide 3
VStack(spacing: 50){
HStack{
Text("Blablabla")
Spacer()
}
VStack{
HStack{
Slider(value: $sliderData.sliderValue, in: 1...10, step: 1, onEditingChanged: { (changed) in
// Editing Changed
self.satisfiability = self.sliderData.sliderValue
})
.accentColor(Color(.systemYellow))
}
HStack(){
ForEach(1 ..< 11) { number in
Text("\(number)")
.frame(maxWidth: .infinity)
.onTapGesture {
self.satisfiability = Float(number)
self.sliderData.sliderValue = Float(number)
}
}
}
}
HStack{
if self.satisfiability > 0.0 && self.satisfiability < 6.0 {
Text("Blablabla")
}
Spacer()
}
Spacer()
} // end of VStack
.padding()
.onAppear(){
if self.completion == "" {
self.page = 1
}
self.progressValue = Float(self.page+1)*self.progressChange
}
),
AnyView( // Slide 4
VStack{
HStack{
Text("Blablabla")
Spacer()
}
HStack{
MultilineTextView(text: $reason, text_saveChangesInstantly: $reason_saveChangesInstantly)
}
}
.keyboardAdaptive(posun: 245)
.padding()
.onAppear(){
if self.satisfiability < 6.0 {
self.page = 2
}
self.progressValue = Float(self.page+1)*self.progressChange
}
),
AnyView( // Slide 5
VStack{
HStack(alignment: .firstTextBaseline){
Text("Blablabla")
Spacer()
}
HStack{
MultilineTextView(text: $deadline, text_saveChangesInstantly: $deadline_saveChangesInstantly)
}
}
.keyboardAdaptive(posun: 245)
.padding()
.onAppear(){
if self.reason == "" {
self.page = 3
}
self.progressValue = Float(self.page+1)*self.progressChange
}
),
AnyView( // Slide 6 - final
ScrollView{
VStack{
HStack{
Text("1.Blablabla")
Spacer()
}
HStack{
Text("\(target)")
Spacer()
}
}.padding(.leading).padding(.bottom)
VStack{
HStack{
Text("2.Blablabla")
Spacer()
}
HStack{
Text("\(completion)")
Spacer()
}
}.padding(.leading).padding(.bottom)
VStack{
HStack{
Text("3. Blablabla")
Spacer()
}
HStack{
Text("\(satisfiability, specifier: "%.0f") / 10")
Spacer()
}
}.padding(.leading).padding(.bottom)
VStack{
HStack{
Text("4. Blablabla")
Spacer()
}
HStack{
Text("\(reason)")
Spacer()
}
}.padding(.leading).padding(.bottom)
VStack{
HStack{
Text("5. Blablabla")
Spacer()
}
HStack{
Text("\(deadline)")
Spacer()
}
}.padding(.leading).padding(.bottom)
VStack{
HStack{
Text("Blablabla")
Spacer()
}
HStack{
TextField("Blablabla" text: $summarizedTarget2, onEditingChanged: { (changed) in
// Editing Changed
if self.summarizedTarget2.count > 69 {
self.summarizedTarget2=String(self.summarizedTarget2.prefix(69))
}
}) {
//Editing Commited
self.summarizedTarget = self.summarizedTarget2
}
.textFieldStyle(RoundedBorderTextFieldStyle())
Spacer()
}
}.padding()
} // end of Scrollview
.keyboardAdaptive(posun: 120)
.onAppear(){
if self.deadline == "" {
self.page = 4
}
self.progressValue = Float(self.page+1)*self.progressChange
}
)
], currentPage: $page, disableSwipe: $disableSwipe)
} // end of VStack
VStack{
Spacer()
HStack{
Spacer()
if (page-1 > -1 ){
Button(action: ({ // Button "Back"
self.page=self.page-1
})){
Image(systemName: "arrow.left")
}
}
if (page+1 < pages ){
Button(action: ({
self.page=self.page+1
})){
Text("Continue")
}.disabled(nextIsDisabled)
}
if (page == 5 ){
Button(action: ({
self.showingAlert = true
})){
Text("Finish")
}.disabled(summarizedTarget == "")
}
} // end of HStack
} //end of VStack
.padding()
} // end of ZStack
.alert(isPresented: $showingAlert, content:{
Alert(title: Text("Blablabla"),
message: Text("Blablabla"),
primaryButton: .default(Text("Continue")) {
self.program1.addProgram1(target: self.target, completion: self.completion, satisfiability: self.satisfiability, reason: self.reason, deadline: self.deadline, sumarizedTarget: self.summarizedTarget)
self.programStates.setProgramStateCompleted(program: 1)
self.state = 2 // HERE IT IS, Starting problem finished with ERROR
self.presentationMode.wrappedValue.dismiss()
},
secondaryButton: .cancel(Text("Edit"))
)
})
} // end of body

Related

Animation not working inside sheet for swiftui

I am using a sheet to present a list of options and on click of the option I want to change the view with the animation of sliding from trailing. As per my understanding and what I have read on various sites I have written this code but I am not sure why it is not working the way intended. I just want to know where exactly this code went wrong.
struct XYZ: App {
let persistenceController = PersistenceController.shared
#State var isPresented : Bool = false
#State var isSwiped : Bool = false
var body: some Scene {
WindowGroup {
optionList(isPresented: $isPresented)
.sheet(isPresented: $isPresented, content: {
Text("This is from modal view!")
.onTapGesture {
withAnimation(Animation.easeIn(duration: 10)){
isSwiped.toggle()
}
}
if isSwiped {
checkedList()
.transition(.move(edge: .trailing))
}
})
}
}
}
struct optionList : View {
#Binding var isPresented : Bool
var body: some View {
Text("This is a testView")
.onTapGesture {
withAnimation{
isPresented.toggle()
}
}
}
}
struct checkedList : View {
#State var name : String = "WatchList"
var arr = ["First", "Second", "Third", "Fourth", "Fifth", "Sixth", "Seventh"]
#State var mp : [Int:Int] = [:]
var body: some View {
VStack{
HStack{
TextField("WatchlistName", text: $name)
.padding(.all)
Image(systemName: "trash.fill")
.padding(.all)
.onTapGesture {
print("Delete watchList!!")
}
}
ScrollView{
ForEach(arr.indices) { item in
HStack (spacing: 0) {
Image(systemName: mp.keys.contains(item) ? "checkmark.square" : "square")
.padding(.horizontal)
Text(arr[item])
}
.padding(.bottom)
.frame(width: UIScreen.main.bounds.width, alignment: .leading)
.onTapGesture {
if mp.keys.contains(item) {
mp[item] = nil
} else {
mp[item] = 1
}
}
}
}
Button {
print("Remove Ticked Elements!")
deleteWatchListItem(arr: Array(mp.keys))
} label: {
Text("Save")
}
}
}
func deleteWatchList(ind: Int){
print(ind)
}
func deleteWatchListItem(arr : [Int]) {
print(arr)
}
}
I tried to create a view and with the animation using withanimation with a bool variable tried to change the view.
It sounds like what you want is to push the checkedList on to a NavigationStack…
struct ContentView: View {
#State var isPresented : Bool = false
var body: some View {
Text("This is a testView")
.onTapGesture {
isPresented.toggle()
}
.sheet(isPresented: $isPresented, content: {
NavigationStack {
NavigationLink("This is from modal view!") {
checkedList()
}
}
})
}
}

Swiftui items get duplicated in all views when added to a single custom view

I'm struggling with the following issue: I'm trying to build a very simple app that lets you add items in a dedicated view that can be collapsed. I managed to write a simple function that lets me add multiple of these custom collapsable views. It's my first app so I wanted to follow the MVVM protocol. I think I got confused along the way because now every item I add gets automatically added to all the custom collapsable views I made. Is there any way to fix this? I thought using the UUID would solve this issue.. I'm guessing that I have to customise the "saveButtonPressed" function, but I don't know how to tell it to only add the item to the view where I pressed the "plus" button..
Here are the Models for the individual items and the collapsable view:
struct ItemModel: Identifiable, Equatable {
let id: String
let title: String
init(id: String = UUID().uuidString, title: String) {
self.id = id
self.title = title
}
func updateCompletion() -> ItemModel {
return ItemModel(id: id, title: title)
}
}
--
import Foundation
struct CollapsableItem: Equatable, Identifiable, Hashable {
let id: String
var title: String
init(id: String = UUID().uuidString, title: String) {
self.id = id
self.title = title
}
func updateCompletion() -> CollapsableItem {
return CollapsableItem(id: id, title: title)
}
}
These are my two ViewModels:
class ListViewModel: ObservableObject {
#Published var items: [ItemModel] = []
init() {
getItems()
}
func getItems() {
let newItems = [
ItemModel(title: "List Item1"),
ItemModel(title: "List Item2"),
ItemModel(title: "List Item3"),
]
items.append(contentsOf: newItems)
}
func addItem(title: String) {
let newItem = ItemModel(title: title)
items.append(newItem)
}
func updateItem(item: ItemModel) {
if let index = items.firstIndex(where: { $0.id == item.id}) {
items[index] = item.updateCompletion()
}
}
}
--
class CollapsedViewModel: ObservableObject {
#Published var collapsableItems: [CollapsableItem] = []
#Published var id = UUID().uuidString
init() {
getCollapsableItems()
}
func getCollapsableItems() {
let newCollapsableItems = [
CollapsableItem(title: "Wake up")
]
collapsableItems.append(contentsOf: newCollapsableItems)
}
func addCollapsableItem(title: String) {
let newCollapsableItem = CollapsableItem(title: title)
collapsableItems.append(newCollapsableItem)
}
func updateCollapsableItem(collapsableItem: CollapsableItem) {
if let index = collapsableItems.firstIndex(where: { $0.id ==
collapsableItem.id}) {
collapsableItems[index] =
collapsableItem.updateCompletion()
}
}
}
The item view:
struct ListRowView: View {
#EnvironmentObject var listViewModel: ListViewModel
let item: ItemModel
var body: some View {
HStack() {
Text(item.title)
.font(.body)
.fontWeight(.bold)
.foregroundColor(.white)
.multilineTextAlignment(.center)
.lineLimit(1)
.frame(width: 232, height: 16)
}
.padding( )
.frame(width: 396, height: 56)
.background(.gray)
.cornerRadius(12.0)
}
}
The collapsable view:
struct CollapsedView2<Content: View>: View {
#State var collapsableItem: CollapsableItem
#EnvironmentObject var collapsedViewModel: CollapsedViewModel
#State private var collapsed: Bool = true
#EnvironmentObject var listViewModel: ListViewModel
#State var label: () -> Text
#State var content: () -> Content
#State private var show = true
var body: some View {
ZStack{
VStack {
HStack{
Button(
action: { self.collapsed.toggle() },
label: {
HStack() {
Text("Hello")
.font(.title2.bold())
Spacer()
Image(systemName: self.collapsed ? "chevron.down" :
"chevron.up")
}
.padding(.bottom, 1)
.background(Color.white.opacity(0.01))
}
)
.buttonStyle(PlainButtonStyle())
Button(action: saveButtonPressed, label: {
Image(systemName: "plus")
.font(.title2)
.foregroundColor(.white)
})
}
VStack {
self.content()
}
ForEach(listViewModel.items) { item in ListRowView (item: item)
}
.lineLimit(1)
.fixedSize(horizontal: true, vertical: true)
.frame(minWidth: 396, maxWidth: 396, minHeight: 0, maxHeight: collapsed ?
0 : .none)
.animation(.easeInOut(duration: 1.0), value: show)
.clipped()
.transition(.slide)
}
}
}
func saveButtonPressed() {
listViewModel.addItem(title: "Hello")
}
}
and finally the main view:
struct ListView: View {
#EnvironmentObject var listViewModel: ListViewModel
#EnvironmentObject var collapsedViewModel: CollapsedViewModel
var body: some View {
ZStack{
ScrollView{
VStack{
HStack{
Text("MyFirstApp")
.font(.largeTitle.bold())
Button(action: newCollapsablePressed, label: {
Image(systemName: "plus")
.font(.title2)
.foregroundColor(.white)
})
}
.padding()
.padding(.leading)
ForEach(collapsedViewModel.collapsableItems) { collapsableItem in
CollapsedView2 (collapsableItem: collapsableItem,
label: { Text("") .font(.title2.bold()) },
content: {
HStack {
Text("")
Spacer() }
.frame(maxWidth: .infinity)
})
}
.padding()
}
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.statusBar(hidden: false)
.navigationBarHidden(true)
}
}
func newCollapsablePressed() {
collapsedViewModel.addCollapsableItem(title: "hello2")
}
}
Would love to understand how I could fix this!
There is the anwser for your comment about add item in each CollapsedView2.
Because ListViewModel is not ObservableObject (ListViewModel is difference from each CollapsableItem). You should use "#State var items: [ItemModel]".
struct CollapsedView2<Content: View>: View {
#State var collapsableItem: CollapsableItem
// #State var listViewModel = ListViewModel()
#State var collapsed: Bool = true
#State var label: () -> Text
#State var content: () -> Content
#State private var show = true
#State var items: [ItemModel] = []
#State var count = 1
var body: some View {
VStack {
HStack{
Text("Hello")
.font(.title2.bold())
Spacer()
Button( action: { self.collapsed.toggle() },
label: {
Image(systemName: self.collapsed ? "chevron.down" : "chevron.up")
}
)
.buttonStyle(PlainButtonStyle())
Button(action: saveButtonPressed, label: {
Image(systemName: "plus")
.font(.title2)
// .foregroundColor(.white)
})
}
VStack {
self.content()
}
ForEach(items) { item in
ListRowView (item: item)
}
.lineLimit(1)
.fixedSize(horizontal: true, vertical: true)
.frame(minHeight: 0, maxHeight: collapsed ? 0 : .none)
.animation(.easeInOut(duration: 1.0), value: show)
.clipped()
.transition(.slide)
}
}
func saveButtonPressed() {
addItem(title: "Hello \(count)")
count += 1
}
func addItem(title: String) {
let newItem = ItemModel(title: title)
items.append(newItem)
}
func updateItem(item: ItemModel) {
if let index = items.firstIndex(where: { $0.id == item.id}) {
items[index] = item.updateCompletion()
}
}
}
There is the anwser. Ask me if you have some questions
struct ListView: View {
#StateObject var collapsedViewModel = CollapsedViewModel()
var body: some View {
ScrollView{
VStack{
HStack{
Text("MyFirstApp")
.font(.largeTitle.bold())
Button(action: newCollapsablePressed, label: {
Image(systemName: "plus")
.font(.title2)
// .foregroundColor(.white)
})
}
ForEach(collapsedViewModel.collapsableItems) { collapsableItem in
CollapsedView2 (collapsableItem: collapsableItem,
label: { Text("") .font(.title2.bold()) },
content: {
HStack {
Text("")
Spacer()
}
})
}
}
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.statusBar(hidden: false)
.navigationBarHidden(true)
}
func newCollapsablePressed() {
collapsedViewModel.addCollapsableItem(title: "hello2")
}
}
struct CollapsedView2<Content: View>: View {
#State var collapsableItem: CollapsableItem
#State var listViewModel = ListViewModel()
#State var collapsed: Bool = true
#State var label: () -> Text
#State var content: () -> Content
#State private var show = true
var body: some View {
VStack {
HStack{
Button( action: { self.collapsed.toggle() },
label: {
HStack() {
Text("Hello")
.font(.title2.bold())
Spacer()
Image(systemName: self.collapsed ? "chevron.down" : "chevron.up")
}
.padding(.bottom, 1)
.background(Color.white.opacity(0.01))
}
)
.buttonStyle(PlainButtonStyle())
Button(action: saveButtonPressed, label: {
Image(systemName: "plus")
.font(.title2)
.foregroundColor(.white)
})
}
VStack {
self.content()
}
ForEach(listViewModel.items) { item in
ListRowView (item: item)
}
.lineLimit(1)
.fixedSize(horizontal: true, vertical: true)
.frame(minHeight: 0, maxHeight: collapsed ? 0 : .none)
.animation(.easeInOut(duration: 1.0), value: show)
.clipped()
.transition(.slide)
}
}
func saveButtonPressed() {
listViewModel.addItem(title: "Hello")
}
}
struct ListRowView: View {
let item: ItemModel
var body: some View {
HStack() {
Text(item.title)
.font(.body)
.fontWeight(.bold)
.foregroundColor(.white)
.multilineTextAlignment(.center)
.lineLimit(1)
.frame(width: 232, height: 16)
}
.padding( )
.frame(width: 396, height: 56)
.background(.gray)
.cornerRadius(12.0)
}
}
class ListViewModel {
var items: [ItemModel] = []
init() {
getItems()
}
func getItems() {
let newItems = [
ItemModel(title: "List Item1"),
ItemModel(title: "List Item2"),
ItemModel(title: "List Item3"),
]
items.append(contentsOf: newItems)
}
func addItem(title: String) {
let newItem = ItemModel(title: title)
items.append(newItem)
}
func updateItem(item: ItemModel) {
if let index = items.firstIndex(where: { $0.id == item.id}) {
items[index] = item.updateCompletion()
}
}
}
class CollapsedViewModel: ObservableObject {
#Published var collapsableItems: [CollapsableItem] = []
#Published var id = UUID().uuidString
init() {
getCollapsableItems()
}
func getCollapsableItems() {
let newCollapsableItems = [
CollapsableItem(title: "Wake up")
]
collapsableItems.append(contentsOf: newCollapsableItems)
}
func addCollapsableItem(title: String) {
let newCollapsableItem = CollapsableItem(title: title)
collapsableItems.append(newCollapsableItem)
}
func updateCollapsableItem(collapsableItem: CollapsableItem) {
if let index = collapsableItems.firstIndex(where: { $0.id ==
collapsableItem.id}) {
collapsableItems[index] =
collapsableItem.updateCompletion()
}
}
}
struct CollapsableItem: Equatable, Identifiable, Hashable {
let id: String
var title: String
init(id: String = UUID().uuidString, title: String) {
self.id = id
self.title = title
}
func updateCompletion() -> CollapsableItem {
return CollapsableItem(id: id, title: title)
}
}
struct ItemModel: Identifiable, Equatable {
let id: String
let title: String
init(id: String = UUID().uuidString, title: String) {
self.id = id
self.title = title
}
func updateCompletion() -> ItemModel {
return ItemModel(id: id, title: title)
}
}

IOS App Crash when show keyboard after change binding inside sheet

this is my code with swiftui, when I change tradingMode inside sheet by click button, after show keyboard from ContentView => app crash. Pease help me and thanks!
enum TradingMode {
case Derivatives, Equities
}
struct ContentView: View {
#State var tradingMode : TradingMode = TradingMode.Equities
#State var isShowSecondView = false
var body: some View {
VStack(content: {
Button("show second view") {
isShowSecondView.toggle()
}
TabView {
switch tradingMode {
case .Equities:
VStack(content: {
Text("Tab 1 Un")
.padding()
TextField("ple", text: .constant(""))
})
.tabItem {
Text("tab 1")
}.tag(0)
case .Derivatives:
VStack(content: {
Text("Tab 1 Der")
.padding()
TextField("ple", text: .constant(""))
})
.tabItem {
Text("tab 1")
}.tag(0)
}
switch tradingMode {
case .Equities:
VStack(content: {
Text("Tab 2 Un")
.padding()
TextField("ple", text: .constant(""))
})
.tabItem {
Text("tab 2")
}.tag(1)
case .Derivatives:
VStack(content: {
Text("Tab 2 Der")
.padding()
TextField("ple", text: .constant(""))
})
.tabItem {
Text("tab 2")
}.tag(1)
}
}
})
.sheet(isPresented: $isShowSecondView, content: {
SecondView(tradingMode: $tradingMode)
})
}
}
struct SecondView: View {
#Environment(\.presentationMode) var presentation
#Binding var tradingMode : TradingMode
var body: some View {
VStack(content: {
Button("Change state") {
if tradingMode == .Derivatives {
tradingMode = .Equities
} else {
tradingMode = .Derivatives
}
self.presentation.wrappedValue.dismiss()
}
})
}
}
This is my bug: https://drive.google.com/file/d/18eXCmlByGqJylE_hCZbtJ4Rn0wmI0bb_/view?usp=sharing
Your code work well.
You can try to clean build folder CMD + shift + K or clear derived data in ~/Library/Developer/Xcode/DerivedData

How do I create a transition between from Login View to Tab View in SwiftUI?

Here's the snippet of code from LoginView:
Button(action: {
if loginAndPasswordAreOK() {
// Perform Segue to TabView
} else {
self.isValidLoginAndPassword = false
self.email = ""
self.password = ""
}
}, label: {
and there's a piece of code of MainTabView (aka Home Tab):
struct MainTabView: View {
var body: some View {
TabView {
Text("Home Tab")
.font(.system(size: 30, weight: .bold, design: .rounded))
.tabItem {
Image(systemName: "house.fill")
Text("Home")
}
I googled around and saw NavigationLink or something but I don't want to wrap up this transition to a NavController at all.
Here:
import SwiftUI
struct ContentView: View {
#State var isPassOk: Bool = false
var userPass: String = "1234"
#State var userGivenPass: String = ""
var body: some View {
if isPassOk == false
{
HStack
{
TextField("Enter your Pass Here!", text: $userGivenPass)
.textFieldStyle(RoundedBorderTextFieldStyle())
Button("Log in") {
// do your logig Here!
if userGivenPass == userPass
{
withAnimation(.easeInOut)
{
isPassOk = true
}
}
else
{
isPassOk = false
}
}
}
.font(Font.title)
.padding()
}
else if isPassOk == true
{
TabView {
Text("Home Tab")
.font(.system(size: 30, weight: .bold, design: .rounded))
.tabItem {
Image(systemName: "house.fill")
Text("Home")
}
}
}
}
}
Here Updated for you:
import SwiftUI
struct ContentView: View {
#State var isPassOk: Bool = false
var body: some View {
if isPassOk == false
{
LogInView(isPassOk: $isPassOk)
}
else if isPassOk == true
{
MainTabView()
}
}
}
struct LogInView: View {
#Binding var isPassOk: Bool
var userPass: String = "1234"
#State var userGivenPass: String = ""
var body: some View {
HStack
{
TextField("Enter your Pass Here!", text: $userGivenPass)
.textFieldStyle(RoundedBorderTextFieldStyle())
Button("Log in") {
// do your logig Here!
if userGivenPass == userPass
{
withAnimation(.easeInOut)
{
isPassOk = true
}
}
else
{
isPassOk = false
}
}
}
.font(Font.title)
.padding()
}
}
struct MainTabView: View {
var body: some View {
TabView {
Text("Home Tab")
.font(.system(size: 30, weight: .bold, design: .rounded))
.tabItem {
Image(systemName: "house.fill")
Text("Home")
}
}
}
}

SwiftUI automatically go to next view on success

I have a login view that does an http request . Once we get the http response we know whether the user can go to the next view . I am wondering how can I trigger the next view without click ? I am not looking to do a sheet since I want to get the full screen mode . I have been looking at this Go to a new view using SwiftUI but no luck . From my code below when I click on Press on me navigationLink I can go to the correct view, however I need that same functionality to work without clicking in the http response below where it says decode.status == 1 because the user has authenticated successfully .
struct ContentView: View {
#State var email: String = ""
#State var password: String = ""
#State var message: String = ""
#State var status: Int = -10
#State var showActive = true
var body: some View {
NavigationView {
ZStack {
Color(UIColor.systemGroupedBackground)
.edgesIgnoringSafeArea(.all)
VStack(alignment: .center) {
Spacer()
NavigationLink(destination: MainView()) {
Text("Press on me")
}.buttonStyle(PlainButtonStyle()) // This works when clicked
TextField("Email", text: $email)
.padding(10)
.textFieldStyle(RoundedBorderTextFieldStyle())
.background(Color(UIColor(hexString: ForestGreen)))
.foregroundColor(Color.black)
SecureField("Password", text: $password)
.padding(10)
.textFieldStyle(RoundedBorderTextFieldStyle())
.background(Color(UIColor(hexString: ForestGreen)))
.foregroundColor(Color.black)
Button(action: {
guard let url = URL(string:ConnectionString+"profile/login") else { return }
let parameter = "email=\(self.email)&password=\(self.password)"
let request = RequestObject(AddToken: true, Url: url, Parameter: parameter)
URLSession.shared.dataTask(with:request, completionHandler: {(data, response, error) in
if let decode = try? JSONDecoder().decode(ProfileCodable.self, from: data!)
{
self.status = decode.status ?? -10
self.message = decode.message ?? ""
if decode.status == 0 {
print("Invalid Credentials")
} else if decode.status == 1 {
// ** Go to next View here **
} else if decode.status == -1 {
print("Error")
}
} else {
print("No Response")
}
}).resume()
}) {
Text("Login")
.padding(10)
.frame(minWidth: 0, maxWidth: .infinity)
.font(.system(size: 22))
.foregroundColor(Color(UIColor(hexString: "#006622")))
.overlay(
RoundedRectangle(cornerRadius: 40)
.stroke(Color.black, lineWidth: 1))
}.padding([.top],40)
if self.status == 0 {
Text(self.message)
.foregroundColor(.red)
.font(.system(size: 20))
.padding([.top],30)
}
Spacer()
}.padding()
}
}
}
}
Try this (scratchy - not tested because not compilable due to absent dependencies), so adapt at your side:
struct ContentView: View {
#State var email: String = ""
#State var password: String = ""
#State var message: String = ""
#State var status: Int = -10
#State var showActive = false // << seems this state, so false
var body: some View {
NavigationView {
ZStack {
Color(UIColor.systemGroupedBackground)
.edgesIgnoringSafeArea(.all)
VStack(alignment: .center) {
Spacer()
TextField("Email", text: $email)
.padding(10)
.textFieldStyle(RoundedBorderTextFieldStyle())
.background(Color(UIColor(hexString: ForestGreen)))
.foregroundColor(Color.black)
SecureField("Password", text: $password)
.padding(10)
.textFieldStyle(RoundedBorderTextFieldStyle())
.background(Color(UIColor(hexString: ForestGreen)))
.foregroundColor(Color.black)
Button(action: {
guard let url = URL(string:ConnectionString+"profile/login") else { return }
let parameter = "email=\(self.email)&password=\(self.password)"
let request = RequestObject(AddToken: true, Url: url, Parameter: parameter)
URLSession.shared.dataTask(with:request, completionHandler: {(data, response, error) in
if let decode = try? JSONDecoder().decode(ProfileCodable.self, from: data!)
{
self.status = decode.status ?? -10
self.message = decode.message ?? ""
if decode.status == 0 {
print("Invalid Credentials")
} else if decode.status == 1 {
self.showActive = true // << here !!
} else if decode.status == -1 {
print("Error")
}
} else {
print("No Response")
}
}).resume()
}) {
Text("Login")
.padding(10)
.frame(minWidth: 0, maxWidth: .infinity)
.font(.system(size: 22))
.foregroundColor(Color(UIColor(hexString: "#006622")))
.overlay(
RoundedRectangle(cornerRadius: 40)
.stroke(Color.black, lineWidth: 1))
}.padding([.top],40)
.background(
// activated by state programmatically !!
NavigationLink(destination: MainView(), isActive: $self.showActive) {
EmptyView() // << just hide
}.buttonStyle(PlainButtonStyle())
)
if self.status == 0 {
Text(self.message)
.foregroundColor(.red)
.font(.system(size: 20))
.padding([.top],30)
}
Spacer()
}.padding()
}
}
}
}
Simply use the isActive property of navigation link. It would look like this:
NavigationLink(destination: MainView(), isActive: $mainViewActive) {
Text("Press on me")
}.buttonStyle(PlainButtonStyle())
and you should also declare the variable in your view:
#State var mainViewActive = false
then on successful login simply change the value to true. If you also do not want to display an actual link use EmptyView() as wrapper. So it would look like this:
NavigationLink(destination: MainView(), isActive: $mainViewActive) {
EmptyView()
}

Resources