SwiftUI 列表中如何添加多条文本?(数据流)

How to Add multi text into the list in SwiftUI?(Data Flow)

我正在尝试通过 swiftUI 构建一个演示应用程序,它从用户那里获取多文本并将它们添加到下面的列表中,每次用户都有一个应用程序图像按加号按钮 AddListView 向用户显示,用户可以向其中添加多文本 List.I 无法通过新的 switUI 数据流将它们添加到列表中 我不知道如何通过data.(我评论更多信息) 谢谢 这是我的 AddListView 代码:

import SwiftUI

struct AddListView: View {

@State var numberOfTextFiled = 1
@Binding var showAddListView : Bool


var body: some View {
    ZStack {
        
        Title(numberOfTextFiled: $numberOfTextFiled)
        
        VStack {
            ScrollView {
                    ForEach(0 ..< numberOfTextFiled, id: \.self) { item in
                    
                        PreAddTextField()
                        
                    }
                        
            }
        }
        .padding()
        .offset(y: 40)
        
        Buttons(showAddListView: $showAddListView)
          
    }
    .frame(width: 300, height: 200)
    .background(Color.white)
    .shadow(color: Color.black.opacity(0.3), radius: 10, x: 0, y: 10)
}
}

struct SwiftUIView_Previews: PreviewProvider {
    static var previews: some View {
        AddListView(showAddListView: .constant(false))
    }
}

struct PreAddTextField: View {

// I made this standalone struct and use @State to every TextField text be independent
// if i use @Binding to pass data all Texfield have the same text value
@State var textInTextField = ""

var body: some View {
    VStack {
        TextField("Enter text", text: $textInTextField)
    }
}
}

struct Buttons: View {
    @Binding var showAddListView : Bool
    var body: some View {
        VStack {
            HStack(spacing:100) {
                Button(action: {
                    showAddListView = false}) {
                    Text("Cancel")
                }
                Button(action: {
                    showAddListView = false
                    // What should happen here to add Text to List???
                
            }) {
                Text("Add")
            }
        }
    }
    .offset(y: 70)
}
}

struct Title: View {

@Binding var numberOfTextFiled : Int

var body: some View {
    VStack {
        HStack {
            Text("Add Text to list")
                .font(.title2)
            Spacer()
            Button(action: {
                numberOfTextFiled += 1
            }) {
                Image(systemName: "plus")
                    .font(.title2)
            }
        }
        .padding()
        Spacer()
    }
}
}

对于数据模型:

import SwiftUI

struct Text1 : Identifiable , Hashable{
    var id = UUID()
    var text : String
}

var textData = [
    Text1(text: "SwiftUI"),
    Text1(text: "Data flow?"),
]

最后:

import SwiftUI

struct ListView: View {
    
    @State var showAddListView = false

var body: some View {
    NavigationView {
        VStack {
            ZStack {
                List(textData, id : \.self){ text in
                    
                    Text(text.text)
                    
                }
                if showAddListView {
                    AddListView(showAddListView: $showAddListView)
                        .offset(y:-100)
                }
            }
        }
        .navigationTitle("List")
        .navigationBarItems(trailing:
                                Button(action: {showAddListView = true}) {
                                    Image(systemName: "plus")
                                        .font(.title2)
                                }
        )
    }
}
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ListView()
    }
}

由于问题的多项部分,这变得不那么琐碎了。但是,结合使用 ObservableObject 和回调函数,绝对可行。查看代码中的内联注释以了解正在发生的事情:

struct Text1 : Identifiable , Hashable{
    var id = UUID()
    var text : String
}

//Store the items in an ObservableObject instead of just in @State
class AppState : ObservableObject {
    @Published var textData : [Text1] = [.init(text: "Item 1"),.init(text: "Item 2")]
}

//This view model stores data about all of the new items that are going to be added
class AddListViewViewModel : ObservableObject {
    @Published var textItemsToAdd : [Text1] = [.init(text: "")] //start with one empty item
    
    //save all of the new items -- don't save anything that is empty
    func saveToAppState(appState: AppState) {
        appState.textData.append(contentsOf: textItemsToAdd.filter { ![=10=].text.isEmpty })
    }
    
    //these Bindings get used for the TextFields -- they're attached to the item IDs
    func bindingForId(id: UUID) -> Binding<String> {
        .init { () -> String in
            self.textItemsToAdd.first(where: { [=10=].id == id })?.text ?? ""
        } set: { (newValue) in
            self.textItemsToAdd = self.textItemsToAdd.map {
                guard [=10=].id == id else {
                    return [=10=]
                }
                return .init(id: id, text: newValue)
            }
        }
    }
}

struct AddListView: View {
    @Binding var showAddListView : Bool
    @ObservedObject var appState : AppState
    @StateObject private var viewModel = AddListViewViewModel()
    
    var body: some View {
        ZStack {
            Title(addItem: { viewModel.textItemsToAdd.append(.init(text: "")) })
            VStack {
                ScrollView {
                    ForEach(viewModel.textItemsToAdd, id: \.id) { item in //note this is id: \.id and not \.self
                        PreAddTextField(textInTextField: viewModel.bindingForId(id: item.id))
                    }
                }
            }
            .padding()
            .offset(y: 40)
            
            Buttons(showAddListView: $showAddListView, save: {
                viewModel.saveToAppState(appState: appState)
            })
            
        }
        .frame(width: 300, height: 200)
        .background(Color.white)
        .shadow(color: Color.black.opacity(0.3), radius: 10, x: 0, y: 10)
    }
}

struct PreAddTextField: View {
    @Binding var textInTextField : String //this takes a binding to the view model now
    
    var body: some View {
        VStack {
            TextField("Enter text", text: $textInTextField)
        }
    }
}

struct Buttons: View {
    @Binding var showAddListView : Bool
    var save : () -> Void //callback function for what happens when "Add" gets pressed
    var body: some View {
        VStack {
            HStack(spacing:100) {
                Button(action: {
                        showAddListView = false}) {
                    Text("Cancel")
                }
                Button(action: {
                    showAddListView = false
                    save()
                }) {
                    Text("Add")
                }
            }
        }
        .offset(y: 70)
    }
}

struct Title: View {
    
    var addItem : () -> Void //callback function for what happens when the plus button is hit
    
    var body: some View {
        VStack {
            HStack {
                Text("Add Text to list")
                    .font(.title2)
                Spacer()
                Button(action: {
                    addItem()
                }) {
                    Image(systemName: "plus")
                        .font(.title2)
                }
            }
            .padding()
            Spacer()
        }
    }
}

struct ListView: View {
    @StateObject var appState = AppState() //store the AppState here
    @State private var showAddListView = false
    
    var body: some View {
        NavigationView {
            VStack {
                ZStack {
                    List(appState.textData, id : \.self){ text in
                        Text(text.text)
                        
                    }
                    if showAddListView {
                        AddListView(showAddListView: $showAddListView, appState: appState)
                            .offset(y:-100)
                    }
                }
            }
            .navigationTitle("List")
            .navigationBarItems(trailing:
                                    Button(action: {showAddListView = true}) {
                                        Image(systemName: "plus")
                                            .font(.title2)
                                    }
            )
        }
    }
}