У меня есть разбитое на страницы 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.