SwiftUI-将@State变量传递给多个视图麻烦

Posted

技术标签:

【中文标题】SwiftUI-将@State变量传递给多个视图麻烦【英文标题】:SwiftUI- passing @State variables to multiple views trouble 【发布时间】:2021-09-14 19:52:00 【问题描述】:

我正在尝试在 UI 方面制作一个类似于 iphone 提醒应用程序的应用程序。我有一个视图,其中有一个可以从中添加和删除项目的列表,我还有一个用于添加项目的视图,该视图允许我命名项目并选择一些选项,并且我有另一个视图,当项目在列表被选中,我希望能够显示我所做的名称和选项,但它不显示。 列表视图的代码

struct DotView: View 

enum ActiveSheet: String, Identifiable 
    case SwiftUIView, EditView
    var id: String 
        return self.rawValue
    


@EnvironmentObject var listViewModel: ListViewModel
@AppStorage("dotApiKey") var selectedDotApi: String = ""
@State var dotName:String = ""
@State var dotNumber:String = ""
@State var selection:String = ""
@State var triggerSelection:String = ""
@State var searchText:String = ""
@State var plugUsername:String = ""
@State var plugPassword:String = ""
@State var toggleNotification:Bool
@State var activeSheet : ActiveSheet? = nil
    
@Binding var show : Bool

var body: some View 
        ZStack
            HStack 
                EditButton()
                    .padding(.leading)
                Spacer()
                Button(action: 
                    self.activeSheet = .SwiftUIView
                , label: 
                    Text("Add")
                )
                    .padding(.trailing)
            

            ZStack 
                List 
                    ForEach(listViewModel.dotitems, id:\.dotId) dotitem in
                        Button(action:  self.activeSheet = .EditView , label: 
                            DotItemListView(dotitem: dotitem)
                        )
                    
                    .onDelete(perform: listViewModel.deleteItem)
                    .onMove(perform: listViewModel.moveItem)
                    .listRowBackground(Color("textBG"))
                    
                .listStyle(PlainListStyle())
                .background(Color("textBG"))
                .frame(height: 300)
                .cornerRadius(10)
                .padding(.horizontal)                    
           
       
   .sheet(item: $activeSheet) sheet in
        switch sheet 
        case .SwiftUIView:
            SwiftUIView(dotName: dotName, dotNumber: dotNumber, selection: selection, triggerSelection: triggerSelection, searchText: searchText, plugUsername: plugUsername, plugPassword: plugPassword, show: $show)
        case .EditView:
            EditView(show:$show)
        
    

当我将一个项目添加到列表中时,它会在每一行中显示它

struct DotItemListView:View 
let dotitem: DotItem

var body: some View
    HStack 
        Text(dotitem.dotName)
        Spacer()
        Text(dotitem.selection)
        Spacer()
        Text(dotitem.dotNumber)
        Spacer()
    

这就是我将每个项目添加到列表中的方式

struct DotItem:Equatable, Codable
var dotId = UUID().uuidString
let dotName:String
let dotNumber:String
let selection:String


class ListViewModel: ObservableObject 

@Published var dotitems: [DotItem] = [] 
    didSet 
        saveItem()
    

let dotitemsKey: String = "dotitems_list"

init() 
    getDotItems()


func getDotItems() 
    guard
        let data = UserDefaults.standard.data(forKey: dotitemsKey),
        let savedDotItems = try? JSONDecoder().decode([DotItem].self, from: data)
    else  return 
    
    self.dotitems = savedDotItems


func deleteItem(indexSet: IndexSet)
    dotitems.remove(atOffsets: indexSet)


func moveItem(from: IndexSet, to: Int)
    dotitems.move(fromOffsets: from, toOffset: to)


func addItem(dotName: String, dotNumber: String, selection: String)
    let newItem = DotItem(dotName: dotName, dotNumber: dotNumber, selection: selection)
    dotitems.append(newItem)
    
    print(newItem)


