有人能告诉我为什么这个 Select 不起作用吗?
如果我单击ForEach
行中的一行,则什么也不会发生:/
@FetchRequest(fetchRequest: ContentView.manufacturerByName)
var manufacturers:FetchedResults<Manufacturer>
@State private var selection: Manufacturer?
Section("Manufacturer") {
List(selection: $selection) {
ForEach(manufacturers, id:\.self) { manufacturer in
Text(manufacturer.name ?? "--")
}
.onDelete { rows in
for row in rows {
let manufacturer = manufacturers[row]
storageProvider.deleteManufacturer(manufacturer)
}
}
}
}
.onDelete
方法运行良好.
我想要实现的是,当单击制造商行时,
- 这一行有一个复选标记
- 并且变量"SELECTION"应设置为制造商
I know that apps for the use of tobacco are not allowed in the AppStore. This is just an app to learn.个
MyApp.swift
个
@main
struct MyApp: App {
let storageProvider = StorageProvider.standard
var body: some Scene {
WindowGroup {
ContentView(storageProvider: storageProvider)
.environment(\.managedObjectContext, storageProvider.persistentContainer.viewContext)
}
}
}
ContentView.swift
个
import SwiftUI
import CodeScanner
struct ContentView: View {
@Environment(\.dismiss) var dismiss
let storageProvider: StorageProvider
// Tobacco
@State var tobaccoName: String = ""
@State var tobaccoNr: String? = nil
@State var ean: String? = nil
// EAN
@State private var isPresentingScanner = false
@State private var scannedCode: String?
// Manufacturers
@FetchRequest(fetchRequest: ContentView.manufacturerByName)
var manufacturers: FetchedResults<Manufacturer>
@StateObject var searchText = SearchText()
@State private var selection: Manufacturer?
var body: some View {
Form {
Section("Tobacco Details") {
TextField("Name", text: $tobaccoName)
TextField("Number (e.g. #017)", text: $tobaccoNr ?? "")
.keyboardType(.decimalPad)
HStack {
TextField("ean", text: $ean ?? "")
.keyboardType(.numberPad)
Button {
isPresentingScanner.toggle()
} label: {
Image(systemName: "camera")
}
}
}
Text(selection?.name ?? "no selection") // <-- here
Button {
storageProvider.saveManufacturer(named: "Test")
} label: {
Text("create manufacturer")
}
Section("Manufacturer") {
List(selection: $selection) {
ForEach(manufacturers, id:\.self) { manufacturer in
// -- here, use a Label
Label(manufacturer.name ?? "--",
systemImage: selection == manufacturer ? "checkmark" : "circle")
}
.onDelete { rows in
for row in rows {
let manufacturer = manufacturers[row]
// storageProvider.deleteManufacturer(manufacturer)
}
}
}
}
}
.navigationTitle("add Tobacco")
.navigationBarTitleDisplayMode(.inline)
// MARK: - EAN-Scan Sheet
.sheet(isPresented: $isPresentingScanner) {
CodeScannerView(codeTypes: [.ean13], showViewfinder: true) { response in
switch response {
case .success(let result): do {
ean = result.string
isPresentingScanner = false
}
case .failure(let error):
print(error.localizedDescription)
}
}
.presentationDetents([.medium])
}
// MARK: - Bottom Button
.safeAreaInset(edge: .bottom) {
VStack {
Button {
if selection == nil {
print("no manufacturer")
} else {
storageProvider.saveTabacco(
named: tobaccoName,
number: tobaccoNr,
ean: ean,
manufacturer: selection
)
dismiss()
}
} label: {
Text("save Tobacco")
.font(.callout)
.frame(maxWidth: .infinity, minHeight: 44)
}
.buttonStyle(.borderedProminent)
.contentTransition(.identity)
}
.frame(maxWidth: .infinity)
.padding([.horizontal, .top])
.background(.ultraThinMaterial)
}
// MARK: - Manufacturer Search-Filter
.onReceive(searchText.$debounced) { query in
/// don't filter when searchbar is empty
guard !query.isEmpty else {
manufacturers.nsPredicate = nil
return
}
/// set filter when someone searches
manufacturers.nsPredicate = NSPredicate(format: "%K CONTAINS[cd] %@", argumentArray: [#keyPath(Manufacturer.name), query])
}
}
}
func ??<T>(lhs: Binding<Optional<T>>, rhs: T) -> Binding<T> {
Binding(
get: { lhs.wrappedValue ?? rhs },
set: { lhs.wrappedValue = $0 }
)
}
StorageProvider.swift
个
public class StorageProvider {
public static var standard = StorageProvider()
public let persistentContainer: NSPersistentContainer
public init() {
persistentContainer = NSPersistentContainer(name: "DataModel")
persistentContainer.loadPersistentStores(completionHandler: { description, error in
if let error = error {
fatalError("Core Data store failed to load with error: \(error)")
}
})
}
}
// MARK: - Functions to call from the Views
public extension StorageProvider {
func saveTabacco(named name: String, number: String?, ean: String?, manufacturer: Manufacturer?) {
let tabacco = Tabacco(context: persistentContainer.viewContext)
tabacco.name = name
tabacco.number = number
tabacco.ean = ean
tabacco.manufacturer = manufacturer
tabacco.creationDate = Date.now
do {
try persistentContainer.viewContext.save()
print("Tabacco saved succesfully")
} catch {
persistentContainer.viewContext.rollback()
print("Failed to save Tabacco: \(error)")
}
}
func saveManufacturer(named name: String) {
let manufacturer = Manufacturer(context: persistentContainer.viewContext)
manufacturer.name = name
do {
try persistentContainer.viewContext.save()
print("Manufacturer saved succesfully")
} catch {
persistentContainer.viewContext.rollback()
print("Failed to save Manufacturer: \(error)")
}
}
func getAllTabaccos() -> [Tabacco] {
let fetchRequest: NSFetchRequest<Tabacco> = Tabacco.fetchRequest()
do {
return try persistentContainer.viewContext.fetch(fetchRequest)
} catch {
print("Failed to fetch tabacco: \(error)")
return []
}
}
func deleteTabacco(_ tabacco: Tabacco) {
persistentContainer.viewContext.delete(tabacco)
do {
try persistentContainer.viewContext.save()
} catch {
persistentContainer.viewContext.rollback()
print("Failed to save context: \(error)")
}
}
func deleteManufacturer(_ manufacturer: Manufacturer) {
persistentContainer.viewContext.delete(manufacturer)
do {
try persistentContainer.viewContext.save()
} catch {
persistentContainer.viewContext.rollback()
print("Failed to save context: \(error)")
}
}
func updateTabacco(_ tabacco: Tabacco) {
do {
try persistentContainer.viewContext.save()
} catch {
persistentContainer.viewContext.rollback()
print("Failed to save context: \(error)")
}
}
}
// MARK: - FetchRequest
extension ContentView {
static var tabaccosByName: NSFetchRequest<Tabacco> {
/// create Request
let request: NSFetchRequest<Tabacco> = Tabacco.fetchRequest()
/// sortDescriptor
request.sortDescriptors = [NSSortDescriptor(keyPath: \Tabacco.name, ascending: true)]
return request
}
static var manufacturerByName: NSFetchRequest<Manufacturer> {
/// create Request
let request: NSFetchRequest<Manufacturer> = Manufacturer.fetchRequest()
/// sortDescriptor
request.sortDescriptors = [NSSortDescriptor(keyPath: \Manufacturer.name, ascending: true)]
return request
}
}
解决方法
Section("Manufacturer") {
List() {
TextField("search for manufacturer", text: $searchText.text)
if manufacturers.isEmpty {
if searchText.debounced != "" {
NavigationLink(destination: CreateManuView(storageProvider: storageProvider, manuName: searchText.text)) {
Label("add **\(searchText.text)**", systemImage: "plus")
.tint(.accentColor)
}
} else {
Text("search for a manufacturer to add")
.foregroundColor(.gray)
}
}
ForEach(manufacturers) { manufacturer in
Button(action: {
selection = manufacturer
}, label: {
HStack {
Text(manufacturer.name ?? "--")
.tint(.primary)
Spacer()
if manufacturer == selection {
Image(systemName: "checkmark")
}
}
})
}
.onDelete { rows in
for row in rows {
let manufacturer = manufacturers[row]
storageProvider.deleteManufacturer(manufacturer)
}
}
}
}