This is rather a convoluted problem, maybe a starter of a discussion for an application pattern.
I am trying to create an application for editing dynamic objects (structure can not be expressed as a struct/class - therefore it is not known to the application developer), which can be simulated using a dictionary for now. There might be multiple collections (or filters) of such objects. In each collection an user can select one or multiple of the objects and inspect them using a third view: an inspector. Inspector can inspect and set properties of one or more objects.
The distilled application idea with some concept mockups and more detailed information is here at Github.
WARNING: The application DOES NOT WORK as intended, it is just there to demonstrate some issues.
It might look a bit convoluted, but can not think of a simpler solution for given problem. Tried to extract the essentials.
Either I have deep misunderstanding of SwiftUI (knowledge comes mostly from official docs), or there is something obscure going on.
The application views are as follows:
Window
|
+-- Content View (*>)
|
+-- Navigation View
| |
| +-- [sidebar] List of Thing Filters
| |
| +-- ThingListView(<*) – list of things, selectable, multiple
|
+-- Inspector(<*) – of selection in the above
(*>) Selection – state, source of truth
(<*) Selection - binding to
"Visual" representation:
Navigation Inspector
|<----------------------->|<--------->|
+----------+--------------+-----------+
| Filter 1 |**Thing 1*****| Selected: |
| Filter 2 | Thing 2 | 1,3 |
| .... |**Thing 3*****| |
| | ... | Name: ___ |
+----------+--------------+-----------+
^
|
+------- this can be 2D canvas, can be anything to present
a collection of selectable objects.
Note: The ThingListView
might as well be some kind of Canvas, or any other view, it does not have to be just a list view.
Issue 1
I am experiencing an issue where the binding set
is called on no change in any of the the Inspector subviews. This has a negative side-effect on the model.
- Pick a Filter (Thing collection)
- Select multiple (more than one) items
- One of (any):
- Change selection to one item
- Switch to another Filter
Result: All previously selected items will be changed.
Expected: No change happens.
What happened: set
of a binding (see below) to a text filed is called on selection change, not on text field content change.
The offending code location: Inspector.swift -> class Inspector
-> var body
-> nameBinding
-> set
. For the context the simplified listing is here. Note the name
and nameBinding
are just mockups, there might be more properties as well as different kinds of inspectors.
struct Inspector: View {
@State var name: String = ""
// ... more state is defined here ...
var body: some View {
let nameBinding = Binding<String>(
get: {
// get one or coalesce multiple values of a dynamic property `name`
},
set: { text in
// set the property in all selected objects
}
)
// ... view definition continues here
}
}
Issue 2
This is minor, but noticed that the get
of the binding is called very frequently.
Question
How to make SwitfUI to call set
of a binding only when the value actually changed?
Alternatively: if I did not get the solution approach right, what is the more paradigmatic way of solving the
(Collections → Things) → Inspector
application flow when the objects have dynamic properties (can not be expressed as structs/classes known to the application developer) and one can inspect a multi-object selection?