2

As below gif showed, in the specific row of tableView, the playing song's image(Just Dance.mp3) is changed to dynamic image.

My main question is how to achieve this effect in my App, to use a GIF image or other approach? Need advice here.

What effect I want to achieve:

  1. when a song is playing, the specific row cell's image is changed to dynamic image. (the main question)
  2. if the song is paused, then that dynamic image stop to play.
  3. when another song is selected to play, the previous song's(cell) image is resume to its album artwork.

Here is my snip code, I'm not test it yet since I'm not sure if I should use GIF or other approach.

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "CustomCell", for: indexPath) as! CustomCell

        if resultSearchController.isActive {
            cell.addButton.tag = indexPath.row
            cell.songTitle.text = filteredTableData[indexPath.row].songName
            cell.songArtist.text = filteredTableData[indexPath.row].artistName
            cell.songArtwork.image = filteredTableData[indexPath.row].albumArtwork
            return cell
        } else {
            cell.addButton.tag = indexPath.row
            cell.songTitle.text = tableData[indexPath.row].songName
            cell.songArtist.text = tableData[indexPath.row].artistName
            cell.songArtwork.image = tableData[indexPath.row].albumArtwork
            return cell
        }

        // set image of specific tableView row cell to GIF image
        if indexPath.row == SongData.currentTrack {
            let image = UIImage(named: "playing-gif-image")
            cell.songArtwork.image = image
        } else {
            // do nothing
        }
    }

enter image description here

===================================================================

Update my code according to ATV's answer, currently I use static image to set different state of playing cell. Well I get interested to this fancy CAShapeLayer:), and I need time to learn about it then to set the dynamic image for the specific cell.

/ / / Model, SongData.swift

import UIKit

class SongData: NSObject, NSCoding {

    var songName: String
    var artistName: String
    var albumName: String
    var albumArtwork: UIImage
    var url: URL

    static var songList = [SongData]()
    static var shuffleSongList = [SongData]()
    static var currentTrack = 0
    static var showCurrentPlayingSong = false
    static var repeatSequence = "repeatList"
    static var isPlaying = false

    enum PlayingCellState {
        case nonState
        case playing
        case paused
    }

    init(songName: String, artistName: String, albumName: String, albumArtwork: UIImage, url: URL) {
        self.songName = songName
        self.artistName = artistName
        self.albumName = albumName
        self.albumArtwork = albumArtwork
        self.url = url
    } 
...
}

/ / / CustomCell.swift

import UIKit

class CustomCell: UITableViewCell {

    @IBOutlet weak var songTitle: UILabel!
    @IBOutlet weak var songArtist: UILabel!
    @IBOutlet weak var songArtwork: UIImageView!
    @IBOutlet weak var addButton: UIButton!

    override func awakeFromNib() {
        super.awakeFromNib()
        songArtwork.layer.cornerRadius = 8.0
    }

    func config(forState state: SongData.PlayingCellState) {
        // setup your cell depends on state
        switch state {
        case .nonState:
            print("nonState") //update cell to default state
        case .playing:
            songArtwork.image = UIImage(named: "Play")
        case .paused:
            songArtwork.image = UIImage(named: "Pause")
        }
    }
}

/ / / TableViewController

 // use for track cell state, for playing dynamic image usage
    func stateForCell(at indexPath: IndexPath) -> SongData.PlayingCellState {
        // when firstly open the tab song list/app(with no song played), do not attach playing state image
        if SongData.songList.count == 0 {
            return .nonState
        } else {
            if indexPath.row == SongData.currentTrack {
                return SongData.isPlaying ? .playing : .paused
            } else {
                return .nonState
            }
        }
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "CustomCell", for: indexPath) as! CustomCell

        if resultSearchController.isActive {
            cell.addButton.tag = indexPath.row
            cell.songTitle.text = filteredTableData[indexPath.row].songName
            cell.songArtist.text = filteredTableData[indexPath.row].artistName
            cell.songArtwork.image = filteredTableData[indexPath.row].albumArtwork
            // return cell
        } else {
            cell.addButton.tag = indexPath.row
            cell.songTitle.text = tableData[indexPath.row].songName
            cell.songArtist.text = tableData[indexPath.row].artistName
            cell.songArtwork.image = tableData[indexPath.row].albumArtwork
            // return cell
        }

        cell.config(forState: stateForCell(at: indexPath))
        return cell
    }

enter image description here

/// Update, finally I make it worked, to involve lottie-ios library, and import it in CustomCell.swift, implement it in playAnimation(), but the pity thing is that animation repeat mode is not working, the animation just repeat once even I set the loopMode. I will search what is wrong later.

import UIKit
import Lottie

class CustomCell: UITableViewCell {

    @IBOutlet weak var songTitle: UILabel!
    @IBOutlet weak var songArtist: UILabel!
    @IBOutlet weak var songArtwork: UIImageView!
    @IBOutlet weak var view: UIView!
    @IBOutlet weak var addButton: UIButton!

    let animationView = AnimationView()

    override func awakeFromNib() {
        super.awakeFromNib()
        songArtwork.layer.cornerRadius = 8.0
    }

    func playAnimation(){
        let animation = Animation.named("366-equalizer-bounce")
        animationView.animation = animation
        // weird thing is that animation repeat is not working here...
        animationView.loopMode = LottieLoopMode.repeat(3600.0)
        animationView.play()

        animationView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(animationView)

        NSLayoutConstraint.activate([
            animationView.heightAnchor.constraint(equalTo: view.heightAnchor),
            animationView.widthAnchor.constraint(equalTo: view.widthAnchor)
        ])
    }

