<!--
{
  "documentType" : "article",
  "framework" : "AVKit",
  "identifier" : "/documentation/AVKit/customizing-the-tvos-playback-experience",
  "metadataVersion" : "0.1.0",
  "role" : "article",
  "title" : "Customizing the tvOS Playback Experience"
}
-->

# Customizing the tvOS Playback Experience

Adopt the latest features of the redesigned tvOS player user interface to provide a more streamlined way to watch your content.

## Discussion

tvOS 15 introduces an all-new playback experience that helps viewers stay in the moment by providing a more consistent, useful, and interactive user interface. The redesigned UI provides viewers with convenient access to relevant controls and information while always keeping the focus on the content.

![An image that shows a conceptual overview tvOS player user interface. The user interface has a transport bar with a title view on its leading side and action controls on its trailing side, and content tabs below the transport bar. The image also shows a menu that pops up from an action control.](images/com.apple.avkit/media-3842260@2x.png)

It’s simple to provide this playback experience in your app by using [`AVPlayerViewController`](/documentation/AVKit/AVPlayerViewController). Using this class for your player UI provides your app the same features and familiar interactions found in the Apple TV app, including support for voice commands using the Siri Remote and presenting video in Picture in Picture. In tvOS 15, [`AVPlayerViewController`](/documentation/AVKit/AVPlayerViewController) is more configurable and customizable than ever, giving you new ways to tailor your app’s video playback experience.

> Note:
> Existing apps that use ``doc://com.apple.avkit/documentation/AVKit/AVPlayerViewController`` adopt the new styling and features of the redesigned player UI when they link against the tvOS 15 SDK.

### Display Supporting Metadata

The player user interface displays a title view above the transport bar when the current player item contains title and subtitle metadata. When playing live streaming content, the title view may also display a badge to indicate that content state to the viewer.

![An image that shows a title view that presents a title, subtitle, and live badge.](images/com.apple.avkit/media-3822567@2x.png)

The title view retrieves the values it displays from an asset’s <doc://com.apple.documentation/documentation/AVFoundation/AVMetadataIdentifier/commonIdentifierTitle> and <doc://com.apple.documentation/documentation/AVFoundation/AVMetadataIdentifier/iTunesMetadataTrackSubTitle> metadata items, when available. If the media your app plays doesn’t contain embedded metadata, you can add custom metadata by creating instances of <doc://com.apple.documentation/documentation/AVFoundation/AVMetadataItem>. The table below lists the metadata values that the player user interface supports.

|Metadata      |Identifier                                                                                                 |Type                                                         |
|--------------|-----------------------------------------------------------------------------------------------------------|-------------------------------------------------------------|
|Title         |<doc://com.apple.documentation/documentation/AVFoundation/AVMetadataIdentifier/commonIdentifierTitle>      |<doc://com.apple.documentation/documentation/Foundation/Data>|
|Subtitle      |<doc://com.apple.documentation/documentation/AVFoundation/AVMetadataIdentifier/iTunesMetadataTrackSubTitle>|<doc://com.apple.documentation/documentation/Swift/String>   |
|Artwork       |<doc://com.apple.documentation/documentation/AVFoundation/AVMetadataIdentifier/commonIdentifierArtwork>    |<doc://com.apple.documentation/documentation/Swift/String>   |
|Description   |<doc://com.apple.documentation/documentation/AVFoundation/AVMetadataIdentifier/commonIdentifierDescription>|<doc://com.apple.documentation/documentation/Swift/String>   |
|Genre         |<doc://com.apple.documentation/documentation/AVFoundation/AVMetadataIdentifier/quickTimeMetadataGenre>     |<doc://com.apple.documentation/documentation/Swift/String>   |
|Content rating|<doc://com.apple.documentation/documentation/AVFoundation/AVMetadataIdentifier/iTunesMetadataContentRating>|<doc://com.apple.documentation/documentation/Swift/String>   |

In an app that defines a structure that stores simple string-based metadata values, you could map its values to their appropriate metadata identifiers and build an array of metadata items as shown below.

```swift
func createMetadataItems(for metadata: Metadata) -> [AVMetadataItem] {
    let mapping: [AVMetadataIdentifier: Any] = [
        .commonIdentifierTitle: metadata.title,
        .iTunesMetadataTrackSubTitle: metadata.subtitle,
        .commonIdentifierArtwork: UIImage(named: metadata.image)?.pngData() as Any,
        .commonIdentifierDescription: metadata.description,
        .iTunesMetadataContentRating: metadata.rating,
        .quickTimeMetadataGenre: metadata.genre
    ]
    return mapping.compactMap { createMetadataItem(for:$0, value:$1) }
}

private func createMetadataItem(for identifier: AVMetadataIdentifier,
                                value: Any) -> AVMetadataItem {
    let item = AVMutableMetadataItem()
    item.identifier = identifier
    item.value = value as? NSCopying & NSObjectProtocol
    // Specify "und" to indicate an undefined language.
    item.extendedLanguageTag = "und"
    return item.copy() as! AVMetadataItem
}
```

