The Util Designer
SwiftUI 使用EnvironmentObject減少傳遞變量
xcode 13.4.1, swift 5.5, iOS 15.4
2022-09-27
SwiftUI List元件的基本使用學習List,今次會以這個為基礎講講如何使用EnvironmentObject來減少傳遞變量。
1. 首先把items打包成一個repository:
import Foundation

class ItemRepository : ObservableObject {
    
    @Published var items : [String] = []
    
    init() {
    }
    
    func appendItem() {
        self.items.insert("Item \(items.count)", at: 0)
    }
    
    func remove(_ indexSet : IndexSet) {
        self.items.remove(atOffsets: indexSet)
    }
    
    func move(_ indexSet : IndexSet, _ offset : Int) {
        self.items.move(fromOffsets: indexSet, toOffset: offset)
    }
    
    func size() -> Int {
        return items.count
    }
}
2. 把ListViewExample中的items改成ItemRepository,並把詳情從Text改為ItemView:
import SwiftUI

struct ItemView: View {
    @ObservedObject var itemRepository : ItemRepository
    let index : Int
    var body: some View {
        Group {
            if index < itemRepository.size() {
                Text("\(index)/\(itemRepository.size()) : \(itemRepository.items[index])")
            } else {
                Text("No Data")
            }
        }
    }
}
import SwiftUI

struct ListViewExample : View {
    @ObservedObject var itemRepository : ItemRepository
    
    var body: some View {
        NavigationView {
            List {
                ForEach(0..<itemRepository.size(), id:\.self) { index in
                    NavigationLink {
                        ItemView(itemRepository: itemRepository, index: index)
                    } label: {
                        Text(itemRepository.items[index])
                    }

                }
                .onDelete { indexSet in
                    self.itemRepository.remove(indexSet)
                }
                .onMove { indexSet, offset in
                    self.itemRepository.move(indexSet, offset)
                }
            }
            .toolbar {
                ToolbarItem(placement: .navigationBarTrailing) {
                    EditButton()
                }
                ToolbarItem(placement: .navigationBarLeading) {
                    Button {
                        withAnimation {
                            self.itemRepository.appendItem()
                        }
                    } label: {
                        Image(systemName: "plus")
                    }
                }
            }
        }
    }
}
import SwiftUI

@main
struct Tutorial20220927App: App {
    
    var body: some Scene {
        WindowGroup {
            ListViewExample(itemRepository: ItemRepository())
        }
    }
}

3. 從程式代碼上看,由App到ListViewExampe,再到ItemView,要把itemRepository一層一層的傳下去,如果在多幾層,傳遞的深度會更深。
4.為了解決這樣傳遞的問題,可以將需要一層層傳遞的變量使用環境變量來代替。
5. 首先用environmentObject來把ItemRepository放到環境變量中。
import SwiftUI

@main
struct Tutorial20220927App: App {
    
    var body: some Scene {
        WindowGroup {
            ListViewExample()
                .environmentObject(ItemRepository())
        }
    }
}
6. 然後使用EnvironmentObject來獲得環境變量中的ItemRepository。
import SwiftUI

struct ListViewExample : View {
    @EnvironmentObject var itemRepository : ItemRepository
    
    var body: some View {
        NavigationView {
            List {
                ForEach(0..<itemRepository.size(), id:\.self) { index in
                    NavigationLink {
                        ItemView(index: index)
                    } label: {
                        Text(itemRepository.items[index])
                    }

                }
                .onDelete { indexSet in
                    self.itemRepository.remove(indexSet)
                }
                .onMove { indexSet, offset in
                    self.itemRepository.move(indexSet, offset)
                }
            }
            .toolbar {
                ToolbarItem(placement: .navigationBarTrailing) {
                    EditButton()
                }
                ToolbarItem(placement: .navigationBarLeading) {
                    Button {
                        withAnimation {
                            self.itemRepository.appendItem()
                        }
                    } label: {
                        Image(systemName: "plus")
                    }
                }
            }
        }
    }
}
import SwiftUI

struct ItemView: View {
    @EnvironmentObject var itemRepository : ItemRepository

    let index : Int
    var body: some View {
        Group {
            if index < itemRepository.size() {
                Text("\(index)/\(itemRepository.size()) : \(itemRepository.items[index])")
            } else {
                Text("No Data")
            }
        }
    }
}
7. 以上學習了使用EnvironmentObject構制減少變量的傳遞。