    func config(forState state: SongData.PlayingCellState) {

        // setup your cell depends on state
        switch state {
        case .nonState:
            print("nonState")
            view.isHidden = true
        case .playing:
            view.isHidden = false
            playAnimation()
        case .paused:
            view.isHidden = false
            // to set this latter 
            // songArtwork.image = UIImage(named: "Pause")
        }
    }
}
Zhou Haibo
  • 1,681
  • 1
  • 12
  • 32
  • 1
    You can add 1 flag in your model like **isCurrentlyPlay** and once user tap on any row to play the song then you just need to make the above flag to `true` and once user again tap the same row then make it `false` vice versa and you can change the image as per this flag. If you plan to implement this then to deselect all data flag follow this link : https://stackoverflow.com/questions/46717778/change-bool-status-for-all-objects-of-all-cells-in-the-tableview – Kuldeep Oct 09 '19 at 11:36
  • @Kuldeep The question is not formulated correct, but it seems like the one part is about "How to implement GIF image?" and another one "How to implement play/pause view updating using `UITableViewCell`?" @ChuckZHB please, correct me if I'm mistaken. Moreover, your suggestion about adding boolean flag **isCurrentlyPlay** is not pretty straightforward, because the state of the Cell should be store outside of the view. – ATV Oct 09 '19 at 15:02
  • @ATV, sorry, I'm on busy yesterday. My main question is how to achieve above screenshot showed effect. Could be GIF or any other way? Need advice. And next one is to pause/play the dynamic image in ```tableView```, but first I need to know is how to implement that effect in my App. – Zhou Haibo Oct 10 '19 at 03:25
  • @ Kuldeep, yes, I have a ```isPlaying``` flag in my Model class. My express in this question might not be that accurate. My main question is how to achieve above screenshot showed effect. I will update my question. – Zhou Haibo Oct 10 '19 at 03:28

1 Answers1

1
  1. For the implementing of:

"Is that a GIF image used or other dynamic image?" - You can choose any of the options below that is more preferable for you:


  1. Changing of the cell's state:

    //e.g. add it to your presenter or wherever you are storing info about `currentTrack`
    ...
    enum PlayingCellState {
        case default
        case playing
        case paused
        ...
    }
    ...
    func stateForCell(at indexPath: IndexPath) -> PlayingCellState {
        if indexPath.row == SongData.currentTrack {
            return isPlaying? .playing : .paused
        } else {
            return .default
        } 
    }
    
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "CustomCell", for: indexPath) as! CustomCell
    
        if resultSearchController.isActive {
            cell.addButton.tag = indexPath.row
            cell.songTitle.text = filteredTableData[indexPath.row].songName
            cell.songArtist.text = filteredTableData[indexPath.row].artistName
            cell.songArtwork.image = filteredTableData[indexPath.row].albumArtwork
            return cell
        } else {
            cell.addButton.tag = indexPath.row
            cell.songTitle.text = tableData[indexPath.row].songName
            cell.songArtist.text = tableData[indexPath.row].artistName
            cell.songArtwork.image = tableData[indexPath.row].albumArtwork
            return cell
        }
    
        cell.config(forState: stateForCell(at: indexPath)
    }
    
    //add to your CustomCell 
    func config(forState state: PlayingCellState) {
    // setup your cell depends on state
    }
    
ATV
  • 338
  • 1
  • 4
  • 19
  • That is what I need! Especially about ```UIBezierPath``` and ```CAShapeLayer```, they are looked beautiful as your example. I update my code accordingly, it works and it is good to put code in CustomCell and Model separately to make reusable easy. – Zhou Haibo Oct 10 '19 at 06:02
  • @ChuckZHB I've updated my answer and added one more library - that one could save a ton of time and take a beautiful result – ATV Oct 10 '19 at 06:07
  • thanks I have installed lottie-ios library in my project and copy some json animate files into it too. Then how should I create the animated ImageView? To create a Swift file to implement the animation or others? – Zhou Haibo Oct 10 '19 at 10:08
  • @ChuckZHB They have an example project in [repo](https://github.com/airbnb/lottie-ios/tree/master/Example) and you can use any of similar [examples](https://medium.com/@volodymyrklymenko/creating-awesome-animations-in-ios-apps-with-lottie-1ce0b7f78543). Talking briefly - you should use `LOTAnimationView` instead of `UIImageView`. Btw if the answer was useful, don`t forget to vote and mark it as complete – ATV Oct 10 '19 at 10:24
  • well, the example is straight forward, but in my case I need to use ```UIImageView``` for most of cell's image, and just one specific cell needs the AnimationView, the question is that how to connect ```cell.songArtwork.image``` with this AnimationView, need some hint. – Zhou Haibo Oct 10 '19 at 11:05
  • @ChuckZHB The most "straight forward" solution is to add both of them (`LOTAnimationView` and UIImageView) on cell and change their visibility depends on `PlayingCellState` – ATV Oct 10 '19 at 11:12
  • I make it worked now except the animation repeat mode is not working now. Well, I will accept your answer, bro. – Zhou Haibo Oct 10 '19 at 12:36
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/200652/discussion-between-atv-and-chuckzhb). – ATV Oct 10 '19 at 13:08