You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过Delegate实现多按钮数据过滤?类型转换错误排查

Hey there! Let's fix that error first, then we can even clean up the code to be more maintainable.

What's Causing the Error?

The error Cannot convert value of type 'RoundButton?' to expected argument type 'String?' happens because your acceptSelections method is passing button instances (jewelryBtn, hatBtn, etc.) to your delegate method—but that method expects optional String values (the actual category names), not the buttons themselves.


Step 1: Fix the Immediate Error

Update your acceptSelections method in FilterViewController to pass category strings (instead of buttons) based on each button's selected state:

@IBAction func acceptSelections(_ sender: Any) {
    // Map each button's selected state to its category string (or nil if not selected)
    let selectedJewelry = jewelryBtn.isSelected ? "Jewelry" : nil
    let selectedHat = hatBtn.isSelected ? "Hat" : nil
    let selectedShoe = shoeBtn.isSelected ? "Shoe" : nil
    let selectedGear = gearBtn.isSelected ? "Gear" : nil
    let selectedApparel = apparelBtn.isSelected ? "Apparel" : nil
    
    // Now pass the correct string values to the delegate
    delegate?.controller(self, 
                        didSelectCategory: selectedJewelry, 
                        hat: selectedHat, 
                        shoe: selectedShoe, 
                        gear: selectedGear, 
                        apparel: selectedApparel)
    dismiss(animated: true)
}

This will resolve the type mismatch error immediately.


Your current delegate method has 5 separate optional parameters, which can get messy as you add more categories. Let's simplify it to pass a set of selected categories instead:

1. Update the FiltersViewControllerDelegate Protocol

protocol FiltersViewControllerDelegate: NSObjectProtocol {
    // Pass a Set of selected category names (nil if none are selected)
    func controller(_ controller: FilterViewController, didSelectCategories categories: Set<String>?)
}

2. Update acceptSelections in FilterViewController

@IBAction func acceptSelections(_ sender: Any) {
    var selectedCategories = Set<String>()
    
    // Add category names to the set if their corresponding button is selected
    if jewelryBtn.isSelected {
        selectedCategories.insert("Jewelry")
    }
    if hatBtn.isSelected {
        selectedCategories.insert("Hat")
    }
    if shoeBtn.isSelected {
        selectedCategories.insert("Shoe")
    }
    if gearBtn.isSelected {
        selectedCategories.insert("Gear")
    }
    if apparelBtn.isSelected {
        selectedCategories.insert("Apparel")
    }
    
    // Pass nil if no categories are selected, otherwise pass the set
    delegate?.controller(self, didSelectCategories: selectedCategories.isEmpty ? nil : selectedCategories)
    dismiss(animated: true)
}

3. Update the Delegate Implementation in HomeViewController

This makes your filtering logic cleaner and easier to maintain:

extension HomeViewController: FiltersViewControllerDelegate{
    func query(withCategories categories: Set<String>?) -> Query {
        var filteredQuery = baseQuery
        
        guard let categories = categories, !categories.isEmpty else {
            // No filters selected: hide the active filters bar
            stackViewHeightConstraint.constant = 0
            activeFiltersStackView.isHidden = true
            return filteredQuery
        }
        
        // Show the active filters bar
        stackViewHeightConstraint.constant = 44
        activeFiltersStackView.isHidden = false
        
        // Filter Firestore query to include any of the selected categories
        filteredQuery = filteredQuery.whereField("category", in: categories)
        return filteredQuery
    }
    
    func controller(_ controller: FilterViewController, didSelectCategories categories: Set<String>?) {
        let filteredQuery = query(withCategories: categories)
        self.query = filteredQuery
        
        // Update filter labels visibility and text
        jewelryFilterLbl.isHidden = !(categories?.contains("Jewelry") ?? false)
        jewelryFilterLbl.text = "Jewelry"
        
        hatFilterLbl.isHidden = !(categories?.contains("Hat") ?? false)
        hatFilterLbl.text = "Hat"
        
        shoeFilterLbl.isHidden = !(categories?.contains("Shoe") ?? false)
        shoeFilterLbl.text = "Shoe"
        
        gearFilterLbl.isHidden = !(categories?.contains("Gear") ?? false)
        gearFilterLbl.text = "Gear"
        
        apparelFilterLbl.isHidden = !(categories?.contains("Apparel") ?? false)
        apparelFilterLbl.text = "Apparel"
        
        // Fetch filtered products and refresh the table view
        fetchFilteredProducts()
    }
    
    // Add this method to fetch products based on the filtered query
    private func fetchFilteredProducts() {
        guard let query = query else { return }
        
        query.addSnapshotListener { [weak self] snapshot, error in
            guard let self = self, error == nil, let snapshot = snapshot else {
                self?.productSetup = []
                self?.tableView.reloadData()
                return
            }
            
            // Parse and sort the filtered products
            self.productSetup = snapshot.documents.compactMap { ProductList(dictionary: $0.data()) }
                .sorted(by: { $0.itemName < $1.itemName })
            
            // Refresh the table view
            self.tableView.reloadData()
        }
    }
}

Important Note

I noticed your original code wasn't refreshing the table view after setting the filtered query. The fetchFilteredProducts method above fixes this by listening to the filtered query's changes and updating your product list + reloading the table.

内容的提问来源于stack exchange,提问作者Evelyn

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.06 08:27:39