func saveItem() 
    if let encodedData = try? JSONEncoder().encode(dotitems) 
        UserDefaults.standard.set(encodedData, forKey: dotitemsKey)
    


这是我为每个项目输入数据的视图

struct SwiftUIView: View 

@Environment(\.presentationMode) var presentationMode
@EnvironmentObject var listViewModel: ListViewModel
@AppStorage("dotApiKey") var selectedDotApi: String = ""
@State var dotName:String
@State var dotNumber:String
@State var selection:String
@State var triggerSelection:String
@State var selectedColor = Color.black
@State var searchText:String
@State var plugUsername:String
@State var plugPassword:String
@ObservedObject var vm = getDeviceNames()
@State var triggerDot:Bool = false
@State var toggleOnOff:Bool = false
@State var toggleLightColor:Bool = false
@State var isSearching:Bool = false

@StateObject var camera = CameraModel()

@Binding var show : Bool

var body: some View 
    NavigationView 
                    Form 
                        Section(header: Text("Info")) 
                            TextField("Name", text: $dotName)
                            TextField("Number", text: $dotNumber)
                            Picker(selection: $selection, label: Text("Discover Plug")) 
                                ForEach(vm.dataSet, id:\.self)  item in
                                    Text(item.Device).tag(item.Device)
                                
                            
                            Toggle(isOn: $isSearching, label: 
                                Text("Have smart plugs?")
                            )
                            if isSearching 
                                HStack
                                    Text("Casa")
                                    Spacer()
                                    Button(action: sendPlugDict(), label: 
                                        Text("Login")
                                    )
                                
                                TextField("Username", text: $plugUsername)
                                TextField("Password", text: $plugPassword)
                                
                            
                        
                        Section 
                            Toggle(isOn: $toggleOnOff, label: 
                                Text("On/Off")
                            )
                        
                        Section 
                            Toggle(isOn: $toggleLightColor, label: 
                                Text("Light Color")
                            )
                            if toggleLightColor 
                                ColorPicker("Choose Light Color", selection: $selectedColor)
                            
                        
                        Section 
                            if listViewModel.dotitems.isEmpty == false 
                                Toggle(isOn: $triggerDot, label: 
                                    Text("Add a DOT to trigger")
                                )
                                if triggerDot 
                                    Picker(selection: $triggerSelection, label: Text("Select DOT")) 
                                        ForEach(listViewModel.dotitems, id:\.dotId) dotitem in
                                            DotItemListView(dotitem: dotitem)
                                        
                                    
                                
                            
                        
                    
                    .navigationBarTitle("")
                    .navigationBarHidden(true)
                


这是我在选择任何列表项时尝试显示数据的视图,除了少数地方外,与上面基本相同

struct EditView: View 
@Environment(\.presentationMode) var presentationMode
@EnvironmentObject var listViewModel: ListViewModel
@ObservedObject var vm = getDeviceNames()
@State var dataSet = [Result]()
@State var dotName:String = ""
@State var dotNumber:String = ""
@State var selection:String = ""
@State var triggerSelection:String = ""
@State var selectedColor = Color.black
@State var searchText:String = ""
@State var plugUsername:String = ""
@State var plugPassword:String = ""
@State var triggerDot:Bool = false
@State var toggleOnOff:Bool = false
@State var toggleLightColor:Bool = false
@State var isSearching:Bool = false

@Binding var show : Bool

