Swift實(shí)現(xiàn)3D輪播圖效果
本文實(shí)例為大家分享了Swift實(shí)現(xiàn)3D輪播圖效果的具體代碼,供大家參考,具體內(nèi)容如下
整天逛淘寶,偶爾有一天看到其中的展示頁(yè)有個(gè)看起來(lái)很炫的效果,閑來(lái)無(wú)事就試著寫(xiě)一個(gè)出來(lái),先來(lái)看效果:
簡(jiǎn)單記一下思路,這里我選擇使用UICollectionView控件,先根據(jù)其復(fù)用和滾動(dòng)的特性做出無(wú)限輪播的效果,關(guān)鍵代碼:
//數(shù)據(jù)源數(shù)組 let totalCount = 100 var models: [String] = [String]() { didSet { //判斷元素個(gè)數(shù) if models.count < 2 { collectionView.isScrollEnabled = false } //網(wǎng)上的找來(lái)的一個(gè)辦法添加100組數(shù)據(jù)源對(duì)應(yīng)的索引數(shù)組indexArr for _ in 0..<totalCount { for j in 0..<models.count { indexArr.append(j) } } //開(kāi)始就滾動(dòng)到第50組數(shù)據(jù)源 collectionView.scrollToItem(at: IndexPath(item: totalCount/2 * models.count, section: 0), at: .centeredHorizontally, animated: false) } }
滾動(dòng)停止時(shí)走的方法里做處理:
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) { //找到滾動(dòng)停止的點(diǎn)對(duì)應(yīng)的collectionView的indexPath let point = self.convert(collectionView.center, to: collectionView) let index = collectionView.indexPathForItem(at: point) let indexpath = (index?.row ?? 0) % models.count collectionView.scrollToItem(at: IndexPath(item: totalCount/2 * models.count + indexpath, section: 0), at: .centeredHorizontally, animated: false) }
以上是滾動(dòng)的處理,接下來(lái)就是item的漸變效果處理,看到這種情況就想到要寫(xiě)一個(gè)UICollectionViewFlowLayout
類供collectionView使用,UICollectionViewFlowLayout是管理item怎么展示用的,首先重寫(xiě)展示視圖內(nèi)的layoutAttributes方法,在此方法中找到attribute對(duì)應(yīng)的item及其屬性,和collectionView的偏移量和中心點(diǎn),進(jìn)行一系列的計(jì)算:
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? { //集合視圖的寬高(這里默認(rèn)寬高相等) let itemHeight = self.collectionView?.frame.height ?? 0 //可是視圖內(nèi)的attributes數(shù)組 let array = super.layoutAttributesForElements(in: rect) //item透明度開(kāi)始變化時(shí)的item的中心點(diǎn)x let centerX = self.collectionView!.contentOffset.x + itemHeight/2 print(self.collectionView?.contentOffset.x ?? 0) for attributes in array! { //開(kāi)始變化時(shí)的item的中心點(diǎn)x 與 實(shí)際中心點(diǎn)相比較 let value = attributes.center.x - centerX let delta = abs(value) //設(shè)置縮放比例,此處4*itemHeight可根據(jù)縮放效果進(jìn)行修改 let scale = 1 - delta/(4*itemHeight) //設(shè)置縮放比例 attributes.transform = CGAffineTransform.init(scaleX: scale, y: scale) //層次關(guān)系,設(shè)置此屬性使item依次上下排列 attributes.zIndex = Int(1 - abs(delta)) //value<=0表示向左移動(dòng),最前面的item停止一段距離 if value <= 0{ //實(shí)際中心點(diǎn)與開(kāi)始變化時(shí)的item的中心點(diǎn)小于等于設(shè)定的邊界值 if delta >= 0 && delta <= itemHeight { //item的中心點(diǎn)固定不變 attributes.center.x = centerX //根據(jù)推進(jìn)值,改變item的透明度,此處的delta>10是想讓item有一個(gè)達(dá)到目標(biāo)區(qū)域時(shí)有一個(gè)停頓效果而不是直接進(jìn)入改變透明度的階段 attributes.alpha = (delta > 10) ? (1 - delta/(itemHeight/4)) : 1 //設(shè)置縮放比例,停頓階段不進(jìn)行縮放 attributes.transform = CGAffineTransform.init(scaleX: 1, y: 1) } else { //移動(dòng)大于邊界值,就是從停頓階段到透明度改變,此處是下一個(gè)item上來(lái),當(dāng)前item透明度變?yōu)? attributes.alpha = 0 } } } return array }
解決最后一個(gè)小問(wèn)題,拖動(dòng)iitem開(kāi)始滾動(dòng),滾動(dòng)結(jié)束時(shí)讓它自動(dòng)滾動(dòng)到網(wǎng)格區(qū)域,而不是停在當(dāng)前或許不適當(dāng)?shù)牡胤?,重?xiě)UICollectionViewFlowLayout另一個(gè)方法:
override func targetContentOffset(forProposedContentOffset proposedContentOffset: CGPoint, withScrollingVelocity velocity: CGPoint) -> CGPoint { let targetRect = CGRect(x: proposedContentOffset.x, y: 0.0, width: self.collectionView!.bounds.size.width, height: self.collectionView!.bounds.size.height) //目標(biāo)區(qū)域中包含的cell let attrArray = super.layoutAttributesForElements(in: targetRect) as! [UICollectionViewLayoutAttributes] //collectionView落在屏幕重點(diǎn)的x坐標(biāo) let horizontalCenterX = proposedContentOffset.x + (self.collectionView?.frame.height ?? 0)/2 var offsetAdjustment = CGFloat(MAXFLOAT) for layoutAttributes in attrArray { let itemHorizontalCenterX = layoutAttributes.center.x //找出離中心店最近的 if (abs(itemHorizontalCenterX-horizontalCenterX) < abs(offsetAdjustment)) { offsetAdjustment = itemHorizontalCenterX - horizontalCenterX } } //返回collectionView最終停留的位置 return CGPoint(x: proposedContentOffset.x + offsetAdjustment, y: proposedContentOffset.y) } //當(dāng)collectionView的顯示范圍發(fā)生改變的時(shí)候,是否重新布局 override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool { return true }
最后一個(gè)方法不寫(xiě)看不到漸變的效果。
源碼地址:點(diǎn)擊打開(kāi)鏈接
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- Swift調(diào)用Objective-C編寫(xiě)的API實(shí)例
- swift5.3 UIColor使用十六進(jìn)制顏色的方法實(shí)例
- 詳解Swift 結(jié)構(gòu)體
- Swift 進(jìn)階 —— map 和 flatMap的使用
- Swift 5.1 之類型轉(zhuǎn)換與模式匹配的教程詳解
- 如何使用Swift來(lái)實(shí)現(xiàn)一個(gè)命令行工具的方法
- Swift4使用GCD實(shí)現(xiàn)計(jì)時(shí)器
- Swift實(shí)現(xiàn)倒計(jì)時(shí)5秒功能
- Swift 中如何使用 Option Pattern 改善可選項(xiàng)的 API 設(shè)計(jì)
相關(guān)文章
Swift之UITabBarController 導(dǎo)航控制器的自定義
本文給大家介紹swift導(dǎo)航控制器之UITabBarController,本文通過(guò)代碼實(shí)例給大家講解swift導(dǎo)航控制器,導(dǎo)航控制器類繼承UITabBarController,代碼簡(jiǎn)單易懂,需要的朋友可以參考下2015-10-10在?Swift?中編寫(xiě)Git?Hooks腳本的方法
在本例中,我使用了?commit-msg?鉤子,它能夠在當(dāng)前提交信息生效前修改此信息,鉤子由一個(gè)參數(shù)調(diào)用,該參數(shù)是指向包含用戶輸入的提交消息的文件的路徑,這意味著,為了改變提交消息,我們只需要從文件中讀取、修改其內(nèi)容,然后寫(xiě)回調(diào)用掛鉤的文件2022-06-06Swift使用編解碼庫(kù)Codable的過(guò)程詳解
Codable 是 Swift 引入的全新的編解碼庫(kù),使開(kāi)發(fā)者更方便的解析JSON 或 plist 文件,支持枚舉、結(jié)構(gòu)體和類,這篇文章主要介紹了Swift使用編解碼庫(kù)Codable,需要的朋友可以參考下2023-09-09Swift、Objective-C、Cocoa混合編程設(shè)置指南
這篇文章主要介紹了Swift、Objective-C、Cocoa混合編程設(shè)置指南,需要的朋友可以參考下2014-07-07