Текстовые поля SwiftUI внутри списков - PullRequest
0 голосов
/ 17 февраля 2020

Мне нужен список со строками, в каждой строке по 2 текстовых поля. Эти строки должны быть сохранены в массиве, чтобы я мог использовать данные в другом представлении для дальнейших функций. Если текст в текстовом поле изменяется, текст должен быть сохранен внутри нужной записи в массиве. Вы также можете добавить новые строки в список с помощью кнопки, которая также должна изменить массив для строк.

Цель состоит в том, чтобы получить список пар ключ-значение, каждая из которых может быть изменена, и эти записи будут сохранены с помощью текущий текст.

Может ли кто-нибудь помочь мне и / или дать подсказку для решения этой проблемы?

До сих пор я пробовал что-то вроде этого:

// the array of list entries     
@State var list: [KeyValue] = [KeyValue()]
// the List inside of a VStack  
List(list) { entry in
    KeyValueRow(list.$key, list.$value)
}
// the single item
struct KeyValue: Identifiable {
    var id = UUID()

    @State var key = ""
    @State var value = ""
}
// one row in the list with view elements
struct KeyValueRow: View {

    var keyBinding: Binding<String>
    var valueBinding: Binding<String>

    init(_ keyBinding: Binding<String>, _ valueBinding: Binding<String>){
        self.keyBinding = keyBinding
        self.valueBinding = valueBinding
    }

    var body: some View {
        HStack() {
            TextField("key", text: keyBinding)
            Spacer()
            TextField("value", text: valueBinding)
            Spacer()
        }
    }
}

Также про кнопку добавления новых записей. Проблема заключается в том, что если я сделаю следующее, мой список в представлении станет пустым, и все снова будет пустым (может быть связано: SwiftUI TextField внутри ListView станет пустым после фильтрации элементов списка ?)

Button("Add", action: {
    self.list.append(KeyValue())
})

Ответы [ 2 ]

0 голосов
/ 15 марта 2020

Не нужно возиться с классами, Observable, Identifiable. Вы можете сделать все это с помощью структур.

Обратите внимание, что приведенная ниже версия подойдет для вставок, но не удастся, если вы попытаетесь удалить элементы массива:

import SwiftUI

// the single item
struct KeyValue {
    var key: String
    var value: String
}

struct ContentView: View {
    @State var boolArr: [KeyValue] = [KeyValue(key: "key1", value: "Value1"), KeyValue(key: "key2", value: "Value2"), KeyValue(key: "key3", value: "Value3"), KeyValue(key: "key4", value: "Value4")]

    var body: some View {
        NavigationView {
            // id: \.self is obligatory if you need to insert
            List(boolArr.indices, id: \.self) { idx in
                HStack() {
                    TextField("key", text: self.$boolArr[idx].key)
                    Spacer()
                    TextField("value", text: self.$boolArr[idx].value)
                    Spacer()
                }
            }
            .navigationBarItems(leading:
                Button(action: {
                    self.boolArr.append(KeyValue(key: "key\(UInt.random(in: 0...100))", value: "value\(UInt.random(in: 0...100))"))
                    print(self.boolArr)
                })
                { Text("Add") }
                , trailing:
                Button(action: {
                    self.boolArr.removeLast() // causes "Index out of range" error
                    print(self.boolArr)
                })
                { Text("Remove") })
        }
    }
}

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

Обновление: небольшая хитрость заставить работать также с удалениями.

import SwiftUI

// the single item
struct KeyValue {
    var key: String
    var value: String
}

struct KeyValueView: View {
    @Binding var model: KeyValue
    var body: some View {
        HStack() {
            TextField("Key", text: $model.key)
            Spacer()
            TextField("Value", text: $model.value)
            Spacer()
        }
    }
}

struct ContentView: View {
    @State var kvArr: [KeyValue] = [KeyValue(key: "key1", value: "Value1"), KeyValue(key: "key2", value: "Value2"), KeyValue(key: "key3", value: "Value3"), KeyValue(key: "key4", value: "Value4")]

    var body: some View {
        NavigationView {
            List(kvArr.indices, id: \.self) { i in
                KeyValueView(model: Binding(
                  get: {
                    return self.kvArr[i]
                },
                set: { (newValue) in
                    self.kvArr[i] = newValue
                }))
            }
            .navigationBarItems(leading:
                Button(action: {
                    self.kvArr.append(KeyValue(key: "key\(UInt.random(in: 0...100))", value: "value\(UInt.random(in: 0...100))"))
                    print(self.kvArr)
                })
                { Text("Add") }
                , trailing:
                Button(action: {
                    self.kvArr.removeLast() // Works like a charm
                    print(self.kvArr)
                })
                { Text("Remove") })
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
0 голосов
/ 19 февраля 2020

Я не уверен, что лучше всего обновлять представление с состоянием в массиве, подобном этому, но вот один из подходов, чтобы заставить его работать.

Для моделей я добавил список класс, соответствующий объекту Observable, и каждый элемент KeyValue предупреждает его об изменениях:

class KeyValueList: ObservableObject {

    @Published var items = [KeyValue]()

    func update() {
        self.objectWillChange.send()
    }

    func addItem() {
        self.items.append(KeyValue(parent: self))
    }
}
class KeyValue: Identifiable {

    init(parent: KeyValueList) {
        self.parent = parent
    }

    let id = UUID()
    private let parent: KeyValueList

    var key = "" {
        didSet { self.parent.update() }
    }
    var value = "" {
        didSet { self.parent.update() }
    }
}

Затем я смог просто просмотреть строку, чтобы сохранить только один фрагмент состояния:

struct KeyValueRow: View {

    @State var item: KeyValue

    var body: some View {
        HStack() {
            TextField("key", text: $item.key)
            Spacer()
            TextField("value", text: $item.value)
            Spacer()
        }
    }
}

И для представления списка:

struct TextFieldList: View {

    @ObservedObject var list = KeyValueList()

    var body: some View {

        VStack {
            List(list.items) { item in
                HStack {
                    KeyValueRow(item: item)
                    Text(item.key)
                }
            }
            Button("Add", action: {
                self.list.addItem()
            })

        }
    }
}

Я просто добавил туда Text для тестирования, чтобы увидеть его обновление в реальном времени.

Я не столкнулся с Добавьте кнопку, закрывающую вид, как вы описали. Решает ли это эту проблему и для вас?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...