swift拾遗(二)

187 阅读3分钟

import SwiftUI

class DoTryCatchThrowsDemoDataManager {

private var isError: Bool = false

func fetchTitle(_ completion: (String?, Error?) -> Void) {
    if !isError {
        completion("New title", nil)
    } else {
        completion(nil, URLError(.badURL))
    }
}

}

class DoTryCatchThrowsDemoViewModel: ObservableObject {

@Published var title: String = "Original title"

private var dataManager = DoTryCatchThrowsDemoDataManager()

func getTitle() {
    dataManager.fetchTitle { [weak self] title, error in
        guard let self else { return }
        if let title {
            self.title = title
        } else if let error {
            self.title = error.localizedDescription
        }
    }
}

}

struct DoTryCatchThrowsDemo: View {

@StateObject private var viewModel = DoTryCatchThrowsDemoViewModel()
    
var body: some View {
    Text(viewModel.title)
        .font(.largeTitle)
        .frame(width: 300, height: 300)
        .background(Color.cyan)
        .onTapGesture {
            viewModel.getTitle()
        }
}

}
@StateObject 是 SwiftUI 中的一个属性包装器(Property Wrapper),在管理视图的状态和依赖对象时起着重要作用,以下从基本概念、使用场景、使用方法、与其他属性包装器对比等方面详细介绍:

基本概念

在 SwiftUI 里,视图是值类型,每次视图重新渲染时,其状态可能会丢失或被重置。而 @StateObject 用于创建和管理引用类型(通常是遵循 ObservableObject 协议的类)的实例,并且保证该实例在视图的生命周期内是唯一且持久的,即无论视图如何刷新,@StateObject 包装的对象都会保持其状态。

使用场景

  • 复杂状态管理:当视图需要管理复杂的状态逻辑,如网络请求、数据处理等,将这些逻辑封装在一个遵循 ObservableObject 协议的类中,然后使用 @StateObject 来管理这个类的实例,能让视图代码更简洁,逻辑更清晰。
  • 多视图共享状态:在多个视图之间共享同一个状态对象时,使用 @StateObject 可以确保状态的一致性和持久性。

使用方法

1. 创建遵循 ObservableObject 协议的类

swift

import Combine

class MyViewModel: ObservableObject {
    @Published var count = 0

    func increment() {
        count += 1
    }
}

在上述代码中,MyViewModel 类遵循 ObservableObject 协议,使用 @Published 属性包装器标记的 count 属性,当该属性的值发生变化时,会自动发布通知,让依赖该对象的视图进行更新。increment 方法用于增加 count 的值。

2. 在视图中使用 @StateObject

swift

import SwiftUI

struct ContentView: View {
    @StateObject private var viewModel = MyViewModel()

    var body: some View {
        VStack {
            Text("Count: (viewModel.count)")
            Button("Increment") {
                viewModel.increment()
            }
        }
    }
}

ContentView 中,使用 @StateObject 声明了一个 viewModel 实例。无论 ContentView 如何刷新,viewModel 始终是同一个实例,其状态会一直保留。点击按钮调用 viewModelincrement 方法,count 属性的值会增加,同时视图会自动更新显示新的 count 值。

与其他属性包装器对比

1. @StateObject@ObservedObject

  • @StateObject 用于创建和拥有一个 ObservableObject 实例,通常在视图第一次创建时初始化该实例,并且在视图的整个生命周期内保持不变。
  • @ObservedObject 用于观察一个已经存在的 ObservableObject 实例,它不会创建实例,只是对传入的实例进行观察。一般在子视图中使用 @ObservedObject 来观察父视图传递过来的状态对象。

示例:

swift

// 父视图使用 @StateObject 创建和管理 ViewModel
struct ParentView: View {
    @StateObject private var viewModel = MyViewModel()

    var body: some View {
        ChildView(viewModel: viewModel)
    }
}

// 子视图使用 @ObservedObject 观察传入的 ViewModel
struct ChildView: View {
    @ObservedObject var viewModel: MyViewModel

    var body: some View {
        Text("Count in ChildView: (viewModel.count)")
    }
}

2. @StateObject@State

  • @State 用于管理视图内部的简单值类型(如 IntString 等)的状态,它主要用于处理视图自身的基本状态变化。
  • @StateObject 用于管理复杂的引用类型(遵循 ObservableObject 协议的类)的状态,适合处理更复杂的业务逻辑和数据管理。

注意事项

  • @StateObject 修饰的对象必须在视图初始化时创建,否则可能会导致状态丢失或不一致。
  • 在 SwiftUI 中,尽量避免在 @StateObject 对象的初始化过程中进行耗时操作,以免影响视图的加载性能。可以将耗时操作放在对象的方法中,在合适的时机调用