Reputation: 445
tl;dr I'm trying to recreate this in the Twitter app: https://i.imgur.com/173CVyM.mp4
As you can see, Twitter plays a video in the feed (table view cell) and when the video is tapped, the video smoothly transitions into its own view controller, without stopping or buffering the video.
I have the video playing in the feed part done (in the table view cell), but now I'm stuck on how to actually smoothly transition the video into a view controller on tap.
At a high level, what do I need to do here?
My current setup has an AVPlayer
instance as part of each table view cell. Would I pass the AVPlayer
instance to the view controller and continue playing the video from there? What about memory management? I feel like having one AVPlayer
instance for each table view cell is going to cause some issues, but I'm not entirely sure.
As a secondary question, if I wanted to ignore the fancy animation/transition, how could I continue playing the video seamlessly in a view controller when it's tapped in the table view cell?
Upvotes: 12
Views: 552
Reputation: 13788
To make the transition from your video cell to the player controller you can use your AVPlayerLayer
and a custom animation, e.g:
ViewController.swift
// Present player controller
let playerViewController = AVPlayerViewController()
playerViewController.player = player // Your current player instance
playerViewController.transitioningDelegate = self // Custom animation
self.present(playerViewController, animated: true, completion: nil)
extension ViewController: UIViewControllerTransitioningDelegate {
func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return PlayerAnimationController(playerLayer: playerLayer) // Your current player's layer
}
}
PlayerAnimationController.swift
class PlayerAnimationController: NSObject, UIViewControllerAnimatedTransitioning {
let playerLayer: AVPlayerLayer
init(playerLayer: AVPlayerLayer) {
self.playerLayer = playerLayer
}
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return 0.5
}
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
guard let toView = transitionContext.view(forKey: .to) else { return }
let containerView = transitionContext.containerView
containerView.addSubview(toView)
let originalSuperlayer = playerLayer.superlayer
let originalFrame = playerLayer.frame
let frame = playerLayer.convert(playerLayer.bounds, to: nil)
containerView.layer.addSublayer(self.playerLayer)
// Start frame
CATransaction.begin()
CATransaction.setAnimationDuration(0)
CATransaction.setDisableActions(true)
self.playerLayer.frame = frame
CATransaction.commit()
toView.alpha = 0
DispatchQueue.main.async {
let duration = self.transitionDuration(using: transitionContext)
UIView.animateKeyframes(withDuration: duration, delay: 0) {
UIView.addKeyframe(withRelativeStartTime: 0, relativeDuration: 1/2) {
self.playerLayer.frame = containerView.bounds
}
UIView.addKeyframe(withRelativeStartTime: 1/2, relativeDuration: 1/2) {
toView.alpha = 1.0
}
}
completion: { _ in
originalSuperlayer?.addSublayer(self.playerLayer)
self.playerLayer.frame = originalFrame
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
}
}
}
}
The sample uses AVPlayerViewController
but you can use your own one of course.
Upvotes: 3