如何在UICollectionViewCell选中后显示对应评分图标
问题:UICollectionViewCell完成评分后无法显示对应图标
我有一个从API加载图片的UICollectionViewCell,需求是用户点击cell完成评分后,在该cell上显示对应选择的评分图标。自定义的MoviesCollectionCell包含两个UIImageView:一个用于展示API返回的图片,另一个btnRate用于展示选中后的评分图标(初始透明度为0)。
目前已通过ModalDelegate协议将用户的评分选择信息传回GuestRateMovieView控制器,能正常更新页面标签,但无法在选中的cell上显示对应图标。以下是相关代码:
协议与GuestRateMovieView控制器代码
protocol ModalDelegate { func changeValue(userChoice: String, rateMovieID: String, rateImageUrl: String, title: String) } class GuestRateMovieView: UIViewController, ModalDelegate { func changeValue(userChoice: String, rateMovieID: String, rateImageUrl: String, title: String) { self.userChoice = userChoice totalRated = totalRated + 1 lblRated.text = "\(totalRated) rated" if totalRated > 0 { ratedView.backgroundColor = .ratedGoldColour }else{ ratedView.backgroundColor = .white } if totalRated >= 5 { btnFloatNext.alpha = 1 } if totalRated > 5 { userChoiceMovieImage.sd_setImage(with: URL(string: rateImageUrl), placeholderImage: UIImage(named: "ImagePlaceholder")) lblUserChoice.text = "Great taste. We love the \(title) too." } var rating = 1 if userChoice == "Hate it"{ rating = 1 }else if userChoice == "Good" { rating = 3 }else{ rating = 5 } let guestRatingValues = GuestUserRate(id: rateMovieID, imageUrl: rateImageUrl, userRate: rating) GuestRateMovieView.createUserRating(guestRatingValues) print("Received on movie", totalRated) } func getMovieDetails(){ activityLoader.displayActivityLoader(image: activityLoader, view: activityLoaderView) let urlString = "https://t2fmmm2hfg.execute-api.eu-west-2.amazonaws.com/mobile/media/onboarding-items" let url = URL(string: urlString)! var request = URLRequest(url: url) request.setValue("application/json", forHTTPHeaderField: "Accept") request.addValue("application/json", forHTTPHeaderField: "Content-Type") request.httpMethod = "POST" let postParameters: Dictionary<String, Any> = [ "category": "tv" ] if let postData = (try? JSONSerialization.data(withJSONObject: postParameters, options: JSONSerialization.WritingOptions.prettyPrinted)){ request.httpBody = postData let task = URLSession.shared.dataTask(with: request) { [weak self] data, response, error in guard let data = data, error == nil else { return } do { print("got data") let jsonResult = try JSONDecoder().decode([Responder].self, from: data) DispatchQueue.main.async { self?.movieObj = jsonResult self?.moviesCollectionView.reloadData() self?.activityLoader.removeActivityLoader(image: self!.activityLoader, view: self!.activityLoaderView) } } catch { print(error) } } task.resume() } } var movieObj: [Responder] = [] // 新增:存储每个电影ID对应的评分选择 var userRatings: [String: String] = [:] override func viewDidLoad() { super.viewDidLoad() getMovieDetails() } } // UICollectionView数据源与代理方法 extension GuestRateMovieView: UICollectionViewDataSource, UICollectionViewDelegate { func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return movieObj.count } func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reUseMoviesCellID, for: indexPath) as! MoviesCollectionCell let movie = movieObj[indexPath.row] let ratingChoice = userRatings[movie.id] cell.configure(with: movie.packShot?.thumbnail ?? "", ratingChoice: ratingChoice) return cell } func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { let modalVC = RateSingleMovieView() modalVC.movieID = movieObj[indexPath.row].id modalVC.movieTitle = movieObj[indexPath.row].title modalVC.movieImageURL = movieObj[indexPath.row].packShot?.thumbnail ?? "" modalVC.delegate = self modalVC.modalPresentationStyle = .overCurrentContext modalVC.modalTransitionStyle = .crossDissolve present(modalVC, animated: true, completion: nil) } }
MoviesCollectionCell代码
class MoviesCollectionCell: UICollectionViewCell { private var movieImages = NSCache<NSString, NSData>() weak var textLabel: UILabel! let movieImage: UIImageView = { let image = UIImageView() image.translatesAutoresizingMaskIntoConstraints = false image.clipsToBounds = true image.contentMode = .scaleAspectFill image.layer.cornerRadius = 10 return image }() let btnRate: UIImageView = { let image = UIImageView() image.translatesAutoresizingMaskIntoConstraints = false image.clipsToBounds = true image.contentMode = .scaleAspectFit image.alpha = 0 return image }() override init(frame: CGRect) { super.init(frame: frame) contentView.addSubview(movieImage) movieImage.addSubview(btnRate) NSLayoutConstraint.activate([ movieImage.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 10), movieImage.topAnchor.constraint(equalTo: contentView.topAnchor), movieImage.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -10), movieImage.bottomAnchor.constraint(equalTo: contentView.bottomAnchor), btnRate.centerXAnchor.constraint(equalTo: movieImage.centerXAnchor), btnRate.centerYAnchor.constraint(equalTo: movieImage.centerYAnchor), btnRate.widthAnchor.constraint(equalToConstant: 30), btnRate.heightAnchor.constraint(equalToConstant: 30) ]) btnRate.tintColor = .white btnRate.layer.shadowColor = UIColor.black.cgColor btnRate.layer.shadowOffset = CGSize(width: 1.0, height: 2.0) btnRate.layer.shadowRadius = 2 btnRate.layer.shadowOpacity = 0.8 btnRate.layer.masksToBounds = false } override func prepareForReuse() { super.prepareForReuse() movieImage.image = nil btnRate.image = nil btnRate.alpha = 0 } // 修改configure方法,接收评分选择并设置图标 func configure(with urlString: String, ratingChoice: String?) { movieImage.sd_setImage(with: URL(string: urlString), placeholderImage: UIImage(named: "ImagePlaceholder")) guard let choice = ratingChoice else { btnRate.alpha = 0 return } btnRate.alpha = 1 switch choice { case "Hate it": btnRate.image = UIImage(named: "HateIt") case "Good": btnRate.image = UIImage(named: "goodRate") case "Love it": btnRate.image = UIImage(named: "LoveIt") default: btnRate.alpha = 0 } } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } }
评分模态页面RateSingleMovieView代码
class RateSingleMovieView: UIViewController { var delegate: ModalDelegate? var movieID: String? var movieTitle: String? var movieImageURL: String? var userChoice: String? var totalRated = 0 override func viewDidLoad() { super.viewDidLoad() redBottomView.addGestureRecognizer(createSwipeGestureRecognizer(for: .up)) redBottomView.addGestureRecognizer(createSwipeGestureRecognizer(for: .left)) redBottomView.addGestureRecognizer(createSwipeGestureRecognizer(for: .right)) } private func createSwipeGestureRecognizer(for direction: UISwipeGestureRecognizer.Direction) -> UISwipeGestureRecognizer { let gesture = UISwipeGestureRecognizer(target: self, action: #selector(didSwipe(_:))) gesture.direction = direction return gesture } @objc private func didSwipe(_ sender: UISwipeGestureRecognizer) { switch sender.direction { case .up: showUserRatingSelection(userChoice: "Good") case .left: showUserRatingSelection(userChoice: "Hate it") case .right: showUserRatingSelection(userChoice: "Love it") default: break } } @objc private func removeModal(){ dismiss(animated: true, completion: nil) } private func showUserRatingSelection(userChoice: String){ self.hateItView.alpha = 1 if userChoice == "Hate it"{ userChoiceEmoji.image = UIImage(named: "HateIt") lblRate.text = "Hate it" }else if userChoice == "Good" { userChoiceEmoji.image = UIImage(named: "goodRate") lblRate.text = "Good" }else{ userChoiceEmoji.image = UIImage(named: "LoveIt") lblRate.text = "Love it" } userChoiceEmoji.alpha = 1 DispatchQueue.main.asyncAfter(deadline: .now() + 2) { print("hello ", userChoice) self.delegate?.changeValue(userChoice: userChoice, rateMovieID: self.movieID!, rateImageUrl: self.movieImageURL!, title: self.movieTitle!) self.removeModal() } } }
解决方案步骤
存储用户评分数据:在
GuestRateMovieView中添加字典userRatings,用于记录每个电影ID对应的用户评分选择。更新评分回调方法:在
changeValue方法中,将用户的评分选择存入userRatings,然后找到对应电影的indexPath并刷新cell(比刷新整个collectionView更高效):
func changeValue(userChoice: String, rateMovieID: String, rateImageUrl: String, title: String) { // ... 原有代码保持不变 ... // 存储评分选择 userRatings[rateMovieID] = userChoice // 找到对应电影的indexPath并刷新cell if let index = movieObj.firstIndex(where: { $0.id == rateMovieID }) { let indexPath = IndexPath(item: index, section: 0) moviesCollectionView.reloadItems(at: [indexPath]) } }
修改Cell的配置方法:更新
MoviesCollectionCell的configure方法,接收评分选择参数,根据选择设置btnRate的图片和透明度。传递评分信息到Cell:在
cellForItemAt方法中,从userRatings中取出对应电影的评分选择,传递给cell的configure方法。处理Cell复用:在
prepareForReuse中重置btnRate的图片和透明度,避免复用旧数据。
这样修改后,用户完成评分后对应的cell会自动刷新并显示正确的评分图标。
内容的提问来源于stack exchange,提问作者chevi99
相关产品推荐
相关产品推荐

