Theming
VideoEditor SDK supports adapting the appearance of the editor UI to the look and feel of your application via theming. Theming is part of configuring the editor, for a detailed explanation of how to configure different editor views, refer to this guide.
Light and Dark Theme#
While a dark color theme is the default, and a light color theme is also supported, in many cases we want the UI to dynamically switch between dark and light as in this example.
Assigning .dynamic
to the global theme property of the Configuration
object switches between the light and dark theme depending on the active UITraitCollection.userInterfaceStyle
.
Colors#
Modifying the properties of the theme
objects allows customizing the color of UI elements.
The use of system colors is recommended since they automatically adapt to the current trait environment.
It's also possible to take advantage of this effect using a custom color by assigning a UIColor
object that sets a color based on the current userInterfaceStyle
.
Fonts#
The theme
also allows changing the fonts that are used in the editor. The SDK uses five major font types:
- The
primaryFont
- The
mediumFont
- The
boldFont
- The
monospacedFont
- The
searchBarFont
The sizes of the fonts are automatically set via the SDK. If you need to update the individual font sizes, you can adjust these via UIAppearanceProxy
calls for each of the components.
Toolbar Titles#
By default, all titles in the toolbar are uppercased. To disable this, you can set the Theme.uppercasedToolbarTitles
to false
.
UI elements#
For more fine-grained control over the UI appearance, we provide an appearanceProxyConfigurationClosure
which is called after the theme was applied via UIAppearance
proxies during the initialization of a CameraViewController
or a MediaEditViewController
subclass.
It is intended to run custom calls to UIAppearance
proxies to customize specific UI components.
Consult the API documentation on when a specific property can be configured via the UIAppearance
proxy API.
The immutable active theme that is passed to this closure can be used to configure the appearance properties of individual UI elements.
Overlay Buttons#
In our example, we are ensuring that overlay buttons match the tint color of the view they are embedded in and are adding some opacity to the button background.
Menu Cells#
Menu cells are another group of UI elements that can be configured in this fashion. Here we are customizing the border width or selected cells and their corner radius.
import UIKitimport VideoEditorSDKclass VideoThemingSwift: Example, VideoEditViewControllerDelegate {override func invokeExample() {// Create a `Video` from a URL to a video in the app bundle.let video = Video(url: Bundle.main.url(forResource: "Skater", withExtension: "mp4")!)// Create a `Configuration` object.let configuration = Configuration { builder in// The recommended way to change the appearance of the UI elements is by configuring the `Theme`.// The default is a dark color theme but there is also a predefined light color theme. Here we// use a theme that switches dynamically between the light and the dark theme based on the active// `UITraitCollection.userInterfaceStyle`.builder.theme = .dynamic// The base colors of the UI elements can be customized at a central place by modifying the properties of the theme.// Use a static color.builder.theme.backgroundColor = .darkGray// Use system colors that automatically adapt to the current trait environment.builder.theme.menuBackgroundColor = .secondarySystemBackgroundbuilder.theme.toolbarBackgroundColor = .tertiarySystemBackgroundbuilder.theme.primaryColor = .label// Define and use a custom color that automatically adapts to the current trait environment.builder.theme.tintColor = UIColor { $0.userInterfaceStyle == .dark ? .green : .red }// Use system or custom fonts.builder.theme.primaryFont = UIFont(name: "Avenir", size: 15)!builder.theme.mediumFont = UIFont(name: "Avenir-Medium", size: 15)!builder.theme.boldFont = UIFont(name: "Avenir-Black", size: 15)!builder.theme.monospacedFont = UIFont(name: "Avenir", size: 15)!builder.theme.searchBarFont = UIFont(name: "Avenir-Medium", size: 15)!// For this example, the toolbar titles are not uppercased.builder.theme.uppercasedToolbarTitles = false// This closure is called after the theme was applied via `UIAppearance` proxies during the initialization of a `CameraViewController` or a `MediaEditViewController` subclass.// It is intended to run custom calls to `UIAppearance` proxies to customize specific UI components. The API documentation highlights when a specific property can be configured// with the `UIAppearance` proxy API.builder.appearanceProxyConfigurationClosure = { theme in// The immutable active theme is passed to this closure and can be used to configure appearance properties.// Change the appearance of all overlay buttons.OverlayButton.appearance(whenContainedInInstancesOf: [MediaEditViewController.self]).tintColor = theme.tintColorOverlayButton.appearance().backgroundColor = UIColor.systemBackground.withAlphaComponent(0.3)// Change the appearance of all menu cells.MenuCollectionViewCell.appearance().selectionBorderWidth = 3MenuCollectionViewCell.appearance().cornerRadius = 5}}// Create and present the video editor. Make this class the delegate of it to handle export and cancelation.let videoEditViewController = VideoEditViewController(videoAsset: video, configuration: configuration)videoEditViewController.delegate = selfvideoEditViewController.modalPresentationStyle = .fullScreenpresentingViewController?.present(videoEditViewController, animated: true, completion: nil)}// MARK: - VideoEditViewControllerDelegatefunc videoEditViewControllerShouldStart(_ videoEditViewController: VideoEditViewController, task: VideoEditorTask) -> Bool {// Implementing this method is optional. You can perform additional validation and interrupt the process by returning `false`.true}func videoEditViewControllerDidFinish(_ videoEditViewController: VideoEditViewController, result: VideoEditorResult) {// The user exported a new video successfully and the newly generated video is located at `result.output.url`. Dismissing the editor.presentingViewController?.dismiss(animated: true, completion: nil)}func videoEditViewControllerDidFail(_ videoEditViewController: VideoEditViewController, error: VideoEditorError) {// There was an error generating the video.print(error.localizedDescription)// Dismissing the editor.presentingViewController?.dismiss(animated: true, completion: nil)}func videoEditViewControllerDidCancel(_ videoEditViewController: VideoEditViewController) {// The user tapped on the cancel button within the editor. Dismissing the editor.presentingViewController?.dismiss(animated: true, completion: nil)}}