PresentSectionViewController 动画
2023-09-03 本文已影响0人
_浅墨_
动画效果:
import UIKit
class PresentSectionViewController : NSObject, UIViewControllerAnimatedTransitioning {
var cellFrame : CGRect!
var cellTransform : CATransform3D!
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return 5
}
/**
transitionDuration 方法返回转换动画的持续时间(秒)。在这种情况下,它返回 5 秒。
animateTransition 方法负责动画化两个视图控制器之间的转换。它首先从转换上下文中检索目标视图控制器和容器视图。然后将目标视图添加到容器视图中,并设置动画的初始状态。
接下来,它创建一个将平移和单元格变换组合在一起的变换。它将此变换应用于目标视图的层,并将其 zPosition 设置为 999。然后更新容器视图的布局。
之后,它为目标视图的滚动视图设置了一些视觉效果,并使用平移和缩放变换对其进行了动画处理。
最后,它创建了一个 UIViewPropertyAnimator 对象,用于动画化转换的最终状态。此动画器取消了某些约束条件、重置了某些变换并对其他变换进行了动画处理,以创建平滑的转换效果。
*/
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
let destination = transitionContext.viewController(forKey: .to) as! SectionViewController
let containerView = transitionContext.containerView
containerView.addSubview(destination.view)
// Initial state
let widthConstraint = destination.scrollView.widthAnchor.constraint(equalToConstant: 304)
let heightConstraint = destination.scrollView.heightAnchor.constraint(equalToConstant: 248)
let bottomConstraint = destination.scrollView.bottomAnchor.constraint(equalTo: destination.coverView.bottomAnchor)
NSLayoutConstraint.activate([widthConstraint, heightConstraint, bottomConstraint])
let translate = CATransform3DMakeTranslation(cellFrame.origin.x, cellFrame.origin.y, 0.0)
let tranform = CATransform3DConcat(translate, cellTransform)
destination.view.layer.transform = tranform
destination.view.layer.zPosition = 999
containerView.layoutIfNeeded()
destination.scrollView.layer.cornerRadius = 14
destination.scrollView.layer.shadowOpacity = 0.25
destination.scrollView.layer.shadowOffset.height = 10
destination.scrollView.layer.shadowRadius = 20
let moveUpTransform = CGAffineTransform(translationX: 0, y: -100)
let scaleUpTranform = CGAffineTransform(scaleX: 2, y: 2)
let removeFromViewTransform = moveUpTransform.concatenating(scaleUpTranform)
destination.closeVisualEffectView.alpha = 0
destination.closeVisualEffectView.transform = removeFromViewTransform
destination.subheadVisualEffectView.alpha = 0
destination.subheadVisualEffectView.transform = removeFromViewTransform
let animator = UIViewPropertyAnimator(duration: 5, dampingRatio: 0.7) {
// Final state
NSLayoutConstraint.deactivate([widthConstraint, heightConstraint, bottomConstraint])
destination.view.layer.transform = CATransform3DIdentity
destination.view.layoutIfNeeded()
destination.scrollView.layer.cornerRadius = 0
destination.closeVisualEffectView.alpha = 1
destination.closeVisualEffectView.transform = .identity
destination.subheadVisualEffectView.alpha = 1
destination.subheadVisualEffectView.transform = .identity
let scaleTranform = CGAffineTransform(scaleX: 1.2, y: 1.2)
let moveTransform = CGAffineTransform(translationX: 30, y: 10)
let titleTranform = scaleTranform.concatenating(moveTransform)
destination.titleLabel.transform = titleTranform
}
animator.addCompletion { (finished) in
// Completion
transitionContext.completeTransition(true)
}
animator.startAnimation()
}
}
调用方法:
import UIKit
import AVKit
class HomeViewController: UIViewController {
@IBOutlet weak var titleLabel: UILabel!
@IBOutlet weak var deviceImageView: UIImageView!
@IBOutlet weak var playVisualEffectView: UIVisualEffectView!
@IBOutlet weak var backgroundImageView: UIImageView!
@IBOutlet weak var scrollView: UIScrollView!
@IBOutlet weak var heroView: UIView!
@IBOutlet weak var bookView: UIView!
@IBOutlet weak var chapter1CollectionView: UICollectionView!
var isStatusBarHidden = false
let presentSectionViewController = PresentSectionViewController()
@IBAction func playButtonTapped(_ sender: Any) {
let urlString = "https://player.vimeo.com/external/235468301.hd.mp4?s=e852004d6a46ce569fcf6ef02a7d291ea581358e&profile_id=175"
let url = URL(string: urlString)
let player = AVPlayer(url: url!)
let playerController = AVPlayerViewController()
playerController.player = player
present(playerController, animated: true) {
player.play()
}
}
override func viewDidLoad() {
super.viewDidLoad()
navigationController?.setNavigationBarHidden(true, animated: false)
scrollView.delegate = self
chapter1CollectionView.delegate = self
chapter1CollectionView.dataSource = self
titleLabel.alpha = 0
deviceImageView.alpha = 0
playVisualEffectView.alpha = 0
UIView.animate(withDuration: 1) {
self.titleLabel.alpha = 1
self.deviceImageView.alpha = 1
self.playVisualEffectView.alpha = 1
}
// setStatusBarBackgroundColor(color: #colorLiteral(red: 0, green: 0, blue: 0, alpha: 0.5))
}
func setStatusBarBackgroundColor(color: UIColor) {
guard let statusBar = UIApplication.shared.value(forKeyPath: "statusBarWindow.statusBar") as? UIView else { return }
statusBar.backgroundColor = color
}
override var preferredStatusBarStyle: UIStatusBarStyle {
return .lightContent
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "HomeToSection" {
let destination = segue.destination as! SectionViewController
let indexPath = sender as! IndexPath
let section = sections[indexPath.row]
destination.section = section
destination.sections = sections
destination.indexPath = indexPath
destination.transitioningDelegate = self
let attributes = chapter1CollectionView.layoutAttributesForItem(at: indexPath)!
let cellFrame = chapter1CollectionView.convert(attributes.frame, to: view)
presentSectionViewController.cellFrame = cellFrame
presentSectionViewController.cellTransform = animateCell(cellFrame: cellFrame)
isStatusBarHidden = true
UIView.animate(withDuration: 0.5, animations: {
self.setNeedsStatusBarAppearanceUpdate()
})
}
}
override func viewWillAppear(_ animated: Bool) {
super.viewDidAppear(true)
isStatusBarHidden = false
UIView.animate(withDuration: 0.5) {
self.setNeedsStatusBarAppearanceUpdate()
}
}
override var prefersStatusBarHidden: Bool {
return isStatusBarHidden
}
override var preferredStatusBarUpdateAnimation: UIStatusBarAnimation {
return .slide
}
}
extension HomeViewController: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return sections.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "sectionCell", for: indexPath) as! SectionCollectionViewCell
let section = sections[indexPath.row]
cell.titleLabel.text = section["title"]
cell.captionLabel.text = section["caption"]
cell.coverImageView.image = UIImage(named: section["image"]!)
cell.layer.transform = animateCell(cellFrame: cell.frame)
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
performSegue(withIdentifier: "HomeToSection", sender: indexPath)
}
}
extension HomeViewController : UIViewControllerTransitioningDelegate {
func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return presentSectionViewController
}
}
extension HomeViewController: UIScrollViewDelegate {
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let offsetY = scrollView.contentOffset.y
if offsetY < 0 {
heroView.transform = CGAffineTransform(translationX: 0, y: offsetY)
playVisualEffectView.transform = CGAffineTransform(translationX: 0, y: -offsetY/3)
titleLabel.transform = CGAffineTransform(translationX: 0, y: -offsetY/3)
deviceImageView.transform = CGAffineTransform(translationX: 0, y: -offsetY/4)
backgroundImageView.transform = CGAffineTransform(translationX: 0, y: -offsetY/5)
}
if let collectionView = scrollView as? UICollectionView {
for cell in collectionView.visibleCells as! [SectionCollectionViewCell] {
let indexPath = collectionView.indexPath(for: cell)!
let attributes = collectionView.layoutAttributesForItem(at: indexPath)!
let cellFrame = collectionView.convert(attributes.frame, to: view)
let translationX = cellFrame.origin.x / 5
cell.coverImageView.transform = CGAffineTransform(translationX: translationX, y: 0)
cell.layer.transform = animateCell(cellFrame: cellFrame)
}
}
let navigationIsHidden = offsetY <= 0
navigationController?.setNavigationBarHidden(navigationIsHidden, animated: true)
}
func animateCell(cellFrame: CGRect) -> CATransform3D {
let angleFromX = Double((-cellFrame.origin.x) / 10)
let angle = CGFloat((angleFromX * Double.pi) / 180.0)
var transform = CATransform3DIdentity
transform.m34 = -1.0/1000
let rotation = CATransform3DRotate(transform, angle, 0, 1, 0)
var scaleFromX = (1000 - (cellFrame.origin.x - 200)) / 1000
let scaleMax: CGFloat = 1.0
let scaleMin: CGFloat = 0.6
if scaleFromX > scaleMax {
scaleFromX = scaleMax
}
if scaleFromX < scaleMin {
scaleFromX = scaleMin
}
let scale = CATransform3DScale(CATransform3DIdentity, scaleFromX, scaleFromX, 1)
return CATransform3DConcat(rotation, scale)
}
}