Я использую SwiftData с представлением, предназначенным для обновления @Model.
Вот мои данные:
@Model
final class Pet {
var name: String
var gender: Gender
var type: String
var race: String
var birthdate: Date
var color: String
var eyeColor: String
var photo: Data?
var identification: Identification?
init(
name: String,
gender: Gender,
type: String,
race: String,
birthdate: Date,
color: String,
eyeColor: String,
photo: Data?
) {
self.name = name
self.gender = gender
self.type = type
self.race = race
self.birthdate = birthdate
self.color = color
self.eyeColor = eyeColor
self.photo = photo
}
}
@Model
final class Identification {
var chip: Int?
var chipLocation: String?
var tatoo: String?
var tatooLocation: String?
init(chip: Int?, chipLocation: String?, tatoo: String?, tatooLocation: String?) {
self.chip = chip
self.chipLocation = chipLocation
self.tatoo = tatoo
self.tatooLocation = tatooLocation
}
}
Как видите, у меня есть тип под названием «Идентификация», вложенный в модель «Питомец», и он не является обязательным.
Более того, все его свойства также являются необязательными.
Я пытаюсь отредактировать свойство чипа следующим образом:
struct EditInformationView: View {
@Environment(Pet.self) var pet
var body: some View {
@Bindable var pet = pet
Form {
Section("Identification") {
TextField("Chip", value: $pet.identification?.chip)
}
}
}
}
Мой вопрос очень прост: как мне отредактировать параметр чипа с помощью TextField?
Я немного потерялся и был бы признателен за небольшую помощь, большое спасибо :)
Я попытался использовать необязательную цепочку, как предложил мне Xcode, но это не сработало.
Прежде чем перейти к свойству, вам придется разобраться с вложенным типом. TextField не может знать, как создать идентификацию.
касается ли этот ТАК-пост/ответ вашей проблемы: stackoverflow.com/questions/57021722/swiftui-optional-textfield
@Sweeper, если бы они были равны нулю, я бы отобразил пустое TextField с заполнителем для пользователя, чтобы помочь ему заполнить TextField. Но я не знаю, как это сделать с помощью Bindable :(
@loremipsum Мне бы очень хотелось это сделать, но я не знаю, как это сделать с помощью Bindable, который дает мне привязки к свойствам модели.
@workingdogsupportUkraine Это полезно, но в моем случае у меня есть вложенный тип между ними, и, кроме того, свойство является Int? :х
Вы можете попробовать этот простой подход, используя
промежуточная переменная и .onSubmit
,
как показано в примере кода:
struct EditInformationView: View {
@Environment(Pet.self) var pet
@State private var chipVal = 0 // <--- here
var body: some View {
@Bindable var pet = pet
Form {
Section("Identification") {
TextField("Chip", value: $chipVal, format: .number) // <--- here
.onSubmit {
pet.identification?.chip = chipVal // <--- here
}
}
}
.onAppear {
if let chipv = pet.identification?.chip { // <--- here
chipVal = chipv
}
}
}
}
Обратите внимание, вы также можете использовать .onChange(of: chipVal)
Я попробовал, и это сработало, но когда я закрыл EditInformationView, он не перерисовал родительское представление, а именно: InformationView. Позвольте мне объяснить. Иерархия представлений: PetListView -> InformationView -> EditInformationView (это модальное представление). Я использовал ваш метод, закрыл EditInformationView, и изменение не распространилось на InformationView, это был старый номер чипа. Но он распространился на первый родительский View: PetListView. Может быть, из-за «@Query var pets: [Pet]» внутри?
Это не придает идентификации значения, присвоение просто потеряется.
Вы можете использовать любое из «Дополнительных решений привязки».
Вариант 1
struct EditInformationView: View {
@Environment(Pet.self) var pet
var body: some View {
@Bindable var pet = pet
Form {
Section("Identification") {
if let identification = Binding($pet.identification) {
TextField("Chip", value: identification.chip, format: .number)
} else {
ProgressView()
.task {
pet.identification = Identification(chip: nil, chipLocation: nil, tatoo: nil, tatooLocation: nil)
}
}
}
}
}
}
Вариант 2
struct EditInformationView: View {
@Environment(Pet.self) var pet
var body: some View {
@Bindable var pet = pet
Form {
Section("Identification") {
TextField("Chip", value: ($pet.identification ?? Identification(chip: nil, chipLocation: nil, tatoo: nil, tatooLocation: nil)).chip, format: .number)
}
}
}
}
@MainActor func ?? <Element> (lhs: Binding<Element?>, rhs: Element) -> Binding<Element> {
Binding {
lhs.wrappedValue ?? rhs
} set: { newValue in
lhs.wrappedValue = newValue
}
}
Если вы ищете ТАК, есть еще несколько.
Суть в том, чтобы разобраться с идентификацией до того, как заняться ее свойствами. Есть бесконечное количество способов справиться с этим.
Оба варианта работают отлично, большое спасибо! Я просто хотел спросить вас о .default, я заменил его на Identification init(), полагаю, вы это имели в виду. Но следует ли мне создать переменную «по умолчанию» в модели и предоставить идентификатор по умолчанию?
@Jintae, это стиль, поэтому я удалил его, у меня есть .default
или .sample
, но со SwiftData было бы не лучше, просто ТАК быстро печатать.
Ох, хорошо. Могу ли я спросить вас, что не самое лучшее в SwiftData и стоит ли мне этого избегать?
потому что .default
будет означать, что Identification
имеет static let `default` = Identification()
. Один и тот же объект .default
будет использоваться везде, где вы его используете. Это означает, что у всех Pet
будет одинаковое Identification
. @Джинтэ
@Jintae, ты получишь новый, если воспользуешься static func default() -> Identification {}
, но им так же легко пользоваться init()
О, я вижу, вы совершенно правы, я этого не видел. Еще раз спасибо, вы потрясающие!
Ну, как вы знаете,
identification
иchip
могут быть равны нулю. Что должно произойти, когда они равны нулю?