Реализация действия Like / Unlike внутри CollectionViewCell, данные которого получены из API - PullRequest
0 голосов
/ 07 февраля 2020

У меня есть разбитое на страницы UICollectionView, которое получает свою модель из API, у меня есть изображения и видео внутри ячейки, и кнопка «Мне нравится / не нравится» с изображением, которую необходимо изменить на основе данных API. Я реализовал функцию Like / Unlike, и она корректно работает с изменением изображения на основе понравившегося или не понравившегося, но когда я нажимаю кнопку «Нравится / не нравится», необходимо обновить модель из API, но мне не нужно перезагружать изображение / видео. Мне нужно обновить только состояние кнопки, которое должно нравиться или не нравиться.

Ни один из ответов в StackOverflow не соответствует моим потребностям.

Вот расширение My ViewController для CollectionViewDataSource и Delegate.

// MARK: Extension for UICollectionView functions
extension MediaVC: UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
    func numberOfSections(in collectionView: UICollectionView) -> Int {
        return stories.count
    }

    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        let page = stories[section]
        print(section)

        return page.results.count
    }

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: MediaCollectionViewCell.indetifier, for: indexPath) as? MediaCollectionViewCell else { return UICollectionViewCell() }

        let media = story(at: indexPath)
        self.creator = media?.creator
        cell.story = media
        cell.delegate = self
        cell.indexPath = indexPath

        let tag = indexPath.item
        cell.likeButton.tag = tag
        let defaults = UserDefaults.standard
        let state = defaults.bool(forKey: "isSelected\(tag)")
        print("State - \(state)")
        cell.likeButton.isSelected = state

        return cell
    }

// ...
}

А это функции My CollectionViewCell.

class MediaCollectionViewCell: UICollectionViewCell {

    @IBOutlet weak var mediaView: UIView!
    @IBOutlet weak var loading: UIActivityIndicatorView!
    @IBOutlet weak var usernameBackgroundView: UIView!
    @IBOutlet weak var profileButton: UIButton!
    @IBOutlet weak var profileButtonWithImage: UIButton!
    @IBOutlet weak var nameButton: UIButton!
    @IBOutlet weak var descriptionButton: UIButton!
    @IBOutlet weak var likeBackgroundView: UIVisualEffectView!
    @IBOutlet weak var likeButton: UIButton!
    @IBOutlet weak var likeCount: UILabel!

    lazy var imageView: UIImageView = {
        let imageView = UIImageView()
        imageView.frame = self.mediaView.bounds
        imageView.center = self.mediaView.center
        return imageView
    }()

    lazy var videoView: UIView = {
        let view = UIView()
        view.frame = self.mediaView.frame
        return view
    }()

 var story: Story? {
        didSet {
            configureCell()
            configureMediaImage(mediaForCell: story)
            configureMediaVideo(mediaForCell: story)
            self.creator = story?.creator
        }
    }

    var creator: Creator?