var body: some View 
    NavigationView 
                    Form 
                        Section(header: Text("Info"))  
                            TextField(dotName, text: $dotName)
                            TextField(dotNumber, text: $dotNumber)
                            Picker(selection: $selection, label: Text("Discorver Plug")) 
                                ForEach(dataSet, id:\.self)  item in
                                    Text(item.Device).tag(item.Device)
                                
                            
                            Toggle(isOn: $isSearching, label: 
                                Text("Have smart plugs?")
                            )
                            if isSearching 
                                HStack
                                    Text("Casa")
                                    Spacer()
                                    Button(action: 
                                            SwiftUIView(dotName: dotName, dotNumber: dotNumber, selection: selection, triggerSelection: triggerSelection, searchText: searchText, plugUsername: plugUsername, plugPassword: plugPassword, show: $show).sendPlugDict(), label: 
                                        Text("Login")
                                    )
                                
                                TextField("Username", text: $plugUsername)
                                TextField("Password", text: $plugPassword)
                                
                            
                        
                        Section 
                            Toggle(isOn: $toggleOnOff, label: 
                                Text("On/Off")
                            )
                        
                        Section 
                            Toggle(isOn: $toggleLightColor, label: 
                                Text("Light Color")
                            )
                            if toggleLightColor 
                                ColorPicker("Choose Light Color", selection: $selectedColor)
                            
                        
                        Section 
                            if listViewModel.dotitems.isEmpty == false 
                                Toggle(isOn: $triggerDot, label: 
                                    Text("Add a DOT to trigger")
                                )
                                if triggerDot 
                                    Picker(selection: $triggerSelection, label: Text("Select DOT")) 
                                        ForEach(listViewModel.dotitems, id:\.dotId) dotitem in
                                            DotItemListView(dotitem: dotitem)
                                        
                                    
                                
                            
                        
                    
                    .navigationBarTitle("")
                    .navigationBarHidden(true)
                



我知道这有很多事情要做,但我们将不胜感激任何帮助

【问题讨论】:

你能想出一个minimal reproducible example吗?我怀疑这里有很多内容可以删除,同时仍然可以证明问题。 【参考方案1】:

在您想要将状态传递给的视图上使用@Binding。

视图1:

@State var a = true 

View2(a: $a)

视图2:

@Binding var a : Bool

要在全局范围内传递数据,请使用 @EnvoirmentObject :

示例:

@main
struct YourApp: App 

    @StateObject var session = Session()
    
    var body: some Scene 
        WindowGroup 
            SplashView()
                .environmentObject(session)
        
    

会话:

class Session: ObservableObject 

    /// user signin state
    @Published  var isSignedIn : Bool = false

视图1,视图2,视图3 ....:

struct SplashView: View 
    
    @EnvironmentObject var session : Session
    
    var body: some View 
        VStack
            SignInView()
                .opacity(session.isSignedIn ? 0:1)
        
        .background(Color.background.ignoresSafeArea())
    

【讨论】:

我将如何使用 3 个视图执行此操作?因为如果我在创建项目的视图上执行您的View1 示例,并在显示项目数据的视图上使用您的View2 示例,它会让我在显示它的第三个视图上使用@State var该列表是因为在您的 View1 示例中,您的 View2(a: $a) 与您的 @State var 视图相同,但我的视图不同 基本上我是这样设置的- View1 @State var a: Bool, View2 @Binding var a: Bool, View3 @987654334 @ 但它没有用。如果我在View1View2 中都使用@Binding,当我选择一个列表项时,它会显示在View3 中,但是当我添加另一个项目时,它会显示数据并且它不应该显示 应该是这样的:State -> Binding -> Binding -> .... 但是在其中一个绑定视图中我调用了状态视图,这使我将参数更深入地放入我的应用程序中 在我编辑答案时使用 EnvironmentObject

以上是关于SwiftUI-将@State变量传递给多个视图麻烦的主要内容,如果未能解决你的问题,请参考以下文章

将多个子视图传递给 SwiftUI 中的视图

通过@State 传递 NSManagedObject ...? SwiftUI

如何将一个 SwiftUI 视图作为变量传递给另一个视图结构

Init State var 来自另一个视图,swiftui [重复]

如何将我的 SwiftUI 视图的 @State 交换为我的视图模型 @Published 变量?

如何最好地传递数据以在 swiftui 中进行表单编辑(同时让该数据可用于父视图和其他子视图)?