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

如何在UITableView单元格中切换Realm对象并刷新数据?

How to Update a Single UITableView Cell When Tapping Its "Swap" Button

Hey there! I see you're trying to refresh just the specific cell where the swap button was tapped, instead of reloading the entire table. Let's break this down into simple, actionable steps to get this working right.

First: Get the IndexPath of the Tapped Cell

The key here is figuring out which cell's button was pressed so we can target only that one. In your swapButtonPressed method, we can trace the button's parent views to find its parent cell, then get the corresponding IndexPath from the table view:

@IBAction func swapButtonPressed(_ sender: UIButton) {
    // Find the parent cell by traversing up the view hierarchy
    var currentView = sender as UIView
    var targetCell: WorkoutCell?
    
    while let superview = currentView.superview {
        if let cell = superview as? WorkoutCell {
            targetCell = cell
            break
        }
        currentView = superview
    }
    
    guard let cell = targetCell, let tableView = cell.superview as? UITableView else {
        return // Exit if we can't find the cell or table view
    }
    
    let indexPath = tableView.indexPath(for: cell)!
    swapAndUpdateCell(at: indexPath, in: tableView)
}

This traversal method is more reliable than hardcoding superview?.superview because it works even if you adjust your cell's layout later.

Option 1: Directly Update the Cell's UI (Quick Fix)

If you don't need to update a backing data source (though I'd recommend option 2 for consistency), you can generate the new exercise and update the cell's labels directly:

func swapAndUpdateCell(at indexPath: IndexPath, in tableView: UITableView) {
    let realmExercisePool = realm.objects(ExerciseGeneratorObject.self)
    guard !realmExercisePool.isEmpty else {
        print("No exercises available to swap!")
        return // Prevent crash if the pool is empty
    }
    
    // Generate your new exercise
    let randomIndex = Int(arc4random_uniform(UInt32(realmExercisePool.count)))
    let newExercise = realmExercisePool[randomIndex].generateExercise()
    
    // Grab the cell and update its labels
    if let targetCell = tableView.cellForRow(at: indexPath) as? WorkoutCell {
        targetCell.exerciseName.text = newExercise.name
        targetCell.repsNumber.text = "\(newExercise.reps)" // Adjust this to match your WorkoutExercise's rep property
    }
}

This is the better approach because it keeps your data source in sync with the UI, which prevents issues when cells get reused later. We'll use a closure to let the cell notify the view controller when the swap button is tapped:

First, update your WorkoutCell class to add a closure property and a configure method:

class WorkoutCell : UITableViewCell {
    @IBOutlet weak var exerciseName: UILabel!
    @IBOutlet weak var repsNumber: UILabel!
    
    // Closure to notify the view controller about the tap
    var onSwapTapped: (() -> Void)?

    @IBAction func swapButtonPressed(_ sender: Any) {
        onSwapTapped?() // Trigger the closure when the button is pressed
    }
    
    // Helper method to set up the cell with exercise data
    func configure(with exercise: WorkoutExercise) {
        exerciseName.text = exercise.name
        repsNumber.text = "\(exercise.reps)"
    }
}

Then, in your view controller's tableView(_:cellForRowAt:) method, set up the closure to update the data source and refresh the cell:

// Assume you have a data source array like this in your view controller
var workoutExercises: [WorkoutExercise] = []

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "WorkoutCell", for: indexPath) as! WorkoutCell
    let currentExercise = workoutExercises[indexPath.row]
    
    // Configure the cell with current data
    cell.configure(with: currentExercise)
    
    // Set up the swap closure
    cell.onSwapTapped = { [weak self] in
        guard let self = self else { return }
        
        let realmExercisePool = self.realm.objects(ExerciseGeneratorObject.self)
        guard !realmExercisePool.isEmpty else {
            print("No exercises in pool!")
            return
        }
        
        // Generate new exercise
        let randomIndex = Int(arc4random_uniform(UInt32(realmExercisePool.count)))
        let newExercise = realmExercisePool[randomIndex].generateExercise()
        
        // Update the data source
        self.workoutExercises[indexPath.row] = newExercise
        
        // Only reload the tapped cell - no full table reload!
        tableView.reloadRows(at: [indexPath], with: .automatic)
    }
    
    return cell
}

Why Your Previous Attempts Didn't Work

Trying to access cell.workoutName inside the cell itself was off—you could have used self.exerciseName directly, but the bigger issue was not having the IndexPath to ensure you're updating the right data (and avoiding reuse bugs). The closure approach lets the view controller handle data updates, which is the proper separation of concerns in MVC.

Also, don't forget that check for an empty realmExercisePool—it'll save you from a crash if there are no exercises to swap to!

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 09:51:47