如何在SwiftUI的所有选项卡中使用相同的数据? (ObservableObject或EnvironmentObject)

时间:2020-06-28 05:52:38

标签: swift tabs swiftui observable environment

这是一个带有2个标签的应用程序的非常简单的示例。在这两个选项卡上,我都有“当前月份”的简单自定义选择器。该选择器位于单独的DateView.swift中。

在每个标签上,我都有一个文本,可从选择器中读取数据(我将使用它来过滤每个标签上的列表)。

我想做什么:

更改日期应该在全球范围内有效。在一个标签上更改月份后,我应该在另一个标签上看到本月。

如何使其工作?目前,我正在使用ObservableObject。我执行错了吗? ObservableObject只是一种方式绑定,而我需要双向绑定吗?我应该改用EnvironmentObject吗?


这是我到目前为止的代码:

ContentView.swift

    import SwiftUI
    
    struct ContentView: View {
        @State private var selection = 0
     
        var body: some View {
            TabView(selection: $selection){
                FirstTabView()
                    .tabItem {
                        VStack {
                            Image("first")
                            Text("First")
                        }
                    }
                    .tag(0)
                SecondTabView()
                    .tabItem {
                        VStack {
                            Image("second")
                            Text("Second")
                        }
                    }
                    .tag(1)
            }
        }
    }
    
    struct ContentView_Previews: PreviewProvider {
        static var previews: some View {
            ContentView()
        }
    }

FirstTabView.swift

import SwiftUI

struct FirstTabView: View {
    
    @ObservedObject var selectedMonth = SelectedDate()
    
    var body: some View {
        NavigationView {
            VStack {
                DateView(selectedMonth: selectedMonth)
                
                Text("\(selectedMonth.selectedMonth)")
                .padding()
            }
            .navigationBarTitle("First Tab")
        }
    }
}

struct FirstTabView_Previews: PreviewProvider {
    static var previews: some View {
        FirstTabView()
    }
}

SecondTabView.swift

import SwiftUI

struct SecondTabView: View {
    
    @ObservedObject var selectedMonth = SelectedDate()
    
    var body: some View {
        NavigationView {
            VStack {
                DateView(selectedMonth: selectedMonth)
                
                Text("\(selectedMonth.selectedMonth)")
                .padding()
            }
            .navigationBarTitle("Second Tab")
        }
    }
}

struct SecondTabView_Previews: PreviewProvider {
    static var previews: some View {
        SecondTabView()
    }
}

DateView.swift

import SwiftUI

class SelectedDate: ObservableObject {
    @Published var selectedMonth: Date = Date()
}

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

            Text("\(selectedMonth.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: selectedMonth.selectedMonth) {
            self.selectedMonth.selectedMonth = selectedMonth
        }
    }
}

struct DateView_Previews: PreviewProvider {
    
    struct BindingTestHolder: View {
        @State var testItem: Date = Date()
        var body: some View {
            DateView()
        }
    }
    
    static var previews: some View {
        BindingTestHolder()
    }
}

1 个答案:

答案 0 :(得分:1)

我应该改用EnvironmentObject吗?

是的,如果您需要全局 的内容,那么EnvironmentObject是正确的选择。

所以

struct ContentView: View {
    @State private var selection = 0
 
    // declare & create here (or use below alternates)
    var selectedMonth = SelectedDate()

    // alternates 
    //@EnvironmentObject var selectedMonth: SelectedDate
    //@StateObject var selectedMonth = SelectedDate() // << Xcode12/SwiftUI2

    var body: some View {
        TabView(selection: $selection){
            FirstTabView()
                .tabItem {
                    VStack {
                        Image("first")
                        Text("First")
                    }
                }
                .tag(0)
            SecondTabView()
                .tabItem {
                    VStack {
                        Image("second")
                        Text("Second")
                    }
                }
                .tag(1)
        } 
        // << inject here (not needed if above @EnvironmentObject is used
        .environmentObject(selectedMonth) 
    }
}

如果未使用,则不需要选项卡中的EnvironmentObject对象,因为它是通过层次结构自动传输的

struct FirstTabView: View {
    
    var body: some View {
        NavigationView {
            VStack {
                DateView()

        // ..other code
struct SecondTabView: View {
    
    var body: some View {
        NavigationView {
            VStack {
                DateView()

        // ..other code

最后在哪里使用

struct DateView: View {
    
    // only declared, and will be injected here automatically
    @EnvironmentObject var selectedMonth: SelectedDate

   // .. other code