Содержимое SwiftUI .sheet() перерисовывается после закрытия и не отображается
Сценарий использования
Если у вас есть SwiftUI
ContentView()
который отображает
PausableView()
на свойства основе@State , которое также используется для представления .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
// redraws the body and the sheet on dismiss
// PausableView(isPaused: $presentSheet) // 2. passing the property as a @Binding
// doesn't redraw the body when it changes on dismiss
}
.sheet(isPresented: $presentSheet) {
DismissingView(isPresented: $presentSheet)
}
}
}
Если недвижимость отправлена в
PausableView(isPaused: presentSheet)
как обычное свойство, тело ContentView и тело листа перерисовываются при закрытии листаЕсли недвижимость отправлена в
PausableView(isPaused: $presentSheet)
как @Binding , тело ContentView и тело листа НЕ перерисовываются, когда лист отклоняется
Это нормальное поведение?
Тело 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 nets правильное поведение.
Тело, оцениваемое при использовании обычного свойства, кажется ошибкой, потому что структуры должны быть неизменными ... если вы не используете оболочки свойств, такие как @State/@Binding/ и т. Д.
Кроме того, использование @Binding вместо этого, похоже, полностью меняет способ оценки тела представления. Но отправка его как @Binding неверна, потому что свойство должно быть доступно только для чтения в дочернем представлении. Как вы думаете, почему это неправильно? Использование @Binding означает, что вы читаете и изменяете данные, которые передаете в дочернее представление, но ваше дочернее представление не «владеет» ими.
/ Пользователь Xcode 12 здесь