我們可以通過 Core Animation 來對圖片做各種翻轉的操作。
這次想要讓使用者可以通過手勢來翻轉卡片。
FlipCard
當使用者在畫面上滑動的時候,根據使用者手指所在的位置來180度翻轉卡片。
當翻轉超過 90度的時候,會看得見背面那張圖片。
CALayer
在 ViewController 中建立一個繼承於 CALayer 的 cardLayer 並放在 view.layer 上
在 cardLayer 上我們會先放上一張圖片 ( img-card )
後面會提到為什麼要改變 anchorPoint.
1 2 3 4 5 6 7 8 9 10 |
let screen = UIScreen.main.bounds let cardImage = UIImage(named: "img-card")! let cardWidth = screen.width * 0.5 let cardHeight = cardImage.size.height * (cardWidth / cardImage.size.width) cardLayer = CALayer() cardLayer.contents = cardImage.cgImage cardLayer.anchorPoint = CGPoint(x: 1.0, y: 0.5) cardLayer.frame = CGRect(x: 0, y: 0, width: cardWidth, height: cardHeight) cardLayer.position = CGPoint(x: view.frame.midX, y: view.frame.midY / 2) view.layer.addSublayer(cardLayer) |
UIPanGesture
通過 UIPanGesture 來獲取手勢在 view 上拖動的情況
1 2 |
let panGesture = UIPanGestureRecognizer(target: self, action: #selector(panHandler)) view.addGestureRecognizer(panGesture) |
翻卡片的實作
通過 panHandler 拿到 UIPanGestureRecognizer 事件以後,
我們就可以使用 sender.location(in:view) 方法知道使用者在 view 上當前的座標。
架設使用者滑動到(點到)畫面的最中央時,卡牌就要正好 90度垂直於畫面,也就是手指到最右邊的時候卡片 180度翻開。
而為了讓圖片有翻動的效果:
- 通過 CATransform3DIdentity m34 來實現透視。
- 通過 CATransation 來實現翻轉
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 |
@objc private func panHandler(_ sender: UIPanGestureRecognizer) { let screen = UIScreen.main.bounds let point = sender.location(in: view) // init perspective transform var perspectiveTransform = CATransform3DIdentity perspectiveTransform.m34 = -1.0 / 2000.0 // roate - 將平面滑動的距離轉換成弧度 let rate:CGFloat = 180 / screen.width let angle = point.x * rate * CGFloat(Double.pi) / 180.0 perspectiveTransform = CATransform3DRotate(perspectiveTransform, angle, 0, 1, 0) CATransaction.setDisableActions(true) cardLayer.transform = perspectiveTransform // change image when roate over half cardLayer.contents = UIImage(named: point.x >= screen.width / 2.0 ? "img-tree" : "img-card")?.cgImage // when pan ended if sender.state == .ended { // init perspectiveTransform perspectiveTransform = CATransform3DIdentity perspectiveTransform.m34 = -1.0 / 2000.0 // rotate let x:CGFloat = point.x >= (screen.width / 2) ? 180 : 0 let angle = x * CGFloat(Double.pi) / 180.0 perspectiveTransform = CATransform3DRotate(perspectiveTransform, angle , 0, 1, 0) CATransaction.setDisableActions(false) cardLayer.transform = perspectiveTransform } } |
CATransform3D & m34 透視
m34 默認值是 0, 我們可以通過設置 m34 為 1.0 / d 來實現透視效果,d 代表視角相機和屏幕之間的距離。
- 左圖:未使用透視
- 右圖:使用透視
CATransform3DRotate
旋轉圖片的方法,通過參數就能夠同時對 x,y,z 軸旋轉指定的度數,還能做形狀變化(比如透視)
1 |
CATransform3DRotate(_ t: CATransform3D, _ angle: CGFloat, _ x: CGFloat, _ y: CGFloat, _ z: CGFloat) -> CATransform3D |
錨點 (AnchorPoint)
不論是 View 的 Center 又或者 Layer 的 Position 都指定了 AnchorPoint 相對於父 Layer 的位置,Layer 的 AnchorPoint 通過 Position 來控制它的 frame 位置。
AnchorPoint 默認是在 Layer 的正中間 (0.5, 0.5) 所以 Layer 會以這裏為中心,Layer 的左上角為 (0,0) 右下角為(1,1)
通過圖片來解釋看看
- 有個 View 它的 frame 是 (50, 50, 100, 100)
- 它的 AnchorPoint 默認是 (0.5, 0.5)
- 我們把它的 AnchorPoint 移動到 (1.0, 1.0)
看到結果顯示,position 並沒有改變,但是 frame 卻移動了位置。
改變 Anchor Point 前後的翻拍情況
- 左圖:當我們尚未移動錨點的時候
- 右圖:將錨點移動到 CGPoint(x: 1.0, y: 0.5) 的位置後。
右圖中,由於圖像的 Anchor Point 移動到了原本圖片右邊的中間 (1.0,0.5),所以能看到完整的翻卡的效果了。
參考
- 推薦 – iOS 核心動畫高級技巧
- 可以到 Github 上看對應的 Source Code
AnchorPoint那張圖 好像有點怪怪的
左邊那張frame如果是(50,50,100,100) position應該是(100,100)
右邊那張如果維持position(100,100), anchor point (1,1) frame應該是(0,0,100,100)
就我的觀察啦~
粉絲大人你說的沒錯,我也發現這張圖上的數字有寫錯的地方….這就馬上改 QQ
我來重新畫一下圖 Orz….