0

this is a Macos app where the parsclass is setup in a previous view that contains the YardageRowView below. That previous view is responsible for changing the contents of the parsclass. This is working is other views that use a NavigationLink to display the views.

When the parsclass is changed, this view is refreshed, but the previous value is put in the text field on the holeValueTestView.

I cannot comprehend how the value is not being passed into the holeValueTestView correctly

This is a view shown as a .sheet, and if I dismiss it and display it again, everything is fine.

if you create a macOS project called YardageSample and replace the ContentView.swift and YardageSampleApp.swift with the two files below, you can see that the display in red changes and the black numbers do not change until you click Done and redisplay the .sheet

//
//  YardageSampleApp.swift
//  YardageSample
//
//  Created by Brian Quick on 2021-04-12.
//

import SwiftUI

@main
struct YardageSampleApp: App {
    @StateObject var parsclass = parsClass()
    var body: some Scene {
        WindowGroup {
            ContentView()
                .environmentObject(parsclass)
        }
    }
}
//
//  ContentView.swift
//  YardageSample
//
//  Created by Brian Quick on 2021-04-12.
//

import SwiftUI

struct ContentView: View {
    @StateObject var parsclass = parsClass()

    enum ActiveSheet : String , Identifiable {
        case CourseMaintenance
        var id: String {
            return self.rawValue
        }
    }
    @State var activeSheet : ActiveSheet? = nil
    var body: some View {
        Button(action: {
            self.activeSheet = .CourseMaintenance
        }) {
            Text("Course Maintenance")
        }
        .sheet(item: $activeSheet) { sheet in
            switch sheet {
            case .CourseMaintenance:
                CourseMaintenance()
            }
        }.frame(width: 200, height: 200, alignment: /*@START_MENU_TOKEN@*/.center/*@END_MENU_TOKEN@*/)
    }
}

class parsClass: ObservableObject {
    @Published var pars = [parsRec]()

    init() {

        self.pars = [parsRec]()
        self.pars.append(parsRec())
    }
    func create(newpars: [parsRec]) {
        pars.removeAll()
        pars = newpars
    }
}
class parsRec: Identifiable, Codable {
         var id = UUID()
         var Hole = 1
         var Yardage = 1
}


struct CourseMaintenance: View {

    @EnvironmentObject var parsclass: parsClass
    @Environment(\.presentationMode) var presentationMode
    var body: some View {
        VStack {
            Button(action: {presentationMode.wrappedValue.dismiss()}, label: {
                Text("Done")
            })
            Button(action: {switchScores(number: 1)}, label: {
                Text("Button 1")
            })
            Button(action: {switchScores(number: 2)}, label: {
                Text("Button 2")
            })
            Button(action: {switchScores(number: 3)}, label: {
                Text("Button 3")
            })
            CourseDetail().environmentObject(parsclass)

        }.frame(width: 400, height: 400, alignment: .center)
    }


    func switchScores(number: Int) {
        var newparRecs = [parsRec]()
        for i in 0..<17 {
            let myrec = parsRec()
            myrec.Hole = i
            myrec.Yardage = number
            newparRecs.append(myrec)
        }
        parsclass.create(newpars: newparRecs)
    }
}
struct CourseDetail: View {
    @EnvironmentObject var parsclass: parsClass
    var body: some View {
        HStack(spacing: 0) {
            ForEach(parsclass.pars.indices, id: \.self) { indice in
                // this displays the previous value
                holeValueTestView(value: String(parsclass.pars[indice].Yardage))
                // this displays the correct value after parsclass has changed
                Text(String(parsclass.pars[indice].Yardage))
                    .foregroundColor(.red)
            }
        }
    }
}
struct holeValueTestView: View {
      @State var value: String

    var body: some View {
        //TextField(String(value), text: $value)
        Text(value)
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

BrianQ
  • 55
  • 6
  • You are likely presenting the sheet with `isPresented` and will need to switch to `item:` to get the behavior you're looking for. See: https://stackoverflow.com/a/66287123/560942 – jnpdx Apr 12 '21 at 19:49
  • changed to item: with the same results. don't forget this is working when the sheet is re-presented, only when the @environmentObject is changed within the view – BrianQ Apr 12 '21 at 21:12
  • I'd suggest you provide a [mre] – jnpdx Apr 12 '21 at 21:15
  • changed the question to contain code that can be done to show the problem – BrianQ Apr 13 '21 at 09:37

1 Answers1

0

There are a couple of issues going on:

  1. You have multiple instances of parsClass. One is defined in YardageSampleApp and passed into the view hierarchy as a @EnvironmentObject. The second is defined in ContentView as a @StateObject. Make sure you're only using one.

  2. On holeValueTestView, you defined value as a @State variable. That gets set initially when the view is created by its parent and then it maintains its own state. So, when the environmentObject changed, because it was in charge of its own state at this point, it didn't update the value. You can simply remove @State and see the behavior that you want.

struct ContentView: View {
    @EnvironmentObject var parsclass : parsClass //<-- Here

    enum ActiveSheet : String , Identifiable {
        case CourseMaintenance
        var id: String {
            return self.rawValue
        }
    }
    @State var activeSheet : ActiveSheet? = nil
    var body: some View {
        Button(action: {
            self.activeSheet = .CourseMaintenance
        }) {
            Text("Course Maintenance")
        }
        .sheet(item: $activeSheet) { sheet in
            switch sheet {
            case .CourseMaintenance:
                CourseMaintenance()
            }
        }.frame(width: 200, height: 200, alignment: .center)
    }
}
struct holeValueTestView: View {
    var value: String //<-- Here

    var body: some View {
        Text(value)
    }
}

As a side note: In Swift, normally type names are capitalized. If you want to write idiomatic Swift, you would change your parsClass to ParsClass for example.

jnpdx
  • 45,847
  • 6
  • 64
  • 94
  • Thank you so much...I got hung up on entering the values into the holeValueTestView scenario that I didn't even think of just a var...Needless to say, this is a bigger project than this. So the extra @StateObject snuck in creating this sample. as for the style, between Camel case, Pascal case, Snake case and my old standards, I am fighting with what/when to capitalize. If only I was a noobee with no pre-conditioning:-) – BrianQ Apr 13 '21 at 18:07