iT邦幫忙

2018 iT 邦幫忙鐵人賽
DAY 21
0
Software Development

iOS Swift x Layout x Animation x Transition系列 第 21

Expand Style Transition - 展開式轉場動畫

https://ithelp.ithome.com.tw/upload/images/20180109/20107329c0KufwEowI.png

在畫面上通過 UITableView 展現一系列的花朵,當使用者點某一個 Cell 時,跳轉到另外一個 ViewController 顯示對應的大圖。

Expand Style Transition

Expand Style Transition

  • 點下其中一張圖片的時候會通過 Push 跳轉到 DetailViewController 這時候畫面會上下分離。
  • 從 DetailViewController 做 Pop 回到 HomeViewController 的時候,上下圖會進行合併。

UINavigationControllerDelegate

這次我們要自定義 UINavigationController 的轉場動畫,通過實現 UINavigationControllerDelegate 方法,我們在這裡提交自己的 transition.

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

https://ithelp.ithome.com.tw/upload/images/20180109/20107329LHB4X198Mq.png
建立 SKExpandTransition 繼承於 NSObject, UIViewControllerAnimatedTransitioning 並實 Protocol 規定的兩個方法

// 轉場時間
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval

// 具體的轉場動畫
func animateTransition(using transitionContext: UIViewControllerContextTransitioning)

轉場動畫

首先我們會根據 push 或者 pop 去抓取我們需要的圖片

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 做對應的動畫效果( 展開 / 合併)

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


上一篇
Color Diffusion Transition - 色彩擴散轉場動畫
下一篇
Transitions - UIView Transition (視圖轉場動畫)
系列文
iOS Swift x Layout x Animation x Transition30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

0
陳董粉絲
iT邦新手 5 級 ‧ 2018-01-10 14:21:32

backgroundView 沒手動移除好像會一直疊加

我以為通知轉場動畫結束以後 transition container 的內容會自動被回收掉。

粉絲大人也是通過 debug view Hierarchy 看出來的嗎?~

有沒有解決方案? QQ

一樣在completion裡面移除就OK了吧
sourceView不會疊加我猜應該是因為pop完就nil了

阿...原來是我漏寫了...

我要留言

立即登入留言