0

The clocks will freeze once the detail view is fully presented.

If you replace List with a ScrollView/VStack the propagation will flow.

struct ContentView: View {

    @State private var clocks = [Date(), Date(), Date()]
    
    var body: some View {
        NavigationView {
            List {
                ForEach(clocks.indices) { idx in
                    NavigationLink(destination: Text(clocks[idx], formatter: formatter)) {
                        Text("Counter \(idx)")
                    }
                }
            }
        }
        .onReceive(Timer.publish(every: 1, on: .main, in: .default).autoconnect()) {
            clocks[0] = $0
            clocks[1] = $0
            clocks[2] = $0
        }
    }
    
    var formatter: DateFormatter {
        let formatter = DateFormatter()
        formatter.timeStyle = .medium
        return formatter
    }
}
Lorenzo Fiamingo
  • 3,251
  • 2
  • 17
  • 35

1 Answers1

1

Your destination (Text) is just capturing a static moment (a Date) from clocks and doesn't have a conception of the fact that it's updating.

Why this happens in a List and not a ScrollView is a little mysterious, but I'm going to assume that SwiftUI is doing some stuff under the hood to try to determine if List rows are identical and not re-rendering them if it thinks it doesn't need to, for efficiency, which ScrollView and VStack don't do.

Here's a possible solution that uses an ObservableObject view model to pass the data between views and keep it updating via a Published property.

class ViewModel : ObservableObject {
    @Published var time = Date()
    @Published var clocks = [Date(), Date(), Date()]
    
    var cancellable : AnyCancellable?
    
    init() {
        cancellable = Timer.publish(every: 1, on: .main, in: .default)
            .autoconnect()
            .sink { (date) in
                self.clocks[0] = date
                self.clocks[1] = date
                self.clocks[2] = date
                print(date)
            }
    }
}

struct ContentView: View {

    @ObservedObject var viewModel = ViewModel()
    
    var body: some View {
        NavigationView {
            List {
                ForEach(viewModel.clocks.indices) { idx in
                    NavigationLink(destination: DetailView(index: idx, viewModel: viewModel)) {
                        Text("Counter \(idx)")
                    }
                }
            }
        }.navigationViewStyle(StackNavigationViewStyle())
    }

}

struct DetailView : View {
    var index : Int
    @ObservedObject var viewModel : ViewModel
    
    var body: some View {
        Text(viewModel.clocks[index], formatter: formatter)
    }
    
    var formatter: DateFormatter {
            let formatter = DateFormatter()
            formatter.timeStyle = .medium
            return formatter
        }
}
jnpdx
  • 45,847
  • 6
  • 64
  • 94
  • IMHO it's a good practice to make the formatter *static* - a computed property will essentially be re-created every time the body is computed. An example can be found here: [How to format text inside text views](https://www.hackingwithswift.com/quick-start/swiftui/how-to-format-text-inside-text-views). – pawello2222 Feb 06 '21 at 23:44
  • Definitely true -- didn't address that from the OP's code – jnpdx Feb 06 '21 at 23:48