Skip to navigation

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:

playerViewController.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:

func prepareAvPlayerController() {
addChild(playerViewController)
playerView.addSubview(playerViewController.view)
playerViewController.didMove(toParent: self)
playerViewController.view.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
playerViewController.view.topAnchor.constraint(equalTo: playerView.topAnchor),
playerViewController.view.bottomAnchor.constraint(equalTo: playerView.bottomAnchor),
playerViewController.view.leadingAnchor.constraint(equalTo: playerView.leadingAnchor),
playerViewController.view.trailingAnchor.constraint(equalTo: playerView.trailingAnchor)
])
}

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().

private func setupPlayPauseButton() {
playPauseButton = UIButton(type: .system)
playPauseButton.translatesAutoresizingMaskIntoConstraints = false
playPauseButton.tintColor = .white
playPauseButton.backgroundColor = UIColor.black.withAlphaComponent(0.55)
playPauseButton.layer.cornerRadius = 32
playPauseButton.clipsToBounds = true
let icon = UIImage(systemName: "pause.fill")?.withConfiguration(
UIImage.SymbolConfiguration(pointSize: 30, weight: .bold)
)
playPauseButton.setImage(icon, for: .normal)
playPauseButton.addTarget(self, action: #selector(playPauseTapped), for: .touchUpInside)
playerView.addSubview(playPauseButton)
NSLayoutConstraint.activate([
playPauseButton.centerXAnchor.constraint(equalTo: playerView.centerXAnchor),
playPauseButton.centerYAnchor.constraint(equalTo: playerView.centerYAnchor),
playPauseButton.widthAnchor.constraint(equalToConstant: 64),
playPauseButton.heightAnchor.constraint(equalToConstant: 64)
])
}
@objc private func playPauseTapped() {
// Option 1: Toggle automatically (recommended)
playerViewController.togglePlayPause()
// Option 2: Manually control playback
// playerViewController.play()
// playerViewController.pause()
}

Auto-update the play/pause button

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

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

Add tap-to-show/hide controls

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

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

Auto-hide controls after a delay

Automatically hide controls after a period of inactivity:

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

What’s next