在现有动画为 运行 时为 SwiftUI 状态更改设置动画会导致 Form 处于不一致状态
Animating a SwiftUI state change while existing animation is running causes Form to be in an inconsistent state
我正在尝试实现一个文本字段,在用户输入时更新搜索结果。我已经尝试过同步(每次击键后更新搜索结果)和异步。两者都会导致一些非常奇怪的动画状态发生。
事情是这样的。我先慢慢打字,然后慢慢退格,然后一切正常。然后我快速输入并快速退格。
我已将我的观点简化为:
import SwiftUI
struct ContentView: View {
@State var elements: [Element] = []
@State var text = ""
var body: some View {
Form {
TextField("Text", text: $text)
.onChange(of: text, perform: { newText in
withAnimation {
elements = (0..<newText.count).map { i in
Element(id: i, value: "\(i)")
}.shuffled()
}
})
ForEach(elements) { element in
Text(element.value)
}
Text("Should have \(elements.count) elements")
}
}
}
struct Element: Identifiable {
var id: Int
var value: String
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
如果您在预览、模拟器或设备上的文本字段中输入几个字符,然后按住退格键,表单将处于不一致状态。很多时候,即使文本字段为空,仍然有一个值为“0”的文本元素。我还看到过“应该有 X 元素”文本位于“0”文本元素行上方的情况。发生这种情况后,更改状态时将不会再发生动画。
问题似乎出在我更改 elements
状态时,而现有动画是 运行,它正在动画先前的 elements
状态更改。如果您在文本字段中缓慢键入并缓慢退格,则不会出现此问题。
如果我使用列表而不是表单,也会发生这种情况。如果您使用 VStack,则不会发生这种情况。
编辑:
为了尽可能多地删除变量,我缩小了我的示例(见下文)。问题仍然存在。如果您输入一堆字符,然后按住退格键,底部的文本将显示“应该有 0 个元素”,但列表中仍有包含“旧”数据的行。这是在实际设备上的 iOS 14.4 上发生的。
我看到控制台中弹出此消息:
2021-01-27 12:01:33.231810-0500 Animations[1220:293840] [Snapshotting] Snapshotting a view (0x10410d190, _UIReplicantView) that has not been rendered at least once requires afterScreenUpdates:YES.
import SwiftUI
struct ContentView: View {
@State var text = ""
var body: some View {
TextField("Text", text: $text)
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding()
List {
ForEach(elements) { element in
Text(element.value)
}
}
.animation(.default)
Text("Should have \(elements.count) elements")
.background(Color(.systemRed))
.padding()
}
private var elements: [Element] {
(0..<text.count).map { i in
Element(id: i, value: "\(i)")
}.shuffled()
}
}
struct Element: Identifiable {
var id: Int
var value: String
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
最近刚刚对此进行了测试,发现此问题不再发生(至少从 iOS 15.3 开始,但可能是整个 iOS 15)。
我正在尝试实现一个文本字段,在用户输入时更新搜索结果。我已经尝试过同步(每次击键后更新搜索结果)和异步。两者都会导致一些非常奇怪的动画状态发生。
事情是这样的。我先慢慢打字,然后慢慢退格,然后一切正常。然后我快速输入并快速退格。
我已将我的观点简化为:
import SwiftUI
struct ContentView: View {
@State var elements: [Element] = []
@State var text = ""
var body: some View {
Form {
TextField("Text", text: $text)
.onChange(of: text, perform: { newText in
withAnimation {
elements = (0..<newText.count).map { i in
Element(id: i, value: "\(i)")
}.shuffled()
}
})
ForEach(elements) { element in
Text(element.value)
}
Text("Should have \(elements.count) elements")
}
}
}
struct Element: Identifiable {
var id: Int
var value: String
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
如果您在预览、模拟器或设备上的文本字段中输入几个字符,然后按住退格键,表单将处于不一致状态。很多时候,即使文本字段为空,仍然有一个值为“0”的文本元素。我还看到过“应该有 X 元素”文本位于“0”文本元素行上方的情况。发生这种情况后,更改状态时将不会再发生动画。
问题似乎出在我更改 elements
状态时,而现有动画是 运行,它正在动画先前的 elements
状态更改。如果您在文本字段中缓慢键入并缓慢退格,则不会出现此问题。
如果我使用列表而不是表单,也会发生这种情况。如果您使用 VStack,则不会发生这种情况。
编辑:
为了尽可能多地删除变量,我缩小了我的示例(见下文)。问题仍然存在。如果您输入一堆字符,然后按住退格键,底部的文本将显示“应该有 0 个元素”,但列表中仍有包含“旧”数据的行。这是在实际设备上的 iOS 14.4 上发生的。
我看到控制台中弹出此消息:
2021-01-27 12:01:33.231810-0500 Animations[1220:293840] [Snapshotting] Snapshotting a view (0x10410d190, _UIReplicantView) that has not been rendered at least once requires afterScreenUpdates:YES.
import SwiftUI
struct ContentView: View {
@State var text = ""
var body: some View {
TextField("Text", text: $text)
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding()
List {
ForEach(elements) { element in
Text(element.value)
}
}
.animation(.default)
Text("Should have \(elements.count) elements")
.background(Color(.systemRed))
.padding()
}
private var elements: [Element] {
(0..<text.count).map { i in
Element(id: i, value: "\(i)")
}.shuffled()
}
}
struct Element: Identifiable {
var id: Int
var value: String
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
最近刚刚对此进行了测试,发现此问题不再发生(至少从 iOS 15.3 开始,但可能是整个 iOS 15)。