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

如何在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()
        }
    }
}

解决方案步骤

  1. 存储用户评分数据:在GuestRateMovieView中添加字典userRatings,用于记录每个电影ID对应的用户评分选择。

  2. 更新评分回调方法:在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])
    }
}
  1. 修改Cell的配置方法:更新MoviesCollectionCell的configure方法,接收评分选择参数,根据选择设置btnRate的图片和透明度。

  2. 传递评分信息到Cell:在cellForItemAt方法中,从userRatings中取出对应电影的评分选择,传递给cell的configure方法。

  3. 处理Cell复用:在prepareForReuse中重置btnRate的图片和透明度,避免复用旧数据。

这样修改后,用户完成评分后对应的cell会自动刷新并显示正确的评分图标。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 12:10:51