SwiftUI:具有计算属性的 ObservableObject

Posted

技术标签:

【中文标题】SwiftUI:具有计算属性的 ObservableObject【英文标题】:SwiftUI: ObservableObject with a computed property 【发布时间】:2020-10-25 16:15:49 【问题描述】:

我有一个带有自定义月份选择器的简单视图。每次向左或向右单击 V 形时,

在 DateView 中,我有两个私有变量:startDateOfMonth2: DateendDateOfMonth2: Date。但它们仅在 DateView 中可用。

问题

如何使这两个变量在其他视图中可用?

我尝试将它们添加为 @Published 变量,但出现错误:

属性包装器不能应用于计算属性

我只发现了几个类似的问题,但我无法在我的代码中使用它们的答案。

import SwiftUI

class SelectedDate: ObservableObject 
    @Published var selectedMonth: Date = Date()
    
    @Published var startDateOfMonth2: Date 
        let components = Calendar.current.dateComponents([.year, .month], from: selectedMonth)
        let startOfMonth = Calendar.current.date(from: components)!
        return startOfMonth
    

    @Published var endDateOfMonth2: Date 
        var components = Calendar.current.dateComponents([.year, .month], from: selectedMonth)
        components.month = (components.month ?? 0) + 1
        let endOfMonth = Calendar.current.date(from: components)!
        return endOfMonth
    


struct DateView: View 
    @EnvironmentObject var selectedDate: SelectedDate
    
    static let dateFormat: DateFormatter = 
        let formatter = DateFormatter()
        formatter.setLocalizedDateFormatFromTemplate("yyyy MMMM")
        return formatter
    ()
    
    var body: some View 
        
        HStack 
            
            Image(systemName: "chevron.left")
                .frame(width: 50, height: 50)
                .contentShape(Rectangle())
                .onTapGesture 
                    self.changeMonthBy(-1)
            
            
            Spacer()

            Text("\(selectedDate.selectedMonth, formatter: Self.dateFormat)")
            
            Spacer()
            
            Image(systemName: "chevron.right")
                .frame(width: 50, height: 50)
                .contentShape(Rectangle())
                .onTapGesture 
                    self.changeMonthBy(1)
            
            
        
        .padding(EdgeInsets(top: 5, leading: 5, bottom: 5, trailing: 5))
        .background(Color.yellow)
    
    
    func changeMonthBy(_ months: Int) 
        if let selectedMonth = Calendar.current.date(byAdding: .month, value: months, to: selectedDate.selectedMonth) 
            self.selectedDate.selectedMonth = selectedMonth
        
    

【问题讨论】:

【参考方案1】:

无需将您的计算值声明为已发布,因为它们取决于已发布的值。当发布的值发生变化时,它们会被重新计算。

class SelectedDate: ObservableObject 
    @Published var selectedMonth: Date = Date()
    
    var startDateOfMonth2: Date 
        let components = Calendar.current.dateComponents([.year, .month], from: self.selectedMonth)
        let startOfMonth = Calendar.current.date(from: components)!
        
        print(startOfMonth)
        return startOfMonth
    

    var endDateOfMonth2: Date 
        var components = Calendar.current.dateComponents([.year, .month], from: self.selectedMonth)
        components.month = (components.month ?? 0) + 1
        let endOfMonth = Calendar.current.date(from: components)!

        print(endOfMonth)
        return endOfMonth
    

当您在点击时打印endDateOfMonth2,您会看到它正在发生变化。

【讨论】:

谢谢,但我认为这样我仍然无法在课外访问 endDateOfMonth2。我刚刚在我的代码中将 print("\(startDateOfMonth2)") 添加到 .onTapGesture 中,但出现错误:Use of unresolved identifier 'startDateOfMonth2' 为我工作了 print(selectedDate.endDateOfMonth2) 哦!伟大的。 print(selectedDate.endDateOfMonth2) 有效,我可以在另一个视图中访问它。谢谢! :) 所以就我使用依赖于@Published var 的变量而言,是否不需要声明新的已发布变量?我想这会使应用程序使用更少的内存,对吧?就我而言,我只需要在内存中存储 1 个已发布的 var,而不是 3 个。我正确吗? 您不能更改计算值本身。它总是被计算出来的。所以问题是,你什么时候需要再次计算它?如果 Published 值在类内发生变化,它将计算。由于计算值取决于该 Published 值,因此当 Published 更改时它将重新计算。所以基本上,Published 会改变,computed 会重新计算,因此 Published 会改变,你的视图会重新加载,两个值都会更新。 酷。非常感谢您的进一步解释。在我的情况下,我只需要根据 Published 值更新计算值。所以它与您的解决方案完美配合:)【参考方案2】:

计算属性实质上是函数。 它不能存储值,它只是从 getter 中的其他变量或属性计算值并将值更新回它在 setter 中计算的值。 仔细考虑你的财产的性质。 大多数情况下,您可以直接读写计算属性。 只要记住它是功能核心和属性皮肤

【讨论】:

以上是关于SwiftUI:具有计算属性的 ObservableObject的主要内容,如果未能解决你的问题,请参考以下文章

为 Swiftui 视图转换 @State 的计算属性

SwiftUI 中子结构的计算属性未更新

在 SwiftUI 视图中设置计算属性无法编译

在 SwiftUI 中使用 UserDefaults 进行全局更新

SwiftUI 基于计算属性显示警报

SwiftUI 中已发布的计算属性未更新