本文主要給大家介紹了關于利用swift實現卡片橫向滑動動畫效果的相關資料,分享出來供大家參考學習,下面來一起看看詳細的介紹吧。
根據慣例,首先上效果圖:
那天去面試,面試官突然拿出手機點開了一個app,自個在那點了一會,然后問我 這個效果怎么實現,當時一看可以滑動,肯定用scrollView 或者 collectionView實現,就大概的說了下。今天剛好閑下來,就敲一敲這個效果。
先來分析下這個效果:
卡片是橫向滾動,并且每個卡片的位置都是保持在屏幕中間的,而且 左右相鄰的卡片都露出來一點邊
collectionView 和scrollView都可以實現,在這里,我們用collectionView實現,但是我們平常普通用的collectionView都是正屏滑動的??!而且是平滑,所有我們只能自定義UICollectionViewFlowLayout 流式布局,才可以達到上圖效果.
廢話不多說,直接上代碼:
創建collectionView布局
//創建collectionView布局
func setepUI() {
//CustomLayout是自定義的UICollectionViewFlowLayout
layout = CustomLayout()
layout?.itemSize = CGSize(width: SCREEN_WIDTH-80, height: SCREEN_HEIGHT-64-120)
let rect = CGRect(x: 0, y: 64, width:SCREEN_WIDTH , height: SCREEN_HEIGHT-64)
collectionView = UICollectionView(frame: rect, collectionViewLayout: layout!)
collectionView?.delegate = self
collectionView?.dataSource = self
view.addSubview(collectionView!)
collectionView?.register(CustomViewCell.self, forCellWithReuseIdentifier: "identifier")
collectionView?.backgroundColor = UIColor.red
}
實現代理方法:
我們在extension中實現:
// MARK: -- delegate and datasource
extension ViewController:
UICollectionViewDelegate,
UICollectionViewDataSource,
UICollectionViewDelegateFlowLayout{
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 10
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
//CustomViewCell是自定義的cell
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "identifier", for: indexPath) as! CustomViewCell
cell.backgroundColor = UIColor.orange
cell.lable?.text = "\(indexPath.row)/\(10)"
return cell
}
}
至此,我們可以得到普通的效果,左右滑動,但中間cell不會居中,兩側cell也不會縮放,如下圖:
這個時候就需要在自定義的流式布局 CustomLayout里做點什么了:
初始化方法 prepare , 初始化一些內容:
//重寫prepare方法
//布局之前的準備工作 初始化 這個方法每次layout發生改變就調用一次
override func prepare() {
scrollDirection = UICollectionViewScrollDirection.horizontal
minimumLineSpacing = 20.0
sectionInset = UIEdgeInsets(top: 0, left: 40, bottom: 0, right: 40)
super.prepare()
}
(該方法默認返回false) 返回true frame發生改變就允許重新布局 內部會重新調用prepare 和
layoutAttributesForElementsInRect
override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool {
return true
}
MARK:---用來計算出rect這個范圍內所有cell的UICollectionViewLayoutAttributes 對象,循環遍歷每個attribute對象,修改frame,再將這數組返回給系統
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
//根據當前滾動進行對每個cell進行縮放
//首先獲取 當前rect范圍內的 attributes對象
let array = super.layoutAttributesForElements(in: rect)
private let ScaleFactor:CGFloat = 0.001//縮放因子
//計算縮放比 首先計算出整體中心點的X值 和每個cell的中心點X的值
//用著兩個x值的差值 ,計算出絕對值
//colleciotnView中心點的值
let centerX = (collectionView?.contentOffset.x)! + (collectionView?.bounds.size.width)!/2
//循環遍歷每個attributes對象 對每個對象進行縮放
for attr in array! {
//計算每個對象cell中心點的X值
let cell_centerX = attr.center.x
//計算兩個中心點的便宜(距離)
//距離越大縮放比越小,距離小 縮放比越大,縮放比最大為1,即重合
let distance = abs(cell_centerX-centerX)
let scale:CGFloat = 1/(1+distance*ScaleFactor)
attr.transform3D = CATransform3DMakeScale(1.0, scale, 1.0)
}
return array
}
到目前為止,我們可以得到一個縮放的效果,但是仍然沒有達到我們要的效果,可視區域的cell并沒有居中顯示,而是滑到哪里就到哪里:
如下圖:
所以我們還得重寫一個方法:
func targetContentOffset(forProposedContentOffset proposedContentOffset: CGPoint, withScrollingVelocity velocity: CGPoint) -> CGPoint
需要注意的兩個參數:
- proposedContentOffset :手指滑動視圖最終停止的便宜量,并不是手指離開時的偏移量(congtentOffset)
- velocity:手指滑動的速率
實現該方法:
/// <#Description#>
///
/// - Parameter proposedContentOffset: 當手指滑動的時候 最終的停止的偏移量
/// - Returns: 返回最后停止后的點
override func targetContentOffset(forProposedContentOffset proposedContentOffset: CGPoint, withScrollingVelocity velocity: CGPoint) -> CGPoint {
let visibleX = proposedContentOffset.x
let visibleY = proposedContentOffset.y
let visibleW = collectionView?.bounds.size.width
let visibleH = collectionView?.bounds.size.height
//獲取可視區域
let targetRect = CGRect(x: visibleX, y: visibleY, width: visibleW!, height: visibleH!)
//中心點的值
let centerX = proposedContentOffset.x + (collectionView?.bounds.size.width)!/2
//獲取可視區域內的attributes對象
let attrArr = super.layoutAttributesForElements(in: targetRect)!
//如果第0個屬性距離最小
var min_attr = attrArr[0]
for attributes in attrArr {
if (abs(attributes.center.x-centerX) < abs(min_attr.center.x-centerX)) {
min_attr = attributes
}
}
//計算出距離中心點 最小的那個cell 和整體中心點的偏移
let ofsetX = min_attr.center.x - centerX
return CGPoint(x: proposedContentOffset.x+ofsetX, y: proposedContentOffset.y)
}
至此,整個過程結束,其實很簡單,主要是對這幾個方法理解!
最后代碼下載:
github下載地址:點這里
本地下載地址:http://xiazai.jb51.net/201707/yuanma/ScrollCardDemo(jb51.net).rar
總結
以上就是這篇文章的全部內容,希望本文的內容對大家的學習或者工作能帶來一定的幫助,如果有疑問大家可以留言交流,謝謝大家對html5模板網的支持。