Create your own Mini Player
In this tutorial, we will explain how to create your own mini player on your iOS project.

Goal

Our goal here is to create a Mini Player like below by yourself, it will be a code base to allow you to set create a custom Mini Player.
We can describe the player as follows :
  • On the top, a progress bar to display the current STTrack progression
  • On the left, the image of the current STTrack
  • The first line is the title of the current STTrack
  • The second line is the title of the current STContent
  • On the right, the main button to control the STPlayer

Set up the UI

To redo the design, we have to first create our custom UIView by adding a XIB file with his implementation:
  • MiniPlayer.xib
  • MiniPlayer.swift
Within the XIB, we have to add those elements:
We should map the IBOutlets with our MiniPlayer.swift file:
MiniPlayer.swift
1
import UIKit
2
3
final class MiniPlayer: UIView {
4
5
@IBOutlet private weak var imageView: UIImageView!
6
@IBOutlet private weak var progressView: UIProgressView!
7
@IBOutlet private weak var titleLabel: UILabel!
8
@IBOutlet private weak var subtitleLabel: UILabel!
9
@IBOutlet private weak var playButton: UIButton!
10
@IBOutlet private weak var pauseButton: UIButton!
11
@IBOutlet private weak var openExpandButton: UIButton!
12
13
}
Copied!
Now we have to map the user interactions with our implementation:
MiniPlayer.swift
1
@IBAction private func didTapPlay(_ sender: Any) {
2
3
}
4
5
@IBAction private func didTapPause(_ sender: Any) {
6
7
}
8
9
@IBAction private func didTapOpenMainPage(_ sender: Any) {
10
11
}
Copied!

Connect to the SDK

Now that we have a complete UIView, we need to connect the elements with the SDK values.

Implement STPlayer feature

Let's start by adding the STPlayer feature within our view by folloing those points:
  • Import the SDK
  • Observing the STPlayer changes
MiniPlayer.swift
1
import UIKit
2
import StayTunedSDK
3
4
final class MiniPlayer: UIView {
5
6
private var player: STPlayer? = STPlayer.shared
7
8
@IBOutlet private weak var imageView: UIImageView!
9
@IBOutlet private weak var progressView: UIProgressView!
10
@IBOutlet private weak var titleLabel: UILabel!
11
@IBOutlet private weak var subtitleLabel: UILabel!
12
@IBOutlet private weak var playButton: UIButton!
13
@IBOutlet private weak var pauseButton: UIButton!
14
@IBOutlet private weak var openExpandButton: UIButton!
15
16
override func awakeFromNib() {
17
self.player?.add(observer: self)
18
}
19
}
20
21
extension MiniPlayer: STPlayerObserver {
22
23
func playerCurrentTrackDidChange(to track: STTrack) {
24
25
}
26
27
func playerCurrentContentDidChange(to content: STContent) {
28
29
}
30
31
func playerCurrentTimeDidChange(to value: Double) {
32
33
}
34
35
func playerStateDidChange(to state: STPlayerState) {
36
37
}
38
}
Copied!

Retrieve all information needed

We are going to set the image of the track, the title and subtitle of the view by observing the changes of a track or a content:
MiniPlayer.swift
1
extension MiniPlayer: STPlayerObserver {
2
3
private func setCurrentMedia() {
4
guard let track = self.player.currentTrack,
5
let content = self.player.currentContent else {
6
return
7
}
8
guard let imageUrl = URL(string: track.imgSrc ?? ""),
9
let imageData = try? Data(contentsOf: imageUrl) else {
10
return
11
}
12
13
self.imageView.image = UIImage(data: imageData)
14
self.titleLabel.text = track.title
15
self.subtitleLabel.text = content.title
16
}
17
18
// The track of the player did change
19
func playerCurrentTrackDidChange(to track: STTrack) {
20
self.setCurrentMedia()
21
}
22
23
// The content of the player did change
24
func playerCurrentContentDidChange(to content: STContent) {
25
self.setCurrentMedia()
26
}
27
}
Copied!
We also need to listen to the playhead of the listening track to populate our progress view:
MiniPlayer.swift
1
extension MiniPlayer: STPlayerObserver {
2
3
func playerCurrentTimeDidChange(to value: Double) {
4
guard let time = self.player.currentTime,
5
let duration = self.player.getAudioDuration() else {
6
return
7
}
8
let progression = Float(time) / Float(duration)
9
self.progressView.setProgress(progression, animated: false)
10
}
11
12
}
Copied!
Now we have to manage our Play and Pause button by observing the state of the player:
MiniPlayer.swift
1
extension MiniPlayer: STPlayerObserver {
2
3
private func setPlayingButton(hidden: Bool) {
4
self.playButton.isHidden = hidden
5
self.pauseButton.isHidden = !hidden
6
}
7
8
func playerStateDidChange(to state: STPlayerState) {
9
switch state {
10
case .playing, loading:
11
self.setPlayingButton(hidden: false)
12
case .paused:
13
self.setPlayingButton(hidden: true)
14
@unknown default:
15
fatalError()
16
}
17
}
18
19
}
Copied!

Send user interactions to the STPlayer

We want to allow our user to press Play or Pause a track or to open the expand if he taps on the Mini Player. To send those interactions we have to implement our IBAction like this:
MiniPlayer.swift
1
@IBAction private func didTapPlay(_ sender: Any) {
2
self.player.resume()
3
}
4
5
@IBAction private func didTapPause(_ sender: Any) {
6
self.player.stop()
7
}
8
9
@IBAction private func didTapOpenMainPage(_ sender: Any) {
10
if let parentController = next as? UIViewController {
11
STExpand.shared?.present(from: parentController)
12
}
13
}
Copied!

Resources

You can retrieve all the codes from our demo app: https://github.com/StayTunedAds/staytuned-ios-sdk
The Mini Player has been implemented by using the Clean Swift (VIP) architecture in the demo app. More information are available here: https://clean-swift.com