To apply the metadata to the current player item, set the array of metadata items as the value of the player item’s <doc://com.apple.documentation/documentation/AVFoundation/AVPlayerItem/externalMetadata> property.

```swift
let metadata: Metadata = // A structure that contains simple string values.
playerItem.externalMetadata = createMetadataItems(for: metadata)
```

Only the title and subtitle values display in the title view. The player presents the other supported metadata values in its Info tab, which the Display Content Tabs section below describes.

> Note:
> If your app would prefer to always hide the title view, set the value of the player view controller’s ``doc://com.apple.avkit/documentation/AVKit/AVPlayerViewController/transportBarIncludesTitleView`` to `false`.

### Add Custom Transport Bar Items

The redesigned transport bar displays controls along its trailing side, which provides viewers quick access to common actions. The system automatically displays controls to configure common playback settings, like selecting subtitles and enabling Picture in Picture, but apps can also add custom controls to the transport bar.

![An image that shows a horizontal row of icons that the tvOS player transport bar displays. The image shows system-provided and custom transport bar items.](images/com.apple.avkit/media-3822650@2x.png)

You define custom transport bar items by creating instances of <doc://com.apple.documentation/documentation/UIKit/UIAction> or <doc://com.apple.documentation/documentation/UIKit/UIMenu>. For example, to create a simple control that toggles whether the current movie is a favorite, you could create an instance of <doc://com.apple.documentation/documentation/UIKit/UIAction> as shown below.

```swift
// Create ♡ and ♥ images.
let heartImage = UIImage(systemName: "heart")
let heartFillImage = UIImage(systemName: "heart.fill")

// Create an action to add the item to the viewer's favorites.
let stateImage = isFavorited ? heartFillImage : heartImage
let favoriteAction = UIAction(title: "Favorites",
                              image: stateImage) { [weak self] action in
    
    // Add the movie to or remove it from the viewer's favorites list.
    self?.isFavorited.toggle()
    
    // Update the button image to reflect the new state.
    action.image = isFavorite ? heartFillImage : heartImage
}
```

You can also create more advanced arrangements of actions and present them in a menu. The following example creates a custom preferences menu that adds a gear icon to the transport bar. When a user clicks the icon, the system presents a pop-up menu with an option to enable looping playback and an inline submenu to control playback speed.

```swift
// Create ∞ and ⚙ images.
let loopImage = UIImage(systemName: "infinity")
let gearImage = UIImage(systemName: "gearshape")

// Create an action to enable looping playback.
let loopAction = UIAction(title: "Loop", image: loopImage, state: .off) { action in
    action.state = (action.state == .off) ? .on : .off
}

let speedActions = ["Half": 0.5, "Default": 1.0, "Double": 2.0].map { title, value in
    UIAction(title: title, state: self.playbackSpeed == value ? .on : .off) { [weak self] action in
        // Update the current playback speed.
        self?.playbackSpeed = value
        action.state = .on
    }
}

// Create the submenu.
let submenu = UIMenu(title: "Speed",
                     options: [.displayInline, .singleSelection],
                     children: speedActions)

// Create the main menu.
let menu = UIMenu(title: "Preferences", image: gearImage, children: [loopAction, submenu])
```

To have the player view controller present the custom items, set them as the player view controller’s [`transportBarCustomMenuItems`](/documentation/AVKit/AVPlayerViewController/transportBarCustomMenuItems) property value.

```swift
// Set the custom transport bar items.
playerViewController.transportBarCustomMenuItems = [favoriteAction, menu]
```

Setting the custom action and menu adds two new items to the transport bar.

![An image that shows a custom action to add a movie to or remove it from a user’s favorites list. It also shows a custom preferences menu that displays an action to enable looping playback and a list of options to control playback speed.](images/com.apple.avkit/media-3820865@2x.png)

### Display Content Tabs

The tvOS player UI can display one or more content tabs below the transport bar to show supporting information or related content. By default, the player presents an Info tab when an asset contains embedded metadata or when you set external metadata on the player item, as the Display Supporting Metadata section above describes.

