Files
simvision/KSPlayer-main/Sources/KSPlayer/Video/PlayerTransitionAnimator.swift
Michael Simard 872354b834 Initial commit: SimVision tvOS streaming app
Features:
- VOD library with movie grouping and version detection
- TV show library with season/episode organization
- TMDB integration for trending shows and recently aired episodes
- Recent releases section with TMDB release date sorting
- Watch history tracking with continue watching
- Playlist caching (12-hour TTL) for offline support
- M3U playlist parsing with XStream API support
- Authentication with credential storage

Technical:
- SwiftUI for tvOS
- Actor-based services for thread safety
- Persistent caching for playlists, TMDB data, and watch history
- KSPlayer integration for video playback

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-21 22:12:08 -06:00

72 lines
3.1 KiB
Swift

//
// PlayerTransitionAnimator.swift
// KSPlayer
//
// Created by kintan on 2021/8/20.
//
#if canImport(UIKit)
import UIKit
class PlayerTransitionAnimator: NSObject, UIViewControllerAnimatedTransitioning {
private let isDismiss: Bool
private let containerView: UIView
private let animationView: UIView
private let fromCenter: CGPoint
init(containerView: UIView, animationView: UIView, isDismiss: Bool = false) {
self.containerView = containerView
self.animationView = animationView
self.isDismiss = isDismiss
fromCenter = containerView.superview?.convert(containerView.center, to: nil) ?? .zero
super.init()
}
func transitionDuration(using _: UIViewControllerContextTransitioning?) -> TimeInterval {
0.3
}
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
let animationSuperView = animationView.superview
let animationViewIndex = animationSuperView?.subviews.firstIndex(of: animationView) ?? 0
let initSize = animationView.frame.size
let animationFrameConstraints = animationView.frameConstraints
guard let presentedView = transitionContext.view(forKey: isDismiss ? .from : .to) else {
return
}
if isDismiss {
containerView.layoutIfNeeded()
presentedView.bounds = containerView.bounds
presentedView.removeFromSuperview()
} else {
if let viewController = transitionContext.viewController(forKey: .to) {
presentedView.frame = transitionContext.finalFrame(for: viewController)
}
}
presentedView.layoutIfNeeded()
transitionContext.containerView.addSubview(animationView)
animationView.translatesAutoresizingMaskIntoConstraints = true
guard let transform = transitionContext.viewController(forKey: .from)?.view.transform else {
return
}
animationView.transform = CGAffineTransform(scaleX: initSize.width / animationView.frame.size.width, y: initSize.height / animationView.frame.size.height).concatenating(transform)
let toCenter = transitionContext.containerView.center
let fromCenter = transform == .identity ? fromCenter : fromCenter.reverse
animationView.center = isDismiss ? toCenter : fromCenter
UIView.animate(withDuration: transitionDuration(using: transitionContext), delay: 0, options: .curveEaseInOut) {
self.animationView.transform = .identity
self.animationView.center = self.isDismiss ? fromCenter : toCenter
} completion: { _ in
animationSuperView?.insertSubview(self.animationView, at: animationViewIndex)
if !animationFrameConstraints.isEmpty {
self.animationView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate(animationFrameConstraints)
}
if !self.isDismiss {
transitionContext.containerView.addSubview(presentedView)
}
transitionContext.completeTransition(true)
}
}
}
#endif