SwiftUI:如何将ObservedObject属性传入其他类的初始化器?
问题描述
在SwiftUI的View中,尝试将ObservedObject的属性传入同一View内另一个类的初始化器时,遇到以下错误:
Cannot use instance member 'project' within property initializer; property initializers run before 'self' is available
需求是让Session类的属性依赖于ObservedObject(Project)中的值,比如用project.totalWordsWritten来初始化Session的totalWordCount,进而关联totalWordCountWithSession属性。
相关代码示例:
struct SessionView: View { @Binding var isPresented: Bool @ObservedObject var project: Project // 如何将ObservedObject project的totalWordsWritten传入totalWordCount? @StateObject var session:Session = Session(startDate: Date(), sessionWordCount: 300, totalWordCount: 4000) var body: some View { NavigationView { VStack(alignment: .leading) { Form { Section { Text("Count") HStack { Text("Session word count") TextField("", value: $session.sessionWordCount, formatter: NumberFormatter()) .textFieldStyle(.roundedBorder) } HStack { // 修改此处文本框应同步更新上方的会话字数 Text("Total word count") TextField("", value: $session.totalWordCountWithSession, formatter: NumberFormatter()) .textFieldStyle(.roundedBorder) } } } }.toolbar { ToolbarItem(placement: .navigationBarTrailing) { Button("Save") { // 将会话保存到项目中 project.addSession(newSession: session) isPresented = false } } } } } } struct SessionView_Previews: PreviewProvider { static var previews: some View { SessionView(isPresented: .constant(true), project: Project(title: "TestProject", startWordCount: 0)) } }
其余关联代码:
HomeView.swift
import SwiftUI struct HomeView: View { @State private var showingSessionPopover:Bool = false @StateObject var projectItem:Project = Project(title: "Test Project", startWordCount: 4000) var body: some View { NavigationView { VStack(alignment: .leading) { Text(projectItem.title).font(Font.custom("OpenSans-Regular", size: 18)) .fontWeight(.bold) Text("今日字数: \(projectItem.wordsWrittenToday)") Text("总字数: \(projectItem.totalWordsWritten)") } .toolbar { ToolbarItem { Button(action: { showingSessionPopover = true }, label: { Image(systemName: "calendar").imageScale(.large) } ) } } }.popover(isPresented: $showingSessionPopover) { SessionView(isPresented: $showingSessionPopover, project: projectItem) } } }
Session.swift
import Foundation import SwiftUI class Session: Identifiable, ObservableObject { init(startDate:Date, sessionWordCount:Int, totalWordCount: Int) { self.startDate = startDate self.endDate = Calendar.current.date(byAdding: .minute, value: 30, to: startDate) ?? Date() self.sessionWordCount = sessionWordCount self.totalWordCount = totalWordCount self.totalWordCountWithSession = self.totalWordCount + sessionWordCount } var id: UUID = UUID() @Published var startDate:Date @Published var endDate:Date var totalWordCount: Int var sessionWordCount:Int @Published var totalWordCountWithSession:Int { didSet { sessionWordCount = totalWordCountWithSession - totalWordCount } } }
Project.swift
import SwiftUI class Project: Identifiable, ObservableObject { var id: UUID = UUID() @Published var title:String var sessions:[Session] = [] @Published var wordsWrittenToday:Int = 0 @Published var totalWordsWritten:Int = 0 @Published var startWordCount:Int init(title:String,startWordCount:Int) { self.title = title self.startWordCount = startWordCount self.calculateDailyAndTotalWritten() } // 添加新会话 func addSession(newSession:Session) { sessions.append(newSession) calculateDailyAndTotalWritten() } // 重新计算今日及总字数 func calculateDailyAndTotalWritten() { wordsWrittenToday = 0 totalWordsWritten = startWordCount for session in sessions { if (Calendar.current.isDateInToday(session.startDate)) { wordsWrittenToday += session.sessionWordCount } totalWordsWritten += session.sessionWordCount } } }
解决方案
错误原因是@StateObject的属性初始化代码在self实例化前执行,此时无法访问project这个实例成员。解决方式是通过自定义View的初始化器来创建Session实例,从而拿到project的值。
修改后的SessionView代码如下:
struct SessionView: View { @Binding var isPresented: Bool @ObservedObject var project: Project @StateObject private var session: Session // 先声明,不直接初始化 // 自定义初始化器 init(isPresented: Binding<Bool>, project: Project) { self._isPresented = isPresented self._project = ObservedObject(wrappedValue: project) // 在这里使用project的totalWordsWritten初始化Session self._session = StateObject(wrappedValue: Session( startDate: Date(), sessionWordCount: 300, totalWordCount: project.totalWordsWritten )) } var body: some View { // 原body代码保持不变 NavigationView { VStack(alignment: .leading) { Form { Section { Text("Count") HStack { Text("Session word count") TextField("", value: $session.sessionWordCount, formatter: NumberFormatter()) .textFieldStyle(.roundedBorder) } HStack { // 修改此处文本框应同步更新上方的会话字数 Text("Total word count") TextField("", value: $session.totalWordCountWithSession, formatter: NumberFormatter()) .textFieldStyle(.roundedBorder) } } } }.toolbar { ToolbarItem(placement: .navigationBarTrailing) { Button("Save") { // 将会话保存到项目中 project.addSession(newSession: session) isPresented = false } } } } } } struct SessionView_Previews: PreviewProvider { static var previews: some View { SessionView(isPresented: .constant(true), project: Project(title: "TestProject", startWordCount: 0)) } }
关键说明
- 先声明
@StateObject private var session: Session,不直接赋值初始化 - 自定义
init方法,传入isPresented和project参数 - 在初始化器中,用
project.totalWordsWritten创建Session实例,并赋值给_session(@StateObject的底层存储属性)
这样就能正确将ObservedObject的属性传入Session的初始化器,同时符合SwiftUI的生命周期规则。
内容的提问来源于stack exchange,提问作者Michael Hsieh
相关产品推荐
相关产品推荐

