Build a custom player UI

Learn how to replace the default FastPix player controls with your own custom UI.

The FastPix iOS Player SDK lets you hide the default controls and build a fully custom video player interface. You can add your own play/pause button, seek bar, gesture-based interactions, and custom layouts while still using the FastPix engine for playback, buffering, analytics, and playlists.

Hide default controls

Disable the SDK’s built-in player UI:

1playerViewController.hideDefaultControls = true

Embed the player in a custom view

Place the FastPix player inside your own UIView so you can overlay custom controls on top:

1func prepareAvPlayerController() {
2 addChild(playerViewController)
3 playerView.addSubview(playerViewController.view)
4 playerViewController.didMove(toParent: self)
5
6 playerViewController.view.translatesAutoresizingMaskIntoConstraints = false
7
8 NSLayoutConstraint.activate([
9 playerViewController.view.topAnchor.constraint(equalTo: playerView.topAnchor),
10 playerViewController.view.bottomAnchor.constraint(equalTo: playerView.bottomAnchor),
11 playerViewController.view.leadingAnchor.constraint(equalTo: playerView.leadingAnchor),
12 playerViewController.view.trailingAnchor.constraint(equalTo: playerView.trailingAnchor)
13 ])
14}

Add a custom play/pause button

Create a play/pause button and connect it to the player. You can either use togglePlayPause() for automatic switching, or manually call play() and pause().

1private func setupPlayPauseButton() {
2 playPauseButton = UIButton(type: .system)
3 playPauseButton.translatesAutoresizingMaskIntoConstraints = false
4 playPauseButton.tintColor = .white
5 playPauseButton.backgroundColor = UIColor.black.withAlphaComponent(0.55)
6 playPauseButton.layer.cornerRadius = 32
7 playPauseButton.clipsToBounds = true
8
9 let icon = UIImage(systemName: "pause.fill")?.withConfiguration(
10 UIImage.SymbolConfiguration(pointSize: 30, weight: .bold)
11 )
12 playPauseButton.setImage(icon, for: .normal)
13
14 playPauseButton.addTarget(self, action: #selector(playPauseTapped), for: .touchUpInside)
15 playerView.addSubview(playPauseButton)
16
17 NSLayoutConstraint.activate([
18 playPauseButton.centerXAnchor.constraint(equalTo: playerView.centerXAnchor),
19 playPauseButton.centerYAnchor.constraint(equalTo: playerView.centerYAnchor),
20 playPauseButton.widthAnchor.constraint(equalToConstant: 64),
21 playPauseButton.heightAnchor.constraint(equalToConstant: 64)
22 ])
23}
24
25@objc private func playPauseTapped() {
26 // Option 1: Toggle automatically (recommended)
27 playerViewController.togglePlayPause()
28
29 // Option 2: Manually control playback
30 // playerViewController.play()
31 // playerViewController.pause()
32}

Auto-update the play/pause button

Observe the player’s timeControlStatus to keep the button icon in sync with the playback state:

1playerStatusObserver = player.observe(\.timeControlStatus, options: [.new, .initial]) {
2 [weak self] player, _ in
3 DispatchQueue.main.async {
4 self?.updatePlayPauseButton(for: player.timeControlStatus)
5 }
6}

Add tap-to-show/hide controls

Use a tap gesture to toggle the visibility of your custom controls:

1@objc private func togglePlayerControls() {
2 let shouldShow = playPauseButton.alpha == 0
3 shouldShow ? showAllControls(animated: true) : hideAllControls(animated: true)
4}

Auto-hide controls after a delay

Automatically hide controls after a period of inactivity:

1private func autoHideControls(after delay: TimeInterval = 3.0) {
2 controlsHideWorkItem?.cancel()
3 let workItem = DispatchWorkItem { [weak self] in
4 self?.hideAllControls(animated: true)
5 }
6 controlsHideWorkItem = workItem
7 DispatchQueue.main.asyncAfter(deadline: .now() + delay, execute: workItem)
8}

What’s next