SwiftUI .sheet() 的主体在解雇后被评估并且不呈现
Posted
技术标签:
【中文标题】SwiftUI .sheet() 的主体在解雇后被评估并且不呈现【英文标题】:Body of a SwiftUI .sheet() is being evaluated after dismiss and not presenting 【发布时间】:2021-10-07 17:56:26 【问题描述】:用例
如果您有一个 SwiftUI ContentView()
,它基于 @State 属性 presentSheet
显示 PausableView()
,该属性也用于呈现 .sheet(),则根据 presentSheet
的方式对正文进行不同的评估属性在 ContentView 的主体内使用。
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 的正文和工作表的正文
如果属性以@Binding 的形式发送到PausableView(isPaused: $presentSheet)
,则在关闭工作表时不会评估 ContentView 的正文和工作表的正文
这是正常行为吗?
当工作表在关闭后不再呈现时,是否应该评估工作表的正文?
另外,使用@Binding 似乎完全改变了视图主体的评估方式。但是将其作为@Binding 发送是不正确的,因为该属性在子视图中应该是只读的。
视觉
1 - 使用普通属性时会评估正文(参见第 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
【问题讨论】:
【参考方案1】:使用@Binding 网络正确的行为。
使用普通属性时被评估的主体似乎是一个错误,因为结构应该是不可变的......除非你使用像@State/@Binding/等这样的属性包装器。
另外,使用@Binding 似乎完全改变了视图的方式 身体被评估。但是将其作为@Binding 发送是不正确的,因为 该属性在子视图中应该是只读的。 为什么你认为它不正确?使用 @Binding 意味着您读取和修改传递给子视图的数据,但您的子视图并不“拥有”它。
/Xcode 12 用户在这里
【讨论】:
以上是关于SwiftUI .sheet() 的主体在解雇后被评估并且不呈现的主要内容,如果未能解决你的问题,请参考以下文章
SwiftUI:为 .sheet(_: onDismiss:) 重置状态
SwiftUI禁止用户关闭sheet弹出视图在iOS14.6+失效的巧妙解决