@Published var sheetContent: AnyView? = nil
func present(content: Content) {
self.sheetContent = AnyView(content)
}
}
struct RouterView: View {
@ObservedObject var router: Router
private var content: () -> Content
init (_ router: Router, @ViewBuilder content: @escaping () -> Content) {
self.router = router
self.content = content
}
var body: some View {
content()
.sheet(isPresented: Binding(
get: { router.sheetContent != nil },
set: { if !$0 { router.sheetContent = nil } }
)) {
router.sheetContent
}
}
}
Хотя это хорошо работает в целом, он каким -то образом ломает @binding s, используемый в представленном контенте, как в демо -коде ниже.
Демо включает в себя: < /strong> < /p>
- RouterTestView Удерживание ViewModel с его экземпляром маршрутизатора . Он показывает текущее значение viewmodel.showtitle < /code> и кнопок, чтобы показать лист тремя различными способами:
Настройка «классическая» лист «классический» и контролируется в представлении < /li>
Управляемый лист маршрутизатора, в котором контент создается в ViewModel < /li>
Лист, управляемый маршрутизатором, где контент создается в пределах Просмотр < /li>
< /ul>
< /li>
Ожидаемое поведение:
Независимо от того, какой лист используется, отображается значение valueView . />$ viewmodel.showtitle может быть переключен. Изменение показано в RouterTestView и в ValueView
[*] Фактическое поведение: < Br />
Только классический лист работает, как и ожидалось. $ viewmodel.showtitle правильно применяется к ViewModel. При закрытии листового маршрутизатора правильно показывает true или false. При представлении другого листа начальное состояние (скрытое/видимое) заголовка является правильным.
Однако переключение не сразу применяется к значению . Текст заголовка не изменяет свою видимость. < /Li>
< /ul>
< /li>
< /ul>
Заключение: < /strong> с использованием маршрутизатора каким -то образом нарушает привязку он работает только из valueview обратно в ViewModel , в то время как другое направление (viewModel to valueview ) не работает.
Как это можно решить? [/b]
демо -код: < /strong> < /p>
// View Model
extension RouterTestView {
class ViewModelModel: ObservableObject {
@Published var router = Router()
@Published var title: String = "The Value"
@Published var showTitle: Bool = true
func show() {
router.present(content: ValueView(value: title, showValue: Binding(get: { self.showTitle }, set: { self.showTitle = $0 })))
}
func show(_ inView: Content) {
router.present(content: inView)
}
}
}
struct RouterTestView: View {
@StateObject var viewModel: ViewModelModel = .init()
@State private var isPresentingSheet: Bool = false
var body: some View {
Text("showTitle == \(viewModel.showTitle ? "true" : "false")")
// Do not use Router but instead use the classic way to present
// the sheet. The Binding works without any problem.
Button("Present Sheet manually") {
isPresentingSheet = true
}
.padding()
.sheet(isPresented: $isPresentingSheet) {
ValueView(value: viewModel.title, showValue: $viewModel.showTitle)
}
// Use Router to present the sheet. It makes no difference if the
// Content is created in the ViewModel or here. The Binding is broken.
// Changes to $viewModel.showTitle in ValueView are correctly reported
// to the viewModel, but the changes are not recognized in ValueView
// itself
RouterView(viewModel.router) {
// Sheet with Content created in viewModel
Button("Present Sheet with Router") {
viewModel.show()
}
.padding()
// Sheet with Content created here.
Button("Present Sheet with Router") {
viewModel.show(
ValueView(value: viewModel.title, showValue: $viewModel.showTitle)
)
}
.padding()
}
}
}
// ValueView as dummy "editor". Changes in the showValue Binding should be reported
// Back to the viewModel in RouterTestView and toggle the visibility of the value here.
struct ValueView: View {
let value: String
@Binding var showValue: Bool
var body: some View {
Text("Here is the value:")
if showValue {
Text(value)
}
Button("Toggle") {
showValue.toggle()
}
}
}
#Preview {
RouterTestView()
}
Подробнее здесь: https://stackoverflow.com/questions/794 ... re-and-why
Мобильная версия