I’m searching for a way to simplify/refactor the addition of .onChange(of:) in a SwiftUI
view that has MANY TextFields. If a solution were concise, I would also move the modifier
closer to the appropriate field rather than at the end of, say, a ScrollView. In this
case, all of the .onChange modifiers call the same function.
Example:
.onChange(of: patientDetailVM.pubFirstName) { x in
changeBackButton()
}
.onChange(of: patientDetailVM.pubLastName) { x in
changeBackButton()
}
// ten+ more times for other fields
I tried "oring" the fields. This does not work:
.onChange(of:
patientDetailVM.pubFirstName ||
patientDetailVM.pubLastName
) { x in
changeBackButton()
}
This is the simple function that I want to call:
func changeBackButton() {
withAnimation {
showBackButton = false
isEditing = true
}
}
Any guidance would be appreciated. Xcode 13.2.1 iOS 15
5
Answers
Any time you are duplicating code you want to move it one level down so the same code can be reused.
Here is a solution, the parent view will hold a variable that will know if the "name" as a whole has changes.
Another way you can do this is to make a combined publisher for both
pubFirstName
andpubLastName
.Add following function to your
viewModel
and listen to
nameChanged
publisher ononReceive
of your viewso you can listen to either first or last name change.
Didn’t test the code but just as an idea.
Here’s a fairly DRY approach I came up with. Obviously, once you’ve written the code that defines the
NameKeyPathPairs
struct, and the extension toArray
, etc., it’s very simple to use.Example Usage
.onChange Helper Code
Remaining Code of Example
Overview of Solution
We extend the
Binding
type, to create two new methods, both of which are calledonChange
.Both
onChange
methods are intended to be used in situations in which you need to perform some work whenever theBinding
instance’swrappedValue
property is changed (not just set) via itsset
method.The first
onChange
method doesn’t pass the new value of theBinding
instance’swrappedValue
property to the provided on-change callback method, whereas the secondonChange
method does provide it with the new value.The first
onChange
method allows us to refactor this:to this:
Solution
Helper-Code
Usage
Benefits of Solution
Binding
instance is provided to the TextField/TextEditor/other type.Binding
instance that has awrappedValue
property of any type that conforms to theEquatable
protocol.Binding
instances that have on-change callbacks, look just likeBinding
instances that don’t have on-change callbacks. Consequently, no types to which theseBinding
instances with on-change callbacks are provided, need special modifications to know how to deal with them.View
‘s,@State
properties,ObservableObject
‘s,EnvironmentKey
‘s,PreferenceKey
‘s, or any other types. It simply adds a couple of methods to the existing type calledBinding
– which obviously is a type that would have already been being used in the code…Why not just use a computed var?