有 swiftui 失效专家吗?
我知道视图结构初始值设定项应该一直被调用,但只有当输入更改时才调用主体= swiftui 优化
struct FooView : View {
let counter: Int
let onOpenFaq: () -> Void
@State showDialog = false
var body: some View {
...
.sheet(isPresented: $showDialog) {
SimpleSheetContent(
title: "Bar",
message: "Bar messagge",
onPositiveClick: onOpenFaq,
onNegativeClick: {
showDialog = false <------------------
}
)
}
}
}
struct SimpleSheetContent : View {
let title: String
let message: String
let onPositiveClick: () -> Void
let onNegativeClick: () -> Void
var body: some View {
let _ = print("SimpleSheetContent body")
VStack {
...
}
}
}
Run Code Online (Sandbox Code Playgroud)
我注意到,如果单击闭包showDialog以这种方式写入,那么bodySimpleSheetContent每次更改时都会调用ofcounter - 一个不相关的更改
为什么是这样?这样的闭包不被认为是“稳定的”吗?
//
如果这样写闭包
onNegativeClick: { [$showFoo] in
$showFoo.wrappedValue = false
}
Run Code Online (Sandbox Code Playgroud)
那么工作表主体不会被调用,一切都会按预期显示
这是一个错误吗?还是我用错了?
当counter变化时,SwiftUI 必须再次调用FooView\xe2\x80\x99s body。FooView\xe2\x80\x99sbody每次调用时都会创建一个新的闭包,并将新的闭包作为SimpleSheetContent.init参数传递给onNegativeClick。
然后,SwiftUI 将新SimpleSheetContent值与先前SimpleSheetContent值进行比较。由于SimpleSheetContent不是Equatable,SwiftUI 会逐个比较它。
由于闭包是 never Equatable,SwiftUI\xe2\x80\x99s 唯一的选择是将旧onNegativeClick闭包与新onNegativeClick闭包逐字节进行比较。闭包存储为两个指针:一个指向机器代码的指针,以及一个指向closure\xe2\x80\x99s捕获块的指针,该捕获块通常是堆上的值。
旧闭包和新闭包在堆上的不同地址都有自己的捕获块,因此它们永远不会比较相等。SimpleSheetContent因此 SwiftUI每次都必须询问它的主体。
请注意,SwiftUI 不知道捕获块有多大,或者即使它\xe2\x80\x99 确实是指向捕获块的指针。(Swift ABI 仅要求它是可以传递给swift_retain和 的东西swift_release,并且还有其他东西,例如这些函数可以理解的标记值。)因此 SwiftUI 无法按字节比较捕获块。
您的onNegativeClick关闭只会忽略该工作表。您无需创建闭包即可获得相同的行为。
一种方法是使用dismiss环境的属性:
struct SimpleSheetContent : View {\n let title: String\n let message: String\n let onPositiveClick: () -> Void\n\n // Replace onNegativeClick with this property: <-----------------------\n @Environment(\\.dismiss) var dismiss\n\n var body: some View {\n let _ = print("SimpleSheetContent body")\n VStack {\n ...\n\n // Use it like this: <----------------------------\n Button("Cancel") { dismiss() }\n }\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n在我的少量测试中,这可以防止 SwiftUIbody重复调用。
另一种方法是传入Bindingto showDialog:
struct SimpleSheetContent : View {\n let title: String\n let message: String\n let onPositiveClick: () -> Void\n\n // Replace onNegativeClick with this property: <-----------------------\n @Binding var showDialog: Bool\n\n var body: some View {\n let _ = print("SimpleSheetContent body")\n VStack {\n ...\n\n // Use it like this: <----------------------------\n Button("Cancel") {\n showDialog = false\n }\n }\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n在我的少量测试中,这也阻止了 SwiftUI 调用body重复调用。
| 归档时间: |
|
| 查看次数: |
214 次 |
| 最近记录: |