如何在UITableViewCell中复用KPDDropDownMenu解决滚动复用问题
Hey, I've dealt with this exact reusable cell state issue with dropdown menus before—let's break down the fixes tailored to your code:
1. Reset Dropdown State in setup (Critical!)
The main problem is that when cells are reused, the old dropdown state (expanded status, leftover items, or stale closures) sticks around. Update your setup method to fully reset the dropdown before applying new data:
func setup(withSearchObject searchObject: SearchObject) { // Collapse the dropdown immediately to clear old expanded state dropDownOptions.collapseAllOptions(animated: false) // Clear any old closure references to prevent wrong section updates dropDownOptions.optionsDidAppearedWithHeightClosure = nil // Now apply the new content searchkeyL.text = searchObject.searchKey dropDownOptions.items = searchObject.options }
2. Fix Stale IndexPath in Closures
Your current closure captures the original indexPath from cellForRowAt, but when the cell is reused, that indexPath no longer matches the cell's current position. Instead, get the cell's live indexPath when the closure fires:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let searchObject = searchObjects[indexPath.section] let cell = tableView.dequeueReusableCell(withIdentifier: searchObject.viewIdentifier, for: indexPath) (cell as! CommonSearch).setup(withSearchObject: searchObject) if let dropDownCell = cell as? _DropDownTVC { dropDownCell.dropDownOptions.optionsDidAppearedWithHeightClosure = { [weak self] optionsHeight in guard let self = self, let currentIndexPath = tableView.indexPath(for: dropDownCell) else { return } // Use the current, live indexPath instead of the original captured one self.searchObjects[currentIndexPath.section].viewHeight += optionsHeight tableView.beginUpdates() tableView.endUpdates() } // Bonus: Add a closure for when the dropdown closes to adjust height back dropDownCell.dropDownOptions.optionsDidDisappearedWithHeightClosure = { [weak self] optionsHeight in guard let self = self, let currentIndexPath = tableView.indexPath(for: dropDownCell) else { return } self.searchObjects[currentIndexPath.section].viewHeight -= optionsHeight tableView.beginUpdates() tableView.endUpdates() } } return cell }
Notice we use [weak self] to avoid memory leaks, and fetch the cell's current indexPath directly from the tableView when the event triggers.
3. Use prepareForReuse to Clean Up Reusable Cells
Add this to your _DropDownTVC class to ensure all old state is wiped before the cell is reused—this is a standard practice for UITableViewCell reuse:
override func prepareForReuse() { super.prepareForReuse() // Reset dropdown state dropDownOptions.collapseAllOptions(animated: false) dropDownOptions.optionsDidAppearedWithHeightClosure = nil dropDownOptions.optionsDidDisappearedWithHeightClosure = nil // Reset text labels to avoid leftover text searchkeyL.text = nil }
This acts as a safety net, ensuring no old content or callbacks linger when the cell is recycled.
4. Verify Height Adjustment Logic
Make sure your viewHeight property is correctly managing the cell's height—without the close closure I added earlier, your height would keep increasing every time the dropdown opens, leading to weird cell sizing over time. The close callback fixes that by subtracting the dropdown height when it's hidden.
These changes address both the content residue issue (by fully resetting the dropdown on reuse) and the incorrect section updates (by using the cell's live indexPath in closures).
内容的提问来源于stack exchange,提问作者MAHMOUD OMARA

