Как передать данные в подпредставление из запроса SwiftData

Я изучаю Swift через проект. Я создаю приложение, в котором пользователь может создать несколько accounts и зарегистрироваться transactions в каждой учетной записи. Модели и представления, имеющие отношение к моему вопросу, показаны ниже.

Когда пользователь нажимает на учетную запись, он попадает на экран транзакций, в котором отображаются все транзакции для этой выбранной учетной записи. Затем пользователь может добавить новую транзакцию в учетную запись.

Что мне не ясно, так это как передать выбранную учетную запись TransactionView и AddTransactionView при использовании NavigationStack. Код, который у меня сейчас есть, выдает ошибку: Cannot convert value of type 'Bindable<Account>' to expected argument type 'Binding<Account>'

Если я поменяю Bindable на .constant, это, кажется, сработает, но я не уверен, правильно ли это.

Был бы признателен за объяснение, почему то, что у меня есть, не работает.

Модели

import Foundation
import SwiftData

@Model
final class Account {
    var created: Date
    var name: String
    var transactions: [Transaction]

    init(created: Date, name: String, transactions: [Transaction] = []) {
        self.created = created
        self.name = name
        self.transactions = transactions
    }
}
import Foundation
import SwiftData

@Model
final class Transaction {
    var date: Date
    var value: Decimal?

    init(date: Date, value: Decimal? = nil) {
        self.date = date
        self.value = value
    }
}

Взгляды

import SwiftData
import SwiftUI

struct AccountsView: View {
    @Query private var accounts: [Account]
    @State private var isAddingAccount = false

    var body: some View {
        NavigationStack {
            List {
                ForEach(accounts) { account in
                    NavigationLink(destination: TransactionsView(account: Bindable(account))) {
                        Text(account.name)
                    }
                }
            }
            .navigationTitle("Accounts")
            .toolbar {
                ToolbarItem {
                    Button {
                        isAddingAccount = true
                    } label: {
                        Label("Add Account", systemImage: "plus")
                    }
                }
            }
        }
        .fullScreenCover(isPresented: $isAddingAccount) {
            AddAccountView(isPresented: $isAddingAccount)
        }
    }
}
import SwiftUI

struct TransactionsView: View {
    @Binding var account: Account
    var transactions: [Transaction] { account.transactions }
    @State private var isAddingTransaction = false

    var body: some View {
        NavigationStack {
            List {
                ForEach(transactions) { transaction in
                    NavigationLink(destination: Text(transaction.date.description)) {
                        Text(transaction.value!.formatted())
                    }
                }
            }
            .toolbar {
                ToolbarItem {
                    Button {
                        isAddingTransaction = true
                    } label: {
                        Label("Add transaction", systemImage: "plus")
                    }
                }
            }
        }
        .fullScreenCover(isPresented: $isAddingTransaction) {
            AddTransactionView(isPresented: $isAddingTransaction, account: $account)
        }
    }
}
import SwiftUI

struct AddTransactionView: View {
    @Binding var isPresented: Bool
    @Binding var account: Account
    @State private var newTransaction = Transaction(date: Date())

    var body: some View {
        NavigationView {
            VStack {
                Form {
                    DatePicker(selection: $newTransaction.date, displayedComponents: .date) { Text("Date") }
                        .datePickerStyle(.compact)

                    TextField(value: $newTransaction.value, format: .number) {
                        Text("Value")
                    }
                }
                Button("Add") {
                    addTransaction(transaction: newTransaction)
                    isPresented = false
                }
            }
            .navigationTitle("Add transaction")
            .navigationBarItems(trailing: Button("Cancel") {
                isPresented = false
            })
        }
    }

    private func addTransaction(transaction: Transaction) {
        withAnimation {
            account.transactions.append(transaction)
        }
    }
}

Для типов моделей вам следует использовать Bindable, а не Binding. Но в этом случае похоже, что вам вообще не нужен Bindable, поскольку вы не изменяете объект Account в дочернем представлении.

Joakim Danielson 14.04.2024 20:41

вам нужен еще один @Query для транзакций

malhal 14.04.2024 21:48

отвечает ли этот ТАК-пост на ваш вопрос: stackoverflow.com/questions/76464884/…

workingdog support Ukraine 15.04.2024 00:40

@JoakimDanielson Я изменяю свойство транзакций объекта Account в представлении внука, т. е. AddTransactionView.

bluprince13 15.04.2024 21:45

@workingdogsupportUkraine нет, потому что я использую Bindable, как следует из ответа, и он не работает.

bluprince13 15.04.2024 21:47

@malhal ах, так что передай учетную запись и запроси учетную запись еще раз. Мне это кажется немного неэффективным — когда я уже знаю, какую учетную запись изменить, не запрашивая данные во второй раз.

bluprince13 15.04.2024 21:50

Нет, запрос транзакций с использованием предиката account =

malhal 15.04.2024 21:55

Тогда попробуйте Bindable

Joakim Danielson 15.04.2024 22:11

Ваш код показывает, что вы не используете Bindable, как рекомендовано. Вы используете @Binding var account: Account в TransactionsView и AddTransactionView. Используйте @Bindable var account: Account

workingdog support Ukraine 16.04.2024 01:34
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
4
9
318
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

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

Вы должны объявлять свойства в своем представлении, используя Bindable, а не Binding, поэтому это должно быть

@Bindable var account: Account

и при вызове представления с таким свойством не нужно ничего особенного делать с переданным значением

NavigationLink(destination: TransactionsView(account: account))

Что касается вашего фактического кода, вам вообще не нужно использовать @Bindable при добавлении транзакций в учетную запись, поскольку объект Account на самом деле не обновляется. Это может немного сбивать с толку, но при добавлении транзакции к объекту учетной записи вы обновляете отношения между ними, а не сам объект учетной записи.

Итак, с учетом сказанного, мы можем удалить @Bindable для свойства Account в обоих дочерних представлениях и изменить объявление на

let account: Account

Другие вопросы по теме