LazyVStack 在更改 SwiftUI 时初始化所有视图
LazyVStack Initializes all views when one changes SwiftUI
我有一个 LazyVStack
我想只更新一个视图而不是让所有其他视图重新加载屏幕。对于更复杂的单元格,这会导致很大的性能损失。我已经包含示例代码
import SwiftUI
struct ContentView: View {
@State var items = [String]()
var body: some View {
ScrollView {
LazyVStack {
ForEach(self.items, id: \.self) { item in
Button {
if let index = self.items.firstIndex(where: {[=10=] == item}) {
self.items[index] = "changed \(index)"
}
} label: {
cell(text: item)
}
}
}
}
.onAppear {
for _ in 0...200 {
self.items.append(NSUUID().uuidString)
}
}
}
}
struct cell: View {
let text: String
init(text: String) {
self.text = text
print("init cell", text)
}
var body: some View {
Text(text)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
如您所见,即使仅更改 1 个单元格,也会为每个单元格调用 init。有没有办法避免这种情况?
这是一个有效的代码,有几点需要说明,SwiftUI 中的视图会在 SwiftUI 认为需要的任何时间或任何时候初始化!但是,如果 body 中的某些值确实发生了变化,那么 View 的 body 就会被计算出来。它计划像这样工作,也有一些例外。就像 body 得到计算一样,即使 body 中使用的值也和以前一样没有变化,我不想讨论那个话题!但是在您的示例和您的问题中,我们希望 SwiftUI 仅呈现更改后的视图,为此,向下代码可以正常工作而不会出现问题,但是如您所见,如果我们将 VStack
更改为 [=12],我使用了 VStack =], SwiftUI 会渲染一些额外的视图,因为它的隐藏代码,如果你向下滚动然后向上滚动,它会忘记所有渲染的视图和内存中的数据,它会尝试渲染旧的渲染视图,所以它是LazyVStack
的性质,我们对此无能为力。 Apple 希望 LazyVStack
变得懒惰。但是你可以看到 LazyVStack
不会呈现所有视图,但其中一些需要工作。我们不能说也不知道有多少视图以惰性方式呈现,但肯定不是全部。
let initializingArray: () -> [String] = {
var items: [String] = [String]()
for _ in 0...200 { items.append(UUID().uuidString) }
return items
}
struct ContentView: View {
@State var items: [String] = initializingArray()
var body: some View {
ScrollView {
VStack {
ForEach(items, id: \.self) { item in
Button(action: {
if let index = self.items.firstIndex(where: { [=10=] == item }) {
items[index] = "changed \(index)"
}
}, label: {
ItemView(item: item)
})
}
}
}
}
}
struct ItemView: View {
let item: String
var body: some View {
print("Rendering row for:", item)
return Text(item)
}
}
我有一个 LazyVStack
我想只更新一个视图而不是让所有其他视图重新加载屏幕。对于更复杂的单元格,这会导致很大的性能损失。我已经包含示例代码
import SwiftUI
struct ContentView: View {
@State var items = [String]()
var body: some View {
ScrollView {
LazyVStack {
ForEach(self.items, id: \.self) { item in
Button {
if let index = self.items.firstIndex(where: {[=10=] == item}) {
self.items[index] = "changed \(index)"
}
} label: {
cell(text: item)
}
}
}
}
.onAppear {
for _ in 0...200 {
self.items.append(NSUUID().uuidString)
}
}
}
}
struct cell: View {
let text: String
init(text: String) {
self.text = text
print("init cell", text)
}
var body: some View {
Text(text)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
如您所见,即使仅更改 1 个单元格,也会为每个单元格调用 init。有没有办法避免这种情况?
这是一个有效的代码,有几点需要说明,SwiftUI 中的视图会在 SwiftUI 认为需要的任何时间或任何时候初始化!但是,如果 body 中的某些值确实发生了变化,那么 View 的 body 就会被计算出来。它计划像这样工作,也有一些例外。就像 body 得到计算一样,即使 body 中使用的值也和以前一样没有变化,我不想讨论那个话题!但是在您的示例和您的问题中,我们希望 SwiftUI 仅呈现更改后的视图,为此,向下代码可以正常工作而不会出现问题,但是如您所见,如果我们将 VStack
更改为 [=12],我使用了 VStack =], SwiftUI 会渲染一些额外的视图,因为它的隐藏代码,如果你向下滚动然后向上滚动,它会忘记所有渲染的视图和内存中的数据,它会尝试渲染旧的渲染视图,所以它是LazyVStack
的性质,我们对此无能为力。 Apple 希望 LazyVStack
变得懒惰。但是你可以看到 LazyVStack
不会呈现所有视图,但其中一些需要工作。我们不能说也不知道有多少视图以惰性方式呈现,但肯定不是全部。
let initializingArray: () -> [String] = {
var items: [String] = [String]()
for _ in 0...200 { items.append(UUID().uuidString) }
return items
}
struct ContentView: View {
@State var items: [String] = initializingArray()
var body: some View {
ScrollView {
VStack {
ForEach(items, id: \.self) { item in
Button(action: {
if let index = self.items.firstIndex(where: { [=10=] == item }) {
items[index] = "changed \(index)"
}
}, label: {
ItemView(item: item)
})
}
}
}
}
}
struct ItemView: View {
let item: String
var body: some View {
print("Rendering row for:", item)
return Text(item)
}
}