    // MARK: - Override Functions
    private var generation = 0
    override func prepareForReuse() {
        super.prepareForReuse()
        self.nameButton.setTitle("Name Username", for: .normal)
        self.profileButtonWithImage.setImage(UIImage(named: "profileIcon"), for: .normal)
        self.likeButton.setImage(#imageLiteral(resourceName: "unlike"), for: .normal)
        self.likeCount.text = "0"
        self.likesCount = 0
        self.imageView.image = nil
        self.avPlayerController?.player = nil
        generation += 1
    }

    override func didMoveToSuperview() {
        super.didMoveToSuperview()
        configureCell()
        configureMediaImage(mediaForCell: story)
        configureMediaVideo(mediaForCell: story)
    }

    override func layoutSubviews() {
        super.layoutSubviews()
    }

    override func awakeFromNib() {
        super.awakeFromNib()
        self.loading.hidesWhenStopped = true
        self.likeBackgroundView.addShadowAndRadius(10, 0.5, true)
    }

    // MARK: - IBActions
    @IBAction func showUser(_ sender: Any) {
        guard let indexPath = indexPath else { return }
        self.delegate?.showUser(indexPath: indexPath)
    }

    @IBAction func likeMedia(_ sender: Any) {
        guard let indexPath = indexPath else { return }
        print(indexPath)
        guard let sender = sender as? UIButton else { return }
        DispatchQueue.main.async {
            if sender.isSelected == false {
                sender.isSelected = true
                let defaults = UserDefaults.standard
                print("Sender Tag - \(sender.tag)")
                defaults.set(true, forKey: "isSelected\(sender.tag)")

                self.likeButton.setImage(#imageLiteral(resourceName: "unlike"), for: .normal)
                self.likesCount -= 1
                self.likeCount.text = "\(self.likesCount)"
                self.liked = false
            }
            else {
                sender.isSelected = false
                let defaults = UserDefaults.standard
                print("Sender Tag - \(sender.tag)")
                defaults.set(false, forKey: "isSelected\(sender.tag)")

                self.likeButton.setImage(#imageLiteral(resourceName: "like"), for: .normal)
                self.likesCount += 1
                self.likeCount.text = "\(self.likesCount)"
                self.liked = true
            }
        }
        self.delegate?.likeMedia(indexPath: indexPath)
    }

    // MARK: Functions
    func configureMediaImage(mediaForCell: Story?) {
        if mediaForCell?.imageURL != nil {
            DispatchQueue.main.async {
                self.loading.startAnimating()
            }
            if let imageURL = mediaForCell?.imageURL {
                print(imageURL)

                guard let url = URL(string: imageURL) else { return }
                let currentGeneration = generation
                loadImage(from: url) { (image) in
                    guard currentGeneration == self.generation else {
                        return
                    }
                    DispatchQueue.main.async {
                        self.loading.stopAnimating()
                        self.imageView.contentMode = .scaleAspectFill
                        self.imageView.image = image
                        self.mediaView.addSubview(self.imageView)
                        self.mediaView.sendSubviewToBack(self.imageView)
                    }
                }
            }
        }
    }

    func configureMediaVideo(mediaForCell: Story?) {
        if mediaForCell?.videoURL != nil {
            DispatchQueue.main.async {
                self.loading.startAnimating()
            }
            if let videoURL = mediaForCell?.videoURL, let url = URL(string: videoURL) {
                DispatchQueue.main.async {
                    let currentGeneration = self.generation
                    self.addPlayer(url: url) {
                        guard currentGeneration == self.generation else {
                            return
                        }
                        self.playPause()
                    }
                }
            }
        }
    }

    func addPlayer(url: URL, completion: @escaping Action) {
        let avPlayer = AVPlayer.init(playerItem: AVPlayerItem(url: url))
        avPlayer.volume = self.avVolume
        avPlayer.actionAtItemEnd = self.avActionAtItemEnd
        self.avPlayerController = AVPlayerViewController()
        if let avPlayController = self.avPlayerController {
            avPlayController.player = avPlayer
            NotificationCenter.default.addObserver(self, selector: #selector(self.playerItemDidReachEnd(notification:)), name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: avPlayer.currentItem)
            avPlayController.view.frame = self.mediaView.bounds
            avPlayController.videoGravity = self.avResize
            avPlayController.showsPlaybackControls = self.avShowsPlaybackControls
            avPlayController.view.backgroundColor = self.avBackgroundColor
            avPlayController.allowsPictureInPicturePlayback = self.avAllowsPictureInPicturePlayback
            //                    for view in self.mediaView.subviews {
            //                        view.removeFromSuperview()
            //                    }
            self.mediaView.insertSubview(avPlayController.view, at: 0)
            action = completion
            completion()
        }
    }

    func configureCell() {
        if let creator = creator {
            let name = creator.displayName
            self.nameButton.setTitle(name, for: .normal)
            if let description = self.story?.storyDescription {
                self.descriptionButton.setTitle(description, for: .normal)
            }
            let imageView = UIImageView()
            profileButtonWithImage.cornerRadius = profileButtonWithImage.layer.frame.size.width / 2
            profileButtonWithImage.contentMode = .scaleAspectFill
            imageView.frame = profileButtonWithImage.frame
            imageView.cornerRadius = imageView.frame.size.width / 2

            if let liked = story?.liked {
                print(liked)
                if let likes = self.story?.totalLikesCount {
                    self.likeCount.text = "\(likes)"
                    self.likesCount = likes
                }
                if liked == true {
                    self.likeButton.setImage(#imageLiteral(resourceName: "like"), for: .normal)
                    self.liked = true
                } else {
                    self.likeButton.setImage(#imageLiteral(resourceName: "unlike"), for: .normal)
                    self.liked = false
                }
            }

            let currentGeneration = generation
            guard let img = UIImage(named: "profileIcon") else { return }
            if let imageURL = creator.avatarURL {
                guard let url = URL(string: imageURL) else { return }

                loadImage(from: url) { (image) in
                    guard currentGeneration == self.generation else {
                        return
                    }
                    self.profileButtonWithImage.setImage(image, for: .normal)
                }
            } else {
                imageView.image = img
            }
            usernameBackgroundView.addShadowAndRadius(20, 0.4, true)
        }
    }

// ...

}

Я не смог удовлетворить свои потребности, я перепробовал много подходов, это последний, но все работает правильно, кроме изображения и видео также обновлено, что приносит плохой UI / UX.

...