Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Refreshing a SwiftUI List

Ím trying to refresh this List whenever I click on a NavLink

NavigationView {
    List(feed.items.indices, id:\.self) { i in
        NavigationLink(destination: ListFeedItemDetail(idx: i).environmentObject(self.feed)) {
            ListFeedItem(item: self.$feed.items[i])
        }
    }
}

The list is made out of an array inside an environment object. The problem: It does only refresh when I switch to another tab or close the app I had used a modal View before and it worked there. (I did it with .onAppear) Any Ideas?

Example

Problem: When you tap on an item in the list and tap the toggle button the EnvironmentObject is changed but this changed is only reflected when I change the tab and change it back again

import SwiftUI
import Combine

struct TestView: View {

    @State var showSheet: Bool = false
    @EnvironmentObject var feed: TestObject
    func addObjects() {
        var strings = ["one","two","three","four","five","six"]
        for s in strings {
            var testItem = TestItem(text: s)
            self.feed.items.append(testItem)
        }
    }

    var body: some View {
        TabView {
            NavigationView {
                List(feed.items.indices, id:\.self) { i in
                    NavigationLink(destination: detailView(feed: self._feed, i: i)) {
                        HStack {
                            Text(self.feed.items[i].text)
                            Text("(\(self.feed.items[i].read.description))")
                        }
                    }
                }
            }
                .tabItem({ Text("Test") })
                .tag(0)

            Text("Blank")
                .tabItem({ Text("Test") })
                .tag(0)
        }.onAppear {
            self.addObjects()
        }
    }
}

struct detailView: View {
    @EnvironmentObject var feed: TestObject
    var i: Int
    var body: some View {
        VStack {
            Text(feed.items[i].text)
            Text(feed.items[i].read.description)
            Button(action: {  self.feed.items[self.i].isRead.toggle() }) {
                Text("Toggle read")
            }
        }
    }
}

final class TestItem: ObservableObject {
    init(text: String) {
        self.text = text
        self.isRead = false
    }
    static func == (lhs: TestItem, rhs: TestItem) -> Bool {
        lhs.text < rhs.text
    }
    var text: String
    var isRead: Bool

    let willChange = PassthroughSubject<TestItem, Never>()
    var read: Bool {
        set {
            self.isRead = newValue
        }
        get {
            self.isRead
        }
    }
}

class TestObject: ObservableObject {
    var willChange = PassthroughSubject<TestObject, Never>()

    @Published var items: [TestItem] = [] {
        didSet {
            willChange.send(self)
        }
    }
}
like image 650
nOk Avatar asked Aug 30 '19 13:08

nOk


People also ask

How do I force refresh a view in SwiftUI?

you add the modifier . refreshable and you provide a function to refresh the content inside the closure. As I told you, this feature is powered by async/await, so we need the new await keyword before the function call.

Can you reuse a view SwiftUI?

SwiftUI makes it easy to refactor and reuse code in many features. If both views only used List to display gems, with no other view inside, it would be straightforward to extract the code into another view and use it in both features.

Is SwiftUI production ready 2021?

SwiftUI is more than ready for simple applications, but at the time of this writing (iOS 15, beta 4), I don't think SwiftUI is production-ready yet for complex applications, mainly due to the issues with ScrollViews and the heavy reliance on UIViewRepresentable . It breaks my heart.


1 Answers

I had a similar problem, this is the hack I came up with.

In your "TestView" declare:

@State var needRefresh: Bool = false

Pass this to your "detailView" destination, such as:

NavigationLink(destination: detailView(feed: self._feed, i: i, needRefresh: self.$needRefresh)) {
     HStack {
            Text(self.feed.items[i].text)
            Text("(\(self.feed.items[i].read.description))")
     }.accentColor(self.needRefresh ? .white : .black)
 }

Note ".accentColor(self.needRefresh ? .white : .black)" to force a refresh when "needRefresh" is changed.

In your "detailView" destination add:

@Binding var needRefresh: Bool

Then in your "detailView" in your Button action, add:

self.needRefresh.toggle()
like image 187
workingdog support Ukraine Avatar answered Nov 03 '22 12:11

workingdog support Ukraine