如何在自定义TableViewCell添加收藏按钮并保存至UserDefaults实现收藏列表?
Got it, let's break this down into actionable steps to get your favorite button and filter working properly:
First, add a favorite button to your custom cell (either via Storyboard/XIB or programmatically). Here's the updated cell code with button handling:
import UIKit class ErgWorkoutCell: UITableViewCell { @IBOutlet weak var ergWorkoutTitle: UILabel! @IBOutlet weak var favoriteButton: UIButton! // Closure to pass tap events back to the view controller var favoriteTapped: ((_ isFavorite: Bool) -> Void)? // Track if the current workout is favorited private var isFavorited = false override func awakeFromNib() { super.awakeFromNib() setupFavoriteButton() } private func setupFavoriteButton() { // Set initial button image (adjust the image names to match your assets) favoriteButton.setImage(UIImage(systemName: "star"), for: .normal) favoriteButton.setImage(UIImage(systemName: "star.fill"), for: .selected) } // Call this from the VC to set the button state func configure(isFavorited: Bool) { self.isFavorited = isFavorited favoriteButton.isSelected = isFavorited } @IBAction func favoriteButtonTapped(_ sender: UIButton) { isFavorited.toggle() sender.isSelected = isFavorited favoriteTapped?(isFavorited) } }
Don't forget to connect the favoriteButton outlet and its IBAction in your Storyboard/XIB!
To keep your code clean, create a simple helper to manage saving/retrieving favorite workout IDs. First, make sure your workoutList model has a unique identifier (like id: String or id: Int — using titles can cause conflicts, so a unique ID is better):
// Create a helper struct to handle UserDefaults operations struct FavoriteManager { private static let favoritesKey = "ErgWorkoutFavorites" static func getFavoriteWorkoutIDs() -> Set<String> { return Set(UserDefaults.standard.stringArray(forKey: favoritesKey) ?? []) } static func toggleFavorite(workoutID: String) { var favorites = getFavoriteWorkoutIDs() if favorites.contains(workoutID) { favorites.remove(workoutID) } else { favorites.insert(workoutID) } UserDefaults.standard.set(Array(favorites), forKey: favoritesKey) } static func isWorkoutFavorited(workoutID: String) -> Bool { return getFavoriteWorkoutIDs().contains(workoutID) } }
Now modify your view controller to handle the favorite button taps and filter for favorites:
First, Update cellForRowAt
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let selectedGroup = selectedWorkoutGroup(libraryFilter: libraryFilter, jsonErgWorkouts: jsonErgWorkouts, workoutGroupBox: workoutGroupBox) let cell = tableView.dequeueReusableCell(withIdentifier: "ergWorkoutCell") as! ErgWorkoutCell let workout = selectedGroup[indexPath.row] cell.ergWorkoutTitle.text = workout.title // Configure favorite button state let isFavorited = FavoriteManager.isWorkoutFavorited(workoutID: workout.id) // Replace `workout.id` with your actual unique identifier cell.configure(isFavorited: isFavorited) // Handle favorite button tap cell.favoriteTapped = { [weak self] isFavorited in FavoriteManager.toggleFavorite(workoutID: workout.id) // If we're on the Favorites tab, refresh the table to remove the workout if needed if self?.libraryFilter == 3 { tableView.reloadData() } } return cell }
Then, Fix the selectedWorkoutGroup Method
Update the libraryFilter == 3 case to filter for favorited workouts:
func selectedWorkoutGroup(libraryFilter: Int, jsonErgWorkouts:[workoutList], workoutGroupBox: UITextField) -> [workoutList] { var selectedGroup = [workoutList]() let favoriteIDs = FavoriteManager.getFavoriteWorkoutIDs() switch libraryFilter { case 0: // Segmented Control #1 (Plan) selectedGroup = jsonErgWorkouts.filter { $0.group == workoutGroupBox.text } case 1: // Add your Tag filtering logic here selectedGroup = jsonErgWorkouts.filter { /* Your tag condition */ } case 2: // Add your Duration filtering logic here selectedGroup = jsonErgWorkouts.filter { /* Your duration condition */ } case 3: // Filter for favorited workouts selectedGroup = jsonErgWorkouts.filter { favoriteIDs.contains($0.id) } // Again, use your workout's unique ID default: selectedGroup = jsonErgWorkouts } return selectedGroup }
Make sure your segmented control's value changed event triggers a table reload. Add this action to your segmented control (connect it in Storyboard or add programmatically):
@IBAction func libraryFilterChanged(_ sender: UISegmentedControl) { libraryFilter = sender.selectedSegmentIndex tableView.reloadData() }
Quick Notes:
- Replace
workout.idwith the actual unique identifier from yourworkoutListmodel (if you don't have one, consider adding auuidproperty to each workout in your JSON or model). - Adjust the favorite button images (the SF Symbols "star" and "star.fill" are used here, but you can use custom assets if needed).
- UserDefaults is perfect for small datasets like favorites, but if you expect a large number of favorites, consider using Core Data instead.
内容的提问来源于stack exchange,提问作者app4g