![An image that shows the tvOS player user interface’s Info tab.](images/com.apple.avkit/media-3821023@2x.png)

Your app can also define custom tabs by creating subclasses of <doc://com.apple.documentation/documentation/UIKit/UIViewController> to present your supporting content, and set them as the value of the player view controller’s [`customInfoViewControllers`](/documentation/AVKit/AVPlayerViewController/customInfoViewControllers) property. The system uses the custom view controller’s <doc://com.apple.documentation/documentation/UIKit/UIViewController/title> property value as the tab title, so initialize it before setting it on the player view controller.

```swift
// Set custom content tabs on the player UI.
playerViewController.customInfoViewControllers = [
    BonusContentViewController(title: "Bonus"),
    RelatedContentViewController(title: "Related")
]
```

![An image that shows a Related content tab that presents a horizontal row of items.](images/com.apple.avkit/media-3821592@2x.png)

For the player view controller to size your content appropriately, specify a <doc://com.apple.documentation/documentation/UIKit/UIViewController/preferredContentSize> or define appropriate auto layout constraints. The system sizes all view controllers to the height of the tallest content tab, so size your custom view controllers consistently or verify that they lay out as you expect at their runtime height.

### Present Actions in the Info Tab

A player view controller presents an Info tab when playing an asset with embedded or external metadata. The tab’s view displays the metadata details, and it may show up to two <doc://com.apple.documentation/documentation/UIKit/UIAction> controls along its trailing edge, as shown below.

![An image of the Info tab of the player user interface. The Info tab presents two buttons laid out vertically along its trailing edge. Pressing the top button plays the current media from the beginning, and pressing the bottom button toggles whether the media exists in the user’s watch list.](images/com.apple.avkit/media-3872936@2x.png)

You customize the actions the view presents by setting a value for the player view controller’s [`infoViewActions`](/documentation/AVKit/AVPlayerViewController/infoViewActions) property. When playing nonlive content, this property contains a single-element array that presents an action to play the content from the beginning. You may replace the default value (if present), add an additional action, or set this property value to an empty array to display no actions. The example below shows how to add a Watch Later action to the view.

```swift
let glasses = UIImage(systemName: "eyeglasses")
let watchLater = UIAction(title: "Watch Later", image: glasses) { action in
    // Add or remove the item from the user's watch list,
    // and update the action state accordingly.
}
// Append the action to the array.
playerViewController.infoViewActions.append(watchLater)
```

### Present Actions Contextually

You can use the tvOS player UI to present controls contextually, which you display for a specific range of time in the content and then dismiss. A common use for this type of control is a Skip button that displays during the title sequence of a movie or TV show. Clicking the button allows viewers to bypass the introduction and quickly skip to the main content.

![An image of the player user interface with its standard controls hidden. The UI instead displays a contextual Skip button in the bottom-right corner.](images/com.apple.avkit/media-3820222@2x.png)

[`AVPlayerViewController`](/documentation/AVKit/AVPlayerViewController) provides a [`contextualActions`](/documentation/AVKit/AVPlayerViewController/contextualActions) property that you can use to specify one or more actions to present. The player displays them along the bottom-trailing side of the screen. The following code example shows a simple implementation of an action that seeks the player forward to the time of the main content.

```swift
// Define an action to skip the introduction of a media asset.
private lazy var skipAction = UIAction(title: "Skip") { [weak self] _ in
    guard let self = self else { return }
    self.player.seek(to: self.skipToTime)
}
```

When you set a value for the [`contextualActions`](/documentation/AVKit/AVPlayerViewController/contextualActions) property, the player presents the controls immediately. To present them only during a relevant section of the content, observe the player timing by adding a periodic or boundary time observer. The following example defines a periodic time observer that fires every second during normal playback. In each invocation, it evaluates the new time to determine if it falls within the presentation range. If it does, the example sets the skip action as the contextual actions value; otherwise, it clears the value by setting it to an empty array.

```swift
func addTimeObserver() {
    // Observe the player's timing every second.
    let interval = CMTime(value: 1, timescale: 1)
    timeObserver = player.addPeriodicTimeObserver(forInterval: interval,
                                                  queue: .main) { [weak self] time in
        guard let self = self else { return }
        // If the time is within the defined skip range, present the skip actions.
        let actions = self.skipRange.containsTime(time) ? [self.skipAction] : []
        self.playerViewController.contextualActions = actions
    }
}
```

---

Copyright &copy; 2026 Apple Inc. All rights reserved. | [Terms of Use](https://www.apple.com/legal/internet-services/terms/site.html) | [Privacy Policy](https://www.apple.com/privacy/privacy-policy)