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 BudgetCategoryCell
s 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 usingSet(array)
, this will remove the duplicates - turn the
Set
back to an array by usingArray(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 theForEach
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 insideList
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
Using Animoji/Memoji as Profile Photo
Cloudkit Ckqueryoperation Doesn't Get All Records
Changing Font Color of Uibarbuttonitem
What Is the Slice Compare Logic in Swift
Pop View Controller Using Screen Edge Pan Gesture Recogniser Not Following the Thumb
Swift/Scenekit Problems Getting Touch Events from Scnscene and Overlayskscene
How to Use a Completion Handler to Put an Image in a Swiftui View
Arkit - How to Display the Feed from a Virtual Scncamera Placed on Scnplane
What Is Existentialmetatype in Swift
Enabling Gestures in Realitykit
Curl with Alamofire - Swift - Multipart/Form-Data
Swift: Overriding Typealias Inside Subclass
String Comparison in Swift Is Not Transitive
Swift Codable - Parse JSON Array Which Can Contain Different Data Type
Non Exhaustive List When Handling Errors Inside a Class Function in Swift