在畫面上通過 UITableView 展現一系列的花朵,當使用者點某一個 Cell 時,跳轉到另外一個 ViewController 顯示對應的大圖。
這裡通過 UINavigationController 做 Push/Pop 來跳轉 ViewController 但在這切換的過程,會使用自己定義的動畫。
Expand Style Transition
- 點下其中一張圖片的時候會通過 Push 跳轉到 DetailViewController 這時候畫面會上下分離。
- 從 DetailViewController 做 Pop 回到 HomeViewController 的時候,上下圖會進行合併。
UINavigationControllerDelegate
這次我們要自定義 UINavigationController 的轉場動畫,通過實現 UINavigationControllerDelegate 方法,我們在這裡提交自己的 transition.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
extension HomeViewController: UINavigationControllerDelegate { func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationControllerOperation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? { if operation == UINavigationControllerOperation.push { transition.operation = UINavigationControllerOperation.push transition.duration = 3 transition.selectedFrame = self.selectedCellFrame return transition } if operation == UINavigationControllerOperation.pop { transition.operation = UINavigationControllerOperation.pop transition.duration = 3 return transition } return nil } } |
SKExpandTransition
建立 SKExpandTransition 繼承於 NSObject, UIViewControllerAnimatedTransitioning 並實 Protocol 規定的兩個方法
1 2 3 4 5 |
// 轉場時間 func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval // 具體的轉場動畫 func animateTransition(using transitionContext: UIViewControllerContextTransitioning) |
轉場動畫
首先我們會根據 push 或者 pop 去抓取我們需要的圖片
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
var snapShot = UIImage() let bounds = CGRect(x: 0, y: 0, width: (sourceView?.bounds.size.width)!, height: (sourceView?.bounds.size.height)!) if operation == .push { // 抓一張SourceView的圖(HomeViewController) UIGraphicsBeginImageContextWithOptions((sourceView?.bounds.size)!, true, 1) sourceView?.drawHierarchy(in: bounds, afterScreenUpdates: false) snapShot = UIGraphicsGetImageFromCurrentImageContext()! UIGraphicsEndImageContext() // SnapShot的圖 let tempImageRef = snapShot.cgImage! // selectedFrame是使用者所點選cell的屬性frame let topHeight = selectedFrame.origin.y let imageViewTopFrame = CGRect(x: 0, y: 0, width: bounds.width, height: topHeight) let imageViewBottomFrame = CGRect(x: 0, y: topHeight, width: bounds.width, height: bounds.height - selectedFrame.origin.y) let topImageRef = tempImageRef.cropping(to: imageViewTopFrame) let bottomImageRef = tempImageRef.cropping(to: imageViewBottomFrame) // 上半部的圖片 if topImageRef != nil { imageViewTop = UIImageView(image: UIImage(cgImage: topImageRef!, scale: snapShot.scale, orientation: UIImageOrientation.up)) imageViewTop?.frame = imageViewTopFrame } if (bottomImageRef != nil) { // 下半部的圖片 imageViewBottom = UIImageView(image: UIImage(cgImage: bottomImageRef!, scale: snapShot.scale, orientation: UIImageOrientation.up)) imageViewBottom!.frame = imageViewBottomFrame } } |
然後根據 Push 或 Pop 做對應的動畫效果( 展開 / 合併)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
destinationView?.alpha = 0 sourceView?.alpha = 0 let backgroundView = UIView(frame: bounds) backgroundView.backgroundColor = UIColor.black // animation if self.operation == .push { container.addSubview(backgroundView) container.addSubview(destinationView!) container.addSubview(self.imageViewTop!) container.addSubview(self.imageViewBottom!) UIView.animate(withDuration: transitionDuration(using: transitionContext), animations: { () -> Void in self.imageViewTop!.frame = CGRect(x: 0, y: -self.imageViewTop!.frame.height, width: self.imageViewTop!.frame.width, height: self.imageViewTop!.frame.height) self.imageViewBottom!.frame = CGRect(x: 0, y: bounds.height, width: self.imageViewBottom!.frame.width, height: self.imageViewBottom!.frame.height) destinationView?.alpha = 1 }, completion: { (finish) -> Void in self.imageViewTop?.removeFromSuperview() self.imageViewBottom?.removeFromSuperview() transitionContext.completeTransition(true) }) } else { sourceView?.alpha = 1 container.addSubview(backgroundView) container.addSubview(sourceView!) container.addSubview(destinationView!) container.addSubview(imageViewTop!) container.addSubview(imageViewBottom!) UIView.animate(withDuration: transitionDuration(using: transitionContext), animations: { () -> Void in self.imageViewTop!.frame = CGRect(x: 0, y: 0, width: self.imageViewTop!.frame.width, height: self.imageViewTop!.frame.height) self.imageViewBottom!.frame = CGRect(x: 0, y: bounds.height - self.imageViewBottom!.frame.height, width: self.imageViewBottom!.frame.width, height: self.imageViewBottom!.frame.height) sourceView?.alpha = 0 }, completion: { (finish) -> Void in self.imageViewTop?.removeFromSuperview() self.imageViewBottom?.removeFromSuperview() destinationView?.alpha = 1 transitionContext.completeTransition(true) }) } |
Reference
- 在 Github 上可以看到本文相關的 Source Code