有人能告诉我为什么这个 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)
            }
        }
    }
}

推荐答案

下面是一个示例代码,它在 Select 列表的一行时显示checkmark,并且 显示selection变量包含List(selection: $selection).

struct ContentView: View {
    // for testing
    // @State var manufacturers = [Manufacturer(name: "one"), Manufacturer(name: "two"), Manufacturer(name: "three")]

    @FetchRequest(
        sortDescriptors: [NSSortDescriptor(keyPath: \Manufacturer.name, ascending: true)],
        animation: .default)
     private var manufacturers: FetchedResults<Manufacturer>
    
    @State private var selection: Manufacturer?
    
    var body: some View{
        Text(selection?.name ?? "no selection") // <-- here
        Section("Manufacturer") {
            List(selection: $selection) {
                ForEach(manufacturers) { 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)
                    }
                }
            }
        }
    }
}

Swift相关问答推荐

Form中的Divider在macOS上并不完全扩展

什么是Swift Concurrency中任务组的正常退出

阴影动画的动画不流畅

在用户将输入保存到SWIFT中的核心数据后,如何创建指向我的应用程序S主页的链接?

在SwiftUI中使用自定义图像填充列表行的正确方法

为SwiftUI文本视图提供固定宽度,并仅在文本换行时使文本视图底部增大

如何让ScrollView缩小到合适的大小,并在没有黑客攻击的情况下占用最小空间

当计数大于索引时,索引超出范围崩溃

SwiftUI .task 视图修改器:运行在哪个线程中?

当字符串包含 \r\n 时,NSRegularExpression 不起作用

Swift 数组拆分成重叠的块

如何在 swift 5.0 中获取当前行

URL appendPathComponent(_:) 已弃用?

@Binding 在@StateObject 和 View 上被发布了两次?

格式化大货币数字

Swift - 订阅视图之外的绑定值

调用从 SwiftUI 视图传递到 PageViewController.Coordinator 的函数

Vapor - 流利的,将对象保存到 PostgreSQL

Swift中switch 盒的详尽条件

Alamofire:如何全局处理错误