#18 swift animate 進階動畫(一)
9 min readJul 13, 2021
UICollectionView, UIGestureRecognizer, UIPanGestureRecognizer, UIViewPropertAnimator
製造橫向的UICollectionView, 點擊cell會有展開與折疊效果
且加上拖曳功能,能夠隨手勢拖曳控制展開的動畫進度。
UICollectionView及UICollectionViewFlowLayout
由以下網站教學下載
https://www.appcoda.com.tw/interactive-animation-uiviewpropertyanimator/
collectionView的位置、layout設定都先由flowLayout頁面設定
UIViewController雖有匯入UICollectionViewDataSource 但大部分的Cell顯示內容依然由 UICollectionViewCell 進行,包括按鈕、物件、顯示與否、動作、動畫等。
-----------------------
故整篇的重點集中在 UICollectionViewCell 頁面。
共分為4個部分
- enum Cell 顯示狀態
- 基礎變數與常數設置
- 開啟與收合的function及動畫
- 手勢設定
((1
//追蹤被選擇的Cell狀態
private enum State {
case expanded
case collapsed
var change: State {
switch self {
case .expanded: return .collapsed
case .collapsed: return .expanded
}
}
}
若為展開 回傳 收合,若為收合 回傳 展開。
((2
class CityCollectionViewCell: UICollectionViewCell, UIGestureRecognizerDelegate {
//加入手勢 UIGestureRecognizerDelegate
private let cornerRadius: CGFloat = 6 //圓角
private var initialFrame: CGRect? //Cell框大小會改變,故先紀錄原先大小,方便收合
private var state: State = .collapsed //預設cell合起來的
private lazy var animator: UIViewPropertyAnimator = {
return UIViewPropertyAnimator(duration: 0.3, curve: .easeInOut)
}() //設定動畫秒數及形式
static let cellSize = CGSize(width: 250, height: 350)
static let identifier = "CityCollectionViewCell"
@IBOutlet weak var cityTitle: UILabel!
@IBOutlet weak var cityImage: UIImageView!
@IBOutlet weak var descriptionLabel: UILabel!
@IBOutlet weak var closeButton: UIButton!
private var collectionView: UICollectionView?
private var index: Int?
func configure(with city: City, collectionView: UICollectionView, index: Int) {
//()參數對應到UIViewController的DATASource
cityTitle.text = city.name
cityImage.image = UIImage(named: city.image)
descriptionLabel.text = city.description
self.collectionView = collectionView
self.index = index
} //在viewController顯示
//cell裡面的關閉按鈕
@IBAction func close(_ sender: Any) {
toggle()
}
//ViewController動作,改變狀態
func toggle() {
switch state {
case .expanded:
collapse()
case .collapsed:
expand()
}
}
((3以展開為例
//展開
private func expand() {
guard let collectionView = self.collectionView, let index = self.index else { return }
animator.addAnimations {
self.initialFrame = self.frame //initialFrame有值 後續用來折疊
//不透明
self.descriptionLabel.alpha = 1
self.closeButton.alpha = 1
//沒圓角,與邊界貼合(全螢幕)
self.layer.cornerRadius = 0
self.frame = CGRect(x: collectionView.contentOffset.x, y: 0, width: collectionView.frame.width, height: collectionView.frame.height)
}
//contentOffset??
//左邊的cell動畫
if let leftCell = collectionView.cellForItem(at: IndexPath(row: index - 1, section: 0)) {
leftCell.center.x -= 50
}
//右邊的cell動畫
if let rightCell = collectionView.cellForItem(at: IndexPath(row: index + 1, section: 0)) {
rightCell.center.x += 50
}
self.layoutIfNeeded()
//完成後的動作
animator.addCompletion { position in
switch position {
case .end:
self.state = self.state.change
collectionView.isScrollEnabled = false
collectionView.allowsSelection = false
default:
()
}
}
animator.startAnimation()
}
((4
手勢前設置
private let popupOffset: CGFloat = (UIScreen.main.bounds.height - cellSize.height)/2.0
//螢幕高-cellSize高/2
//拖曳手勢
private lazy var panRecognizer: UIPanGestureRecognizer = {
let recognizer = UIPanGestureRecognizer()
recognizer.addTarget(self, action: #selector(popupViewPanned(recognizer:)))
recognizer.delegate = self
return recognizer
}()
//比較垂直速度與水平速度,判斷其手勢為水平移動或垂直移動
override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
return abs((panRecognizer.velocity(in: panRecognizer.view)).y) > abs((panRecognizer.velocity(in: panRecognizer.view)).x)
}
//加入拖曳手勢
override func awakeFromNib() {
self.addGestureRecognizer((panRecognizer))
}
private var animationProgress: CGFloat = 0 //儲存動畫進度
手勢執行
@objc func popupViewPanned(recognizer: UIPanGestureRecognizer) {
switch recognizer.state {
case .began:
toggle()
animator.pauseAnimation()
animationProgress = animator.fractionComplete
case .changed:
let translation = recognizer.translation(in: collectionView)
var fraction = -translation.y / popupOffset
if state == .expanded { fraction *= -1 }
animator.fractionComplete = fraction + animationProgress
//faractionComplata放手後繼續進行動畫
//animationProgress記住拖曳進度
case .ended:
animator.continueAnimation(withTimingParameters: nil, durationFactor: 0)
default:
()
}
}
下週開始到新公司就職,希望能夠順利。