SkillAgentSearch skills...

FlipBook

A swift package for recording views

Install / Use

/learn @bgayman/FlipBook
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

FlipBook: View Recording in Swift

FlipBook

A swift package for recording views. Record a view and write to video, gif, or Live Photo. Also, create videos, gifs, and Live Photos from an array of images.

Features

  • Record a view over time
  • Write recording to video
  • Write recording to .gif
  • Compose recording into a Live Photo
  • Create asset (video, .gif, Live Photo) from an array of images

Requirements

  • iOS 10.0
  • tvOS 10.0
  • macOS 10.15
  • Xcode 11
  • Swift 5.1

Installation

Use Xcode's built in integration with Swift Package Manager.

  • Open Xcode
  • Click File -> Swift Packages -> Add Package Dependency
  • In modal that says "Choose Package Repository" paste https://github.com/bgayman/FlipBook.git and press return
  • Select version range you desire (default selection works well)
  • Xcode will add the package to your project
  • In any file where you want to use FlipBook add import FlipBook

Usage

The main object of the package is the FlipBook object. With it, you can record a view, create an asset from an array of images, and save a Live Photo to the users photo library. There are other specific writer objects (FlipBookAssetWriter, FlipBookLivePhotoWriter, and FlipBookGIFWriter) for more control over how assets are generated. But, by and large, FlipBook is the class that you'll use for easy view capture and easy asset creation from images.

Recording a View

Begin by creating an instance of FlipBook and setting the assetType to desired. You'll next start the recording by calling start, passing in the view you wish to record, an optional progress closure that will be called when asset creation progress has been made, and a completion closure that will return the asset when you're done. To stop the recording, call stop() which will trigger the asset creation to begin. For example:

import UIKit
import FlipBook

class ViewController: UIViewController {
    // Hold a refrence to `flipBook` otherwise it will go out of scope
    let flipBook = FlipBook()
    @IBOutlet weak var myAnimatingView: UIView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // Set the assetType we want to create
        flipBook.assetType = .video
    }
    
    override func viewDidAppear(animated: Bool) {
        super.viewDidAppear(animated: animated)
        
        // Start recording when we appear, here we're recording the root view of `ViewController` but could record any arbitary view
        flipBook.startRecording(view) { [weak self] result in
            
            // Switch on result
            switch result {
            case .success(let asset):
                // Switch on the asset that's returned
                switch asset {
                case .video(let url):
                    // Do something with the video
                    
                // We expect a video so do nothing for .livePhoto and .gif
                case .livePhoto, .gif:
                    break
                }
            case .failure(let error):
                // Handle error in recording
                print(error)
            }
        }
        
        // In this example we want to record some animation, so after we start recording we kick off the animation
        animateMyAnimatingView {
            // The animation is done so stop recording
            self.flipBook.stop()
        }
    }
    
    private func animateMyAnimatingView(_ completion: () -> Void) { ... }
}

You can checkout a complete iOS example and macOS example. On macOS, remember to set wantsLayer to true as FlipBook depends on rendering CALayers for snapshotting.

Creating an Asset from Images

Similarly, begin by creating an instance of FlipBook and setting the assetType desired. When creating an asset from Images it is also important to set the preferredFramesPerSecond as this will determine the overall duration of the asset. For best results, it is also important that all of the images you wish to include are the same size. Finally, you call makeAsset passing in the images you want to include, a progress closure, and a completion closure. For example:

import UIKit
import FlipBook

class ViewController: UIViewController {

    // Hold a refrence to `flipBook` otherwise it will go out of scope
    let flipBook = FlipBook()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // Set `assetType` to the asset type you desire
        flipBook.assetType = .video
        
        // Set `preferredFramesPerSecond` to the frame rate of the animation images
        flipBook.preferredFramesPerSecond = 24
        
        // Load the images. More realistically these would likely be images the user created or ones that were stored remotely.
        let images = (1 ... 48).compactMap { UIImage(named: "animationImage\($0)") }
        
        // Make the asset
        flipBook.makeAsset(from: images) { [weak self] (result) in
            switch result {
            case .success(let asset):
                // handle asset
            case .failure(let error):
                // handle error
            }
        }
    }
}

