When I run this code, and click on one of both 'currentZin' in the first screen that comes up with the view WordView, I see the content of the .preview value I used to initialize currentVerse (Verse= .preview) and not the values of the currentVerse that is in the Button action.
When I leave the WordView-sheet and click again the WordView shows the good result. I looks that on the first click the currentVerse = verse in the Button is not executed. If Ido not initialize it, it has a nil value. Can Anyone explain what happens and how to solve it.
struct HymnVerses: View {
var hymn:Hymn
@State private var currentZin: Int = 2
@State private var isLatin: Bool = true
@State private var isMasked: Bool = false
@State private var isTranslation: Bool = true
@State private var currentSentence: String = ""
@State private var showWordView: Bool = false
@State private var currentVerse: Verse = .preview
// Deze calculated property wordt op voorhand berekend.
// Hierdoor blijft de referentie naar het origineel bestaan
// wanneer ik currentVerse bereken. Daarvoor geraakte ik ze altijd kwijt.
private var filteredVerses: [Verse] {
hymn.verses.filter { $0.zin <= currentZin }
}
var body: some View {
List {
ForEach(filteredVerses) { verse in
VStack(alignment: .leading) {
Button {
currentVerse = verse
showWordView = true
} label: {
Text("\(verse.zin). \(currentSentence(for: verse))")
.font(.headline)
}
}
} .onAppear {
currentVerse = filteredVerses.first!
}
}.sheet(isPresented: $showWordView, content: {
WordView(vers: currentVerse, showWordView: $showWordView)
})
.toolbar {
ToolbarItem(placement: .bottomBar) {
Button(isLatin ? "Dutch" : "Latin") {
isLatin.toggle()
}
}
ToolbarItem(placement: .bottomBar) {
Button(isMasked ? "Unmask" : "Mask") {
isMasked.toggle()
}
}
ToolbarItem(placement: .bottomBar) {
Button("Restart") {
currentZin = 1
}
}
ToolbarItem(placement: .bottomBar) {
Button("Next") {
if currentZin < hymn.verses.count {
currentZin += 1
}
}
}
}
}
func maskLetters(in sentence: String, with mask: Character = "*") -> String {
return sentence.map { char in
if char.isLetter {
return String(mask)
} else {
return String(char)
}
}.joined()
}
private func currentSentence(for verse: Verse) -> String {
var temp: String {
return isLatin ? verse.latijn : verse.nederlands
}
if isMasked {
return maskLetters(in: temp)
}
else {
return temp
}
}
}
#Preview {
/// the navigationStack is nodig omdat anders de toolbar niet zichtbaar is met #Preview
NavigationStack {
let allTexts = AllTexts()
HymnVerses(hymn: .preview).environment(allTexts)
}
}
Selecting any option will automatically load the page
Post
Replies
Boosts
Views
Activity
I'm currently developing a SwiftUI application that utilizes SwiftData for data management. I am facing a challenge when trying to filter a query. Specifically, I want to filter a list of Item objects to match a Product instance that is passed to my View.
error :
Instance member 'product' cannot be used on type 'MainItemListEncap'; did you mean to use a value of this type instead
The view
//
// 311.1.1. MainRefToItem.swift
// ComparePrice
//
// Created by Herman VAN CAUWELAERT on 11/12/2024.
//
import SwiftUI
import SwiftData
struct MainItemListEncap: View {
@Bindable var product: Product
@Query(
filter: #Predicate { item in
item.productGroup == product
},
sort: [SortDescriptor(\Item.name)]
)
var items: [Item]
@Environment(\.modelContext) var modelContext
var body: some View {
ForEach(items) { item in
VStack(alignment: .leading) {
Text(item.name)
Text(item.description)
}
}
}
}
the product class.
import SwiftData
import Foundation
@Model
final class Product: CustomStringConvertible, CustomDebugStringConvertible {
@Attribute(.unique) var productName: String
var productDescription: String
var outputCurrency: String
// Gebruik een `String` als opslag voor `outputSystem`
var outputSystemRawValue: String = MeasurementSystem.metric.rawValue
// Computed property om `MeasurementSystem` te gebruiken
var outputSystem: MeasurementSystem {
get {
MeasurementSystem(rawValue: outputSystemRawValue)
}
set {
outputSystemRawValue = newValue.rawValue
}
}
// er zijn verschillend item versies voor een product
// als er een hoofdproduct gedelete wordt, dan zullen alle onderliggende items ook gedelete worden
@Relationship(deleteRule: .cascade, inverse: \Item.productGroup) var refToItems = [Item]()
init(productName: String = "", productDescription: String = "what is this product", outputCurrency: String = "EUR", outputSystem: MeasurementSystem = MeasurementSystem.metric) {
self.productName = productName
self.productDescription = productDescription
self.outputCurrency = outputCurrency
self.outputSystem = outputSystem
}
}
the item class
import Foundation
import SwiftData
@Model
final class Item: CustomStringConvertible, CustomDebugStringConvertible {
var timestamp: Date
@Attribute(.unique) var name: String
var productGroup: Product?
var shop: String //TODO: becomes Shop
var price: Double
var currency: String
var quantity: Double
var unit: String //TODO: becomes Unit
var isShown : Bool
var minimumQuantity: String
var rateStr: String {
conversionRate != nil ? " (rate: \(ValueFormatter.shared.format(conversionRate!)))" : ""
}
init(timestamp: Date = Date()
, name: String
, shop: String = "Colruyt"
, price:Double = 1.00
, currency: String = "EUR"
, quantity: Double = 1.0
, unit: String = "g"
, isShown:Bool = true
, conversionRate: Decimal? = 1
, minimumQuantity: String = "1"
) {
self.timestamp = timestamp
self.name = name
self.shop = shop
self.price = price
self.currency = currency
self.quantity = quantity
self.unit = unit
self.isShown = isShown
self.conversionRate = conversionRate
self.minimumQuantity = minimumQuantity
}
}