ios - SwiftUI .sheet() 的主体在解雇后被评估并且不呈现
问题描述
用例
如果您有一个基于@State属性ContentView()
显示的SwiftUI,该属性也用于呈现.sheet(),则根据该属性在 ContentView 正文中的使用方式,对正文进行不同的评估。PausableView()
presentSheet
presentSheet
struct ContentView: View {
@State private var presentSheet = false
var body: some View {
return VStack{
Button("Show Sheet") {
presentSheet.toggle()
}
PausableView(isPaused: presentSheet) // 1. passing the property as a normal variable
// evaluates the body and the sheet on dismiss
// PausableView(isPaused: $presentSheet) // 2. passing the property as a @Binding
// doesn't evaluate the body when it changes on dismiss
}
.sheet(isPresented: $presentSheet) {
DismissingView(isPresented: $presentSheet)
}
}
}
如果该属性
PausableView(isPaused: presentSheet)
作为普通属性发送到 ,则在 关闭工作表时正在评估 ContentView 的正文和工作表的正文如果该属性
PausableView(isPaused: $presentSheet)
作为@Binding发送到,则在关闭工作表时不会评估ContentView 的正文和工作表的正文
这是正常行为吗?
当工作表在解雇后不再呈现时,是否应该评估工作表的正文?
此外,使用 @Binding 似乎完全改变了视图主体的评估方式。但是将其作为@Binding 发送是不正确的,因为该属性在子视图中应该是只读的。
视觉的
1 - Body 在使用普通属性时被评估(见第 27-28 和 53-54 行):
2 - 使用 @Binding 时不评估正文(参见第 27-28 和 53-54 行):
示例项目
此处提供了在 Xcode 13 中创建的示例项目:https ://github.com/clns/SwiftUI-sheet-redraw-on-dismiss 。我注意到 iOS 14 和 iOS 15 上的行为相同。
相关代码在ContentView.swift中:
import SwiftUI
struct DismissingView: View {
@Binding var isPresented: Bool
var body: some View {
if #available(iOS 15.0, *) {
print(Self._printChanges())
} else {
print("DismissingView: body draw")
}
return VStack {
Button("Dismiss") { isPresented.toggle() }
Text("Dismissing Sheet").padding()
}.background(Color.white)
}
}
struct PausableView: View {
var isPaused: Bool
// @Binding var isPaused: Bool
private let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
@State private var counter = 0
var body: some View {
Text("Elapsed seconds: \(counter)")
.onReceive(timer) { _ in
counter += isPaused ? 0 : 1
}
}
}
struct ContentView: View {
@State private var presentSheet = false
var body: some View {
if #available(iOS 15.0, *) {
print(Self._printChanges())
} else {
print("ContentView: body draw")
}
return VStack{
Button("Show Sheet") { presentSheet.toggle() }
Text("The ContentView's body along with the .sheet() is being redrawn immediately after dismiss, if the @State property `presentSheet` is used anywhere else in the view - e.g. passed to `PausableView(isPaused:presentSheet)`.\n\nBut if the property is passed as a @Binding to `PausableView(isPaused:$presentSheet)`, the ContentView's body is not redrawn.").padding()
PausableView(isPaused: presentSheet)
// PausableView(isPaused: $presentSheet)
}
.sheet(isPresented: $presentSheet) {
DismissingView(isPresented: $presentSheet)
.background(BackgroundClearView()) // to see what's happening under the sheet
}
}
}
也发布在 Apple 开发者论坛上:https ://developer.apple.com/forums/thread/691783
解决方案
使用 @Binding 可以解决正确的行为。
使用普通属性时评估的主体似乎是一个错误,因为结构应该是不可变的......除非您使用像@State/@Binding/等这样的属性包装器。
此外,使用 @Binding 似乎完全改变了视图主体的评估方式。但是将其作为@Binding 发送是不正确的,因为该属性在子视图中应该是只读的。为什么你认为它不正确?使用 @Binding 意味着您读取和修改传递给子视图的数据,但您的子视图并不“拥有”它。
/Xcode 12 用户在这里
推荐阅读
- javascript - ReactJs 中的替代 prev 函数是什么?
- python-3.x - 如果某些日期时间值“与格式不匹配”怎么办?
- c# - 使用热键重新加载场景时出现 MissingReferenceException
- azure-devops - 为什么 dotnet test project.csproj --filter category=unit 在 Azure DevOps 中失败?
- regex - Codeigniter 正则表达式对活动过滤器进行分组
- apache-kafka - 如何使用 Spring 云流访问使用密码保护的融合模式注册表服务器?
- java - 有什么方法可以让我在 SecondActivity 上调用在 FirstActivity.Fragment 上的 Arraylist 上添加一个具有 Recyclerview 的新项目?
- c - 如何在 ContinueWith Firebase auth UNITY3D 中更改游戏对象
- angular - 重新加载在formGroup中动态创建的formArray时的无限循环
- excel - excel vba自动生成邮件问题