当SwiftUI视图具有复杂的逻辑时,我喜欢使用枚举来表示它的状态(通常在视图模型/服务中).

struct FooView {

    enum ProgressState {
        case idle
        case loading
        case completed(Model)
        case error

        var stateName: String { ... }
    }

    @State var state: ProgressState = .idle
    ...

PROBLEM:

我想要的是一种方法或方法来连接绑定到一个特定的枚举情况.在这个例子中,显示一个alert (或工作表),因为像<<$state.error>>这样的东西显然是不可能的.

    ...
    var body: some View {
        Text(state.stateName)

        // show alert on error
        .alert("oops there was an error", 
               isPresented: <<$state.error>>) 
        { //1.
            ...
        }
        
    }
}

CURRENT ALTERNATIVE

最简单的解决方案是使用额外的变量,但我真的不喜欢手动保持它们同步.

BETTER SOLUTIONS?

我在探索使用Combine在state个变量更改时更新附加变量,问题始终是可绑定对象也需要更改其状态,但在本例中,alert 不知道将其带回哪个状态.

因此,也许是一个枚举的协议和一个用于alert和sheet的扩展来处理这些而不是Bindable?有没有人try 过类似的方法,或者知道更好的方法?

谢谢

推荐答案

SwiftUI提供了一个解决方案来呈现LocalizedError

func alert<E, A>(
    isPresented: Binding<Bool>,
    error: E?,
    @ViewBuilder actions: () -> A
) -> some View where E : LocalizedError, A : View

您可以创建一个简单的enum,它可以显示任何标准Error或自定义错误.

enum LocalError: LocalizedError {
    //Use for any built in error
    case error(Error)
    //Use for something custom
    case invalidId
    
    var errorDescription: String? {
        switch self {
        case .error(let error):
            return error.localizedDescription
        case .invalidId:
            return "\(self)"
        }
    }
    var recoverySuggestion: String? {
        switch self {
        case .error(let error):
            let nsError = error as NSError
            return nsError.localizedRecoverySuggestion
        default:
            return nil
        }
    }
}

然后,您可以修改进度枚举.

enum ProgressState {
    case idle
    case loading
    case completed(Model)
    case error(Error)

然后,您可以在该状态被触发时触发alert(或工作表

switch state {
case .error(let error):
    Text(state.stateName)
        .task {
            alert = (true, .error(error))
        }
default :
    Text(state.stateName)
}

以下是完整的代码.

import SwiftUI

struct FooView: View {
    
    enum ProgressState {
        case idle
        case loading
        case completed(Model)
        case error(LocalError)
        
        
        var stateName: String {
            switch self {
            case .completed(_):
                return "Complete"
            case .error(_):
                return "Something went wrong"
            default:
                return "\(self)"
            }
        }
    }
    
    @State private var state: ProgressState = .idle
    @State private var alert: (isPresented: Bool, error: LocalError?) = (false, nil)
    var body: some View {
        Group {
    switch state {
    case .error(let error):
        Text(state.stateName)
            .task {
                alert = (true, error)
            }
    default :
        Text(state.stateName)
    }
        }.alert(isPresented: $alert.isPresented, error: alert.error) {
            Button("Ok") {
                alert = (false, nil)
            }
        }
        .task {
            try? await Task.sleep(for: .seconds(1))
            state = .error(.invalidId)
        }
    }
    
    struct Model {
        
    }
    
    enum LocalError: LocalizedError {
        //Use for any built in error
        case error(Error)
        //Use for something custom
        case invalidId
        
        var errorDescription: String? {
            switch self {
            case .error(let error):
                return error.localizedDescription
            case .invalidId:
                return "\(self)"
            }
        }
        var recoverySuggestion: String? {
            switch self {
            case .error(let error):
                let nsError = error as NSError
                return nsError.localizedRecoverySuggestion
            default:
                return nil
            }
        }
    }
}

#Preview {
    FooView()
}

这提供了一个独立的alert变量,因此.alert的表示不会与后面的View冲突,也不会做出任何不安全的假设.

Ios相关问答推荐

第三方框架的iOS隐私声明

在工作表上移动图像SwiftUI

当重新呈现UI时,嵌套的 struct 值如何与@Observable一起工作?

使用AVCaptureEventInteraction捕获音量按键快门

IOS:在UI线程中执行代码的闭包样式的替代

更新 flutter 和 xcode 后 Xcode 14.3 中缺少文件 (libarclite_iphoneos.a)

具有一个参数/参数的泛型类的泛型类

如何包含 Xcode 子项目标头?

包装在 AnyView 中时 UIViewControllerRepresentable 无法正常工作

按钮在 SwiftUI 中没有采用给定的高度和宽度

使用实时数据库的应用程序无法在被 Firebase 阻止的罗马尼亚 ISP 中运行

UICollectionView - 水平滚动,水平布局?

实现一个将块用作回调的方法

未找到签名证书​​iOS Distribution

iOS 中是否有用于自定义振动的 API?

SwiftUI 视图 - viewDidLoad()?

如何在 Swift 中使用包含多个值的查询参数构建一个 URL?

从 WKWebView 获取所有 cookie

iOS Swift - 获取当前本地时间和日期时间戳

UITableView 中的圆形 UIImageView 没有性能影响?