Rei*_*ner 1 swiftui viewmodifier
设置:
我有一个View可以显示警报的 SwiftUI。警报由AlertManager单例通过设置title和/或message其已发布的属性来提供@Published var nextAlertMessage = ErrorMessage(title: nil, message: nil)。该View拥有财产@State private var presentingAlert = false。
当以下修饰符应用于 时,此功能有效View:
.onAppear() {
if !(alertManager.nextAlertMessage.title == nil && alertManager.nextAlertMessage.message == nil) {
presentingAlert = true
}
}
.onChange(of: alertManager.nextAlertMessage) { alertMessage in
presentingAlert = alertMessage.title != nil || alertMessage.message != nil
}
.alert(alertManager.nextAlertMessage.joinedTitle, isPresented: $presentingAlert) {
Button("OK", role: .cancel) {
alertManager.alertConfirmed()
}
}
Run Code Online (Sandbox Code Playgroud)
问题:
由于警报也将在其他视图中呈现,因此我编写了以下自定义视图修饰符:
struct ShowAlert: ViewModifier {
@Binding var presentingAlert: Bool
let alertManager = AlertManager.shared
func body(content: Content) -> some View {
return content
.onAppear() {
if !(alertManager.nextAlertMessage.title == nil && alertManager.nextAlertMessage.message == nil) {
presentingAlert = true
}
}
.onChange(of: alertManager.nextAlertMessage) { alertMessage in
presentingAlert = alertMessage.title != nil || alertMessage.message != nil
}
.alert(alertManager.nextAlertMessage.joinedTitle, isPresented: $presentingAlert) {
Button("OK", role: .cancel) {
alertManager.alertConfirmed()
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
并将其应用于View:
.modifier(ShowAlert(presentingAlert: $presentingAlert))
Run Code Online (Sandbox Code Playgroud)
但是,现在没有显示任何警报。
问题:
我的代码有什么问题以及如何正确执行?
编辑(根据阿什利·米尔斯的要求):
这是一个最小的可重现示例。
请注意:
在 中ContentView,自定义修饰符ShowAlert已被注释掉。此版本的代码显示了警报。
如果修饰符.onAppear,.onChange和.alert被注释掉,并且启用了自定义修饰符,则不会显示警报。
// TestViewModifierApp
import SwiftUI
@main
struct TestViewModifierApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
// ContentView
import SwiftUI
struct ContentView: View {
@ObservedObject var alertManager = AlertManager.shared
@State private var presentingAlert = false
var body: some View {
let alertManager = AlertManager.shared
let _ = alertManager.showNextAlertMessage(title: "Title", message: "Message")
Text("Hello, world!")
// .modifier(ShowAlert(presentingAlert: $presentingAlert))
.onAppear() {
if !(alertManager.nextAlertMessage.title == nil && alertManager.nextAlertMessage.message == nil) {
presentingAlert = true
}
}
.onChange(of: alertManager.nextAlertMessage) { alertMessage in
presentingAlert = alertMessage.title != nil || alertMessage.message != nil
}
.alert(alertManager.nextAlertMessage.joinedTitle, isPresented: $presentingAlert) {
Button("OK", role: .cancel) {
alertManager.alertConfirmed()
}
}
}
}
// AlertManager
import SwiftUI
struct ErrorMessage: Equatable {
let title: String?
let message: String?
var joinedTitle: String {
(title ?? "") + "\n\n" + (message ?? "")
}
static func == (lhs: ErrorMessage, rhs: ErrorMessage) -> Bool {
lhs.title == rhs.title && lhs.message == rhs.message
}
}
final class AlertManager: NSObject, ObservableObject {
static let shared = AlertManager() // Instantiate the singleton
@Published var nextAlertMessage = ErrorMessage(title: nil, message: nil)
func showNextAlertMessage(title: String?, message: String?) {
DispatchQueue.main.async {
// Publishing is only allowed from the main thread
self.nextAlertMessage = ErrorMessage(title: title, message: message)
}
}
func alertConfirmed() {
showNextAlertMessage(title: nil, message: nil)
}
}
// ShowAlert
import SwiftUI
struct ShowAlert: ViewModifier {
@Binding var presentingAlert: Bool
let alertManager = AlertManager.shared
func body(content: Content) -> some View {
return content
.onAppear() {
if !(alertManager.nextAlertMessage.title == nil && alertManager.nextAlertMessage.message == nil) {
presentingAlert = true
}
}
.onChange(of: alertManager.nextAlertMessage) { alertMessage in
presentingAlert = alertMessage.title != nil || alertMessage.message != nil
}
.alert(alertManager.nextAlertMessage.joinedTitle, isPresented: $presentingAlert) {
Button("OK", role: .cancel) {
alertManager.alertConfirmed()
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
Ash*_*lls 12
你太复杂了,呈现错误警报的方法如下:
LocalizedError. 最简单的方法是使用enum,为您的应用程序可能遇到的每个错误提供一个案例。您必须实施var errorDescription: String?,这将显示为警报标题。如果您想显示警报消息,请向枚举添加一个方法以返回该消息。enum MyError: LocalizedError {
case basic
var errorDescription: String? {
switch self {
case .basic:
return "Title"
}
}
var errorMessage: String? {
switch self {
case .basic:
return "Message"
}
}
}
Run Code Online (Sandbox Code Playgroud)
@State变量来保存错误,并需要一个在应该显示警报时设置的变量。你可以这样做:@State private var error: MyError?
@State private var isShowingError: Bool
Run Code Online (Sandbox Code Playgroud)
但是这样你就有了两个事实来源,你必须记住每次都设置两个。或者,您可以使用计算属性Bool:
var isShowingError: Binding<Bool> {
Binding {
error != nil
} set: { _ in
error = nil
}
}
Run Code Online (Sandbox Code Playgroud)
.alert(isPresented: isShowingError, error: error) { error in
// If you want buttons other than OK, add here
} message: { error in
if let message = error.errorMessage {
Text(message)
}
}
Run Code Online (Sandbox Code Playgroud)
4. 额外学分
正如您上面所做的那样,我们可以将一堆这些东西移动到 a 中ViewModifier,所以我们最终得到:
enum MyError: LocalizedError {
case basic
var errorDescription: String? {
switch self {
case .basic:
return "Title"
}
}
var errorMessage: String? {
switch self {
case .basic:
return "Message"
}
}
}
struct ErrorAlert: ViewModifier {
@Binding var error: MyError?
var isShowingError: Binding<Bool> {
Binding {
error != nil
} set: { _ in
error = nil
}
}
func body(content: Content) -> some View {
content
.alert(isPresented: isShowingError, error: error) { _ in
} message: { error in
if let message = error.errorMessage {
Text(message)
}
}
}
}
extension View {
func errorAlert(_ error: Binding<MyError?>) -> some View {
self.modifier(ErrorAlert(error: error))
}
}
Run Code Online (Sandbox Code Playgroud)
现在要显示错误,我们需要的是:
struct ContentView: View {
@State private var error: MyError? = .basic
var body: some View {
Text("Hello, world!")
.errorAlert($error)
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
3575 次 |
| 最近记录: |