我正在尝试实现一个可以显示在导航栏顶部的视图。我们将此视图称为NotificationView
。从任何其他SwiftUI视图中,触发该视图应该很容易。例如在登录视图上,如果用户名和密码错误。
下面您将找到我到目前为止的代码。仅当您在Username
和Password
字段中输入一个值时,此代码的问题才变得可见。通知视图正确显示,但是Username
和Password
字段同时丢失了它们的值。
这是因为再次重新渲染了整个视图树,并通过LoginView
创建了一个带有空LoginModel
的全新NavigationLink
实例。
您如何在SwiftUI中正确执行此操作,以免丢失值?
import SwiftUI
final class NotificationModel: ObservableObject {
func show(text: String) {
self.text = text
isHidden = false
}
@Published
var isHidden = true
var text = ""
}
struct NotificationView: View {
@EnvironmentObject
var model: NotificationModel
var body: some View {
GeometryReader { geometry in
if !self.model.isHidden {
Group {
HStack {
Image(systemName: "exclamationmark.triangle")
.font(Font.largeTitle.weight(.light))
Text(self.model.text)
.font(Font.body.weight(.medium))
.padding()
Spacer()
}
.frame(maxWidth: .infinity, minHeight: geometry.safeAreaInsets.top + 44)
.padding(EdgeInsets(top: geometry.safeAreaInsets.top, leading: 20, bottom: 0, trailing: 20))
}
.background(Color.red)
.transition(AnyTransition.move(edge: .top).combined(with: .opacity))
.onTapGesture {
withAnimation {
self.model.isHidden = true
}
}
}
}.edgesIgnoringSafeArea(.all)
}
}
final class LoginModel: ObservableObject {
@Published
var email = ""
@Published
var password = ""
let notificationModel: NotificationModel
init(notificationModel: NotificationModel) {
self.notificationModel = notificationModel
}
func submit() {
notificationModel.show(text: "Username/password wrong")
}
}
struct LoginView: View {
@ObservedObject
var model: LoginModel
var body: some View {
VStack {
TextField("Username", text: $model.email)
Divider()
TextField("Password", text: $model.password)
Divider()
Button(action: {
self.model.submit()
}) {
Text("Submit")
}
}
}
}
struct ContentView: View {
@EnvironmentObject
var notificationModel: NotificationModel
var body: some View {
ZStack {
NavigationView {
VStack {
NavigationLink(destination: LoginView(model: LoginModel(notificationModel: notificationModel))) {
Text("Login")
}
Spacer()
}
.navigationBarTitle("Start")
}
NotificationView()
}
}
}
final class NotificationModel: ObservableObject {
func show(text: String) {
self.text = text
self.isHidden = false
}
@Published
var isHidden = true
var text = ""
}
struct NotificationView: View {
@EnvironmentObject
var model: NotificationModel
var body: some View {
GeometryReader { geometry in
if !self.model.isHidden {
Group {
HStack {
Image(systemName: "exclamationmark.triangle")
.font(Font.largeTitle.weight(.light))
Text(self.model.text)
.font(Font.body.weight(.medium))
.padding()
Spacer()
}
.frame(maxWidth: .infinity, minHeight: geometry.safeAreaInsets.top + 44)
.padding(EdgeInsets(top: geometry.safeAreaInsets.top, leading: 20, bottom: 0, trailing: 20))
}
.background(Color.red)
.transition(AnyTransition.move(edge: .top).combined(with: .opacity))
.onTapGesture {
withAnimation {
self.model.isHidden = true
}
}
}
}.edgesIgnoringSafeArea(.all)
}
}
final class LoginModel: ObservableObject {
@Published
var email = ""
@Published
var password = ""
let notificationModel: NotificationModel
init(notificationModel: NotificationModel) {
self.notificationModel = notificationModel
}
func submit() {
notificationModel.show(text: "Username/password wrong")
}
}
struct LoginView: View {
@ObservedObject
var model: LoginModel
var body: some View {
VStack {
TextField("Username", text: $model.email)
Divider()
TextField("Password", text: $model.password)
Divider()
Button(action: {
self.model.submit()
}) {
Text("Submit")
}
}
}
}
struct ContentView: View {
@EnvironmentObject var loginModel : LoginModel
var body: some View {
return ZStack {
NavigationView {
VStack {
NavigationLink(destination: LoginView(model: loginModel)) {
Text("Login")
}
Spacer()
}
.navigationBarTitle("Start")
}
NotificationView().environmentObject(loginModel.notificationModel)
}
}
}