EnvrionmentObject in SwiftUI

50 阅读2分钟

EnvrionmentObject 和前面说的StateObjecte功能很类似,唯一不同的是。使用 EnvrionmentObject 包装的属性会在全局范围都可以访问使用。只需在父类中注入到环境变量,后续的子类不需要层层传递。

一起来看看例子

视图一:EnvironmentObjectSample

class EnvrionmentObjectViewModel: ObservableObject {
    @Published var languages: [String] = []
    
    init() {
        getLanguages()
    }
    
    func getLanguages() {
        languages.append(contentsOf: ["iOS", "Golang", "Java", "C++", ".Net"])
    }
}

struct EnvironmentObjectSample: View {
    @StateObject var envrionmentViewModel: EnvrionmentObjectViewModel = EnvrionmentObjectViewModel()
    
    var body: some View {
        NavigationView {
            List {
                ForEach(envrionmentViewModel.languages, id: .self) { item in
                    NavigationLink {
                        DetailsView(envrionmentViewModel: envrionmentViewModel, title: item)
                    } label: {
                        Text(item)
                    }
                }
            }
            .navigationTitle("Envrionment Object")
        }
        .tint(Color.black)
    }
}

image.png

视图二:DetailsView

struct DetailsView: View {
    @ObservedObject var envrionmentViewModel: EnvrionmentObjectViewModel
    var title: String
    
    var body: some View {
        ZStack {
            Color.mint.edgesIgnoringSafeArea(.all)
            
            NavigationLink {
                OtherView(envrionmentViewModel: envrionmentViewModel)
            } label: {
                Text(title)
                    .font(.headline)
                    .fontWeight(.semibold)
                    .foregroundColor(Color.mint)
                    .padding()
                    .padding(.horizontal)
                    .background(Color.white.cornerRadius(25.0))
            }
        }
    }
}

image.png

视图三:OtherView

struct OtherView: View {
    @ObservedObject var envrionmentViewModel: EnvrionmentObjectViewModel
    var body: some View {
        ZStack {
            LinearGradient(
                colors: [Color.gray, Color.blue],
                startPoint: .topLeading,
                endPoint: .bottomTrailing)
            .edgesIgnoringSafeArea(.all)
            
            VStack {
                ForEach(envrionmentViewModel.languages, id: .self) { item in
                    Text(item)
                        .font(.title2)
                        .foregroundColor(Color.white)
                }
            }
        }
    }
}

image.png

上述示例总共三个视图

  1. EnvironmentObjectSample视图显示一个列表,点击跳转到DetailsView,会把父类(EnvironmentObjectSample)的envrionmentViewModel数据传入到DetailsView中。因为OtherView中显示的是第一个视图中的数据
  2. DetailsView 视图只需要显示在父类中点击的行的名称
  3. 显示的内容就是第一个视图的array数据

以上代码写起来很麻烦,其实DetailsViewb ing并不需要使用envrionmentViewModel数据,但是介于需要传递数据到OtherView,所以不得不在DetailsView中去声明一个属性来接收上层到数据传递到下一级

如果有十个View需要使用第一个视图中的数据,那么就需要层层传递。这样真的会很奔溃。但是好在SwiftUI中有一个解决此问题的属性,它叫environmentObject. 其实改造很简单,具体如下:

首先,需要在最上层视图加入一个环境变量,并且把需要传递的数据属性放在里面

NavigationView {
  ......            
}
.environmentObject(envrionmentViewModel)

其次,去掉DetailsView视图中接收上次Array数据的代码

//    @ObservedObject var envrionmentViewModel: EnvrionmentObjectViewModel

最后,把@ObservedObject 修饰的变量编变成@EnvironmentObject

//    @ObservedObject var envrionmentViewModel: EnvrionmentObjectViewModel
    @EnvironmentObject var envrionmentViewModel: EnvrionmentObjectViewModel

以下是全部代码:

import SwiftUI

class EnvrionmentObjectViewModel: ObservableObject {
    @Published var languages: [String] = []
    
    init() {
        getLanguages()
    }
    
    func getLanguages() {
        languages.append(contentsOf: ["iOS", "Golang", "Java", "C++", ".Net"])
    }
}

struct EnvironmentObjectSample: View {
    @StateObject var envrionmentViewModel: EnvrionmentObjectViewModel = EnvrionmentObjectViewModel()
    
    var body: some View {
        NavigationView {
            List {
                ForEach(envrionmentViewModel.languages, id: .self) { item in
                    NavigationLink {
                        DetailsView(title: item)
                    } label: {
                        Text(item)
                    }
                }
            }
            .navigationTitle("Envrionment Object")
        }
        .tint(Color.black)
        .environmentObject(envrionmentViewModel)
    }
}

struct DetailsView: View {
//    @ObservedObject var envrionmentViewModel: EnvrionmentObjectViewModel
    var title: String
    
    var body: some View {
        ZStack {
            Color.mint.edgesIgnoringSafeArea(.all)
            
            NavigationLink {
                OtherView()
            } label: {
                Text(title)
                    .font(.headline)
                    .fontWeight(.semibold)
                    .foregroundColor(Color.mint)
                    .padding()
                    .padding(.horizontal)
                    .background(Color.white.cornerRadius(25.0))
            }
        }
    }
}

struct OtherView: View {
//    @ObservedObject var envrionmentViewModel: EnvrionmentObjectViewModel
    @EnvironmentObject var envrionmentViewModel: EnvrionmentObjectViewModel
    
    var body: some View {
        ZStack {
            LinearGradient(
                colors: [Color.gray, Color.blue],
                startPoint: .topLeading,
                endPoint: .bottomTrailing)
            .edgesIgnoringSafeArea(.all)
            
            VStack {
                ForEach(envrionmentViewModel.languages, id: .self) { item in
                    Text(item)
                        .font(.title2)
                        .foregroundColor(Color.white)
                }
            }
        }
    }
}

大家有什么看法呢?欢迎留言讨论。
公众号:RobotPBQ