You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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))
    }
}

关键说明

  1. 先声明@StateObject private var session: Session,不直接赋值初始化
  2. 自定义init方法,传入isPresented和project参数
  3. 在初始化器中,用project.totalWordsWritten创建Session实例,并赋值给_session(@StateObject的底层存储属性)

这样就能正确将ObservedObject的属性传入Session的初始化器,同时符合SwiftUI的生命周期规则。


内容的提问来源于stack exchange,提问作者Michael Hsieh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 15:25:19