问题
I'm new to SwiftUI and I'm having problems with presenting Alerts back-to-back.
The description of the .alert(item:content:)
modifier has this written in it's definition:
/// Presents an alert.
///
/// - Parameters:
/// - item: A `Binding` to an optional source of truth for the `Alert`.
/// When representing a non-nil item, the system uses `content` to
/// create an alert representation of the item.
///
/// If the identity changes, the system will dismiss a
/// currently-presented alert and replace it by a new alert.
///
/// - content: A closure returning the `Alert` to present.
public func alert<Item>(item: Binding<Item?>, content: (Item) -> Alert) -> some View where Item : Identifiable
I'm particularly interested in the If the identity changes, the system will dismiss a currently-presented alert and replace it by a new alert
part. Since I want Alerts to be presented back-to-back, if I'm somehow able to change the 'identity', I'll be able to achieve the functionality that I want - which is having the system dismiss the currently-presented alert and replacing the old Alert with a new Alert (back-to-back).
If someone can explain to me what 'identity' is and how I can change the 'identity' of something I'll be extremely grateful.
(Or if you know a better way to present alerts back-to-back that'll also be very very helpful.)
Thanks in advance!
回答1:
Find below demo of alert-by-item usage. And some investigation results about changing identity as documented.
As you find experimenting with example (by tap on row) alert activated by user interaction works fine, but changing identity programmatically, as documented, seems not stable yet, however alert is really updated.
Tested with Xcode 11.4 / iOS 13.4
struct SomeItem: Identifiable { // identifiable item
var id: Int // identity
}
struct DemoAlertOnItem: View {
@State private var selectedItem: SomeItem? = nil
var body: some View {
VStack {
ScrollView (.vertical, showsIndicators: false) {
ForEach (0..<5) { i in
Text("Item \(i)").padding()
.onTapGesture {
self.selectedItem = SomeItem(id: i)
// below simulate change identity while alert is shown
DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
self.selectedItem = nil // works !!
// self.selectedItem?.id = 100 // crash !!
// self.selectedItem = SomeItem(id: 100) // crash !!
}
}
}
}
}
.alert(item: self.$selectedItem) { item in
Alert(title: Text("Alert"), message: Text("For item \(item.id)"), dismissButton: .default(Text("OK")))
}
}
}
回答2:
Notice how this method requires a Binding<Item?>
, and that Item
should be Identifiable
. For the Binding<Item?>
parameter, you're supposed to pass in a "source of truth" that controls what the alert shown looks like, or whether the alert shows at all. When this source of truth changes (i.e. becomes something else), the view will update the alert.
But here's the problem, how does SwiftUI know what does "change" mean in the context of your model? Let's say Item
is a Person
class that you wrote. Person
has a name
and age
. It is your job to tell SwiftUI, that "a Person
becomes a totally different person when its name changes". (Of course, you could have some other definition of what is meant by "a person changes" here. This definition is just an example.)
struct Person : Identifiable {
var id: String {
name
}
let name: String
let age: Int
}
This is why Item
must be Identifiable
. Item.id
is thus the "identity".
Note that Identifiable
is different from Equatable
, in that Identifiable
asks the question "what makes this person a different person?" whereas Equatable
asks "what result would you want ==
to give?". See this for another example.
how do we change the 'identity' of something?
Just change the binding you pass in (e.g. setting the @State
that the binding is based on) in such a way that its id
changes.
来源:https://stackoverflow.com/questions/61183557/what-does-identity-mean-in-swifui-and-how-do-we-change-the-identity-of-somet