Advanced Usage

FlipBook will work for most view animations and interactions however many CoreAnimation animations and effects will not work with the simple start and stop method described above. However, there is an optional animationComposition closure of type ((CALayer) -> Void)? that will allow you to composite CALayer animations and effects with a FlipBook video using the AVVideoCompositionCoreAnimationTool. For example:

import UIKit
import FlipBook

class ViewController: UIViewController {
    // Hold a refrence to `flipBook` otherwise it will go out of scope
    let flipBook = FlipBook()
    @IBOutlet weak var myBackgroundView: UIView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // Set the assetType we want to create
        flipBook.assetType = .video
    }
    
    override func viewDidAppear(animated: Bool) {
        super.viewDidAppear(animated: animated)
        
        // Get the scale of the screen that we capturing on as we'll want to apply the scale when animating for the composition
        let scale = view.window?.screen.scale ?? 1.0
        
        // Start recording when we appear, here we're recording a view that will act as the background for our layer animation
        flipBook.startRecording(myBackgroundView, compositionAnimation: { layer in
        
            // create a gradient layer
            let gradientLayer = CAGradientLayer()
            gradientLayer.frame = layer.bounds
            gradientLayer.colors = [UIColor.systemRed.cgColor, UIColor.systemBlue.cgColor]
            gradientLayer.locations = [0.0, 1.0]
            gradientLayer.startPoint = CGPoint.zero
            gradientLayer.endPoint = CGPoint(x: 1.0, y: 1.0)
            
            // create a shape layer
            let shapeLayer = CAShapeLayer()
            shapeLayer.frame = layer.bounds
            
            // remember that layer composition is in pixels not points so scale up
            shapeLayer.lineWidth = 10.0 * scale
            shapeLayer.lineCap = .round
            shapeLayer.fillColor = UIColor.clear.cgColor
            shapeLayer.strokeColor = UIColor.black.cgColor
            shapeLayer.path = UIBezierPath(ovalIn: layer.bounds.insetBy(dx: 150 * scale, dy: 150 * scale)).cgPath
            shapeLayer.strokeEnd = 0.0
            gradientLayer.mask = shapeLayer
            
            layer.addSublayer(gradientLayer)
            
            let strokeAnimation = CABasicAnimation(keyPath: "strokeEnd")
            strokeAnimation.fromValue = 0.0
            strokeAnimation.toValue = 1.0
            strokeAnimation.duration = 8.0
            
            // must start the animation at `AVCoreAnimationBeginTimeAtZero`
            strokeAnimation.beginTime = AVCoreAnimationBeginTimeAtZero
            strokeAnimation.isRemovedOnCompletion = false
            strokeAnimation.fillMode = .forwards
            shapeLayer.add(strokeAnimation, forKey: "strokeAnimation")
            
        }, completion: { [weak self] result in
            
            // Switch on result
            switch result {
            case .success(let asset):
                // Switch on the asset that's returned
                switch asset {
                case .video(let url):
                    // Do something with the video
                    
                // We expect a video so do nothing for .livePhoto and .gif
                case .livePhoto, .gif:
                    break
                }
            case .failure(let error):
                // Handle error in recording
                print(error)
            }
        })
        
        // After 9 seconds stop recording. We'll have 8 seconds of animation and 1 second of final state
        DispatchQueue.main.asyncAfter(deadline: .now() + 9.0) {
            self.flipBook.stop()
        }
    }
}

Generating a gif with the code above you should get something like:

Animated gif of gradient layer composition

Where the card view is the background view recorded by FlipBook and the gradient stroke is the layer composited on top of the recording. Remember that AVVideoCompositionCoreAnimationTool has an origin in the lower left, not top left like UIKit.

When to Use

FlipBook is a great way to capture view animations and interactions or to compose a video, gif, or Live Photo from a loose collection of images. It's great for targeting just a portion of the screen or window. And for creating not just videos, but also animated gifs and Live Photos.

However, it is likely not the bes

View on GitHub
GitHub Stars142
CategoryDevelopment
Updated2mo ago
Forks31

Languages

Swift

Security Score

95/100

Audited on Jan 28, 2026

No findings