Swiftui: How to Update a List That Is Driven by a Static Set of Data and Pulls Bits of Information from Another Dynamic Set of Data

SwiftUI: How do I update a List that is driven by a static set of data and pulls bits of information from another dynamic set of data?

It turns out that the List was correctly refreshing, but the BudgetCategoryCells within it were not. I pulled out the body of BudgetCategoryCell directly into the ForEach and all of a sudden it started working as expected. Now I need to figure out how to make that view update, but this is a matter for another question.

Sorting Filtered Data by Date

You don't need to "save" the elements in a set - you can just:

  • turn the array into a Set by simply using Set(array), this will remove the duplicates
  • turn the Set back to an array by using Array(yourSetHere)
  • sort it
  • turn the array of dates into an array of strings

Here's what uniqueBankDates should look like (WdModel shall be conform to Hashable):

    var uniqueBankDates: [String] {
Array(Set(wdvm.wdArray)) // This will remove duplicates, but WdModel needs to be Hashable
.sorted { $0.wdDate < $1.wdDate } // Compare dates
.compactMap {
$0.wdDate.formatted(date: .abbreviated, time: .omitted) // Return an array of formatted the dates
}
}

Is there a way to modify fetched results with a predicate after they are initialized?

Is there a way to modify fetched results with a predicate after they
are initialized?

Well... no, not in the way you try to do this, and even if you'd try to create it with NSFetchRequest instance, which is reference, and allows to change predicate later, that wouldn't work, because SwiftUI's FetchRequest stores copy of provided fetch request (or creates own with provided parameters)... so, no. But...

You can break apart view providing fetch request parameters with view constructing fetch request and showing result.

Here is a demo of approach (important part of it) which gives you possibility to get results with different dynamically changed predicates:

struct MasterView: View {
@State var predicate: NSPredicate? = nil
var body: some View {
VStack {
Button(action: { // button just for demo
self.predicate = NSPredicate(format: "title contains[c] %@", "h")
}, label: { Text("Filter") })
ResultView(predicate: self.predicate)
}
}
}

struct ResultView: View {

@FetchRequest
var events: FetchedResults<Event>

@Environment(\.managedObjectContext)
var viewContext

init(predicate: NSPredicate?) {
let request: NSFetchRequest<Event> = Event.fetchRequest()
request.sortDescriptors = [NSSortDescriptor(keyPath: \Event.timestamp, ascending: true)]
if let predicate = predicate {
request.predicate = predicate
}
_events = FetchRequest<Event>(fetchRequest: request)
}

var body: some View {
List {
ForEach(events, id: \.self) { event in
...

SwiftUI View and @FetchRequest predicate with variable that can change

had the same problem, and a comment of Brad Dillon showed the solution:

var predicate:String
var wordsRequest : FetchRequest<Word>
var words : FetchedResults<Word>{wordsRequest.wrappedValue}

init(predicate:String){
self.predicate = predicate
self.wordsRequest = FetchRequest(entity: Word.entity(), sortDescriptors: [], predicate:
NSPredicate(format: "%K == %@", #keyPath(Word.character),predicate))

}

in this example, you can modify the predicate in the initializer.

What is the difference between List and ForEach in SwiftUI?

  • ForEach is a view that lets you pass a collection of data to its initializer and then creates multiple "subviews" from the closure you provide. It doesn't have any semantics on how the views will be arranged.

    Example:

      ForEach(1..<5) { row in
    Text("Row \(row)")
    }

    will create the equivalent off

      Text("Row 1")
    Text("Row 2")
    Text("Row 3")
    Text("Row 4")

    wrapped in a single container view.

  • List is a view that can compose multiple views together, but not necessarily views of the same type. You can simply add multiple views without any loop.

    Example 1:

      List {
    Image("avatar")
    Text("Title")
    Button(action: {
    print("Button tapped!")
    }) {
    Text("Energize!")
    }
    }

    As a convenience, the List initializer allows you to use it just like the ForEach view in case you want to have a list consisting of a single cell type only.

    Example 2:

      List(1..<5) { row in
    Text("Row \(row)")
    }

    A list has a special appearance, depending on the platform. For example, on iOS a list will appear as a table view and insert separator lines between its vertically stacked views.

    You can use ForEach views inside List views to have both dynamic and static content – a very powerful feature of SwiftUI.

    Example 3:

      List {
    Text("Food")
    ForEach(meals) { meal in
    Text(meal.name)
    }
    Text("Drinks")
    ForEach(drinks) { drink in
    Text(drink.name)
    }
    }

Unwanted animation when moving items in SwiftUI list

Here is solution (tested with Xcode 11.4 / iOS 13.4)

var body: some View {
NavigationView {
List {
ForEach(numbers, id: \.self) { number in
HStack {
Text(number)
}.id(UUID()) // << here !!
}
.onMove {
self.numbers.move(fromOffsets: $0, toOffset: $1)
}
}
.navigationBarItems(trailing: EditButton())
}
}


Related Topics



Leave a reply



Submit