最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Swift無限循環(huán)控件開發(fā)

 更新時(shí)間:2020年07月28日 09:42:12   作者:海闊任月飛  
這篇文章主要為大家詳細(xì)介紹了Swift無限循環(huán)控件開發(fā),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下

無限循環(huán)控件是一個(gè)常常用到的一個(gè)控件,尤其是一些廣告或者應(yīng)用內(nèi)容公告通知,或者新聞滾動(dòng)的設(shè)計(jì),都是必備的。這種控件網(wǎng)上也有很多,也有很多可以自定義的版本,功能非常強(qiáng)大。 但對(duì)于我們開發(fā)者來說,在具體的應(yīng)用上風(fēng)格和樣式都是比較統(tǒng)一的,一般只需要自己特定的一種風(fēng)格或樣式即可,引入第三方顯然有點(diǎn)大材小用。那么我們?cè)趺茨芎?jiǎn)單而且又快速的造一個(gè)無限循環(huán)的控件呢,只要我們知道無限循環(huán)的原理,那么我們就很自由的按照需求快速的完成。今天我們就講講這個(gè)‘造輪'過程。

首先我們簡(jiǎn)單分析一下無限循環(huán)的原理。一個(gè)控件的自帶滾動(dòng)有UIScrollView、UICollectionView、UITableView。我們就選這個(gè)代表性的控件來講------UICollectionView。他是一個(gè)橫向和縱向都可以高度定制的一個(gè)控件,而且也遵循Cell重用機(jī)制。

第一步,數(shù)據(jù)倍數(shù)增加,一般為3倍,我們只顯示中間那些數(shù)據(jù)即可,我們向左滑動(dòng)的時(shí)候,滑到中間數(shù)據(jù)的最后一條數(shù)據(jù)的時(shí)候繼續(xù)滑動(dòng)的時(shí)候要瞬間換成中間的第一條數(shù)據(jù)。如果向右滑動(dòng)的時(shí)候,如果當(dāng)前是第一條數(shù)據(jù)那么就瞬間移到中間的最后一條數(shù)據(jù)上。這樣看起來就是無限循環(huán)了。一圖勝千言,有圖為證。

滑動(dòng)原理很簡(jiǎn)單,那么我怎么來用代碼實(shí)現(xiàn)呢。下面就使用代碼來實(shí)現(xiàn)這個(gè)控件。

測(cè)試環(huán)境:Xcode版本: Version 11.5 (11E608c)    Mac 系統(tǒng):10.15.4 (19E266)

我們先創(chuàng)建一個(gè)工程命名為:InfiniteLoopDemo,然后我們?cè)趧?chuàng)建一個(gè)InfiniteLoopContentView視圖。代碼如下:

import UIKit
 
protocol InfiniteLoopContentViewDelegate: NSObjectProtocol {
 
 func infiniteLoopView(loopView: InfiniteLoopContentView,index: Int) -> UICollectionViewCell;
 func numberCountOfRows(loopView: InfiniteLoopContentView) -> Int;
 func infiniteLoopView(loopView: InfiniteLoopContentView,didSelectedIndexPath index: Int);
 
 func didEndScrollView(loopView: InfiniteLoopContentView) -> Void
}
 
extension InfiniteLoopContentViewDelegate {
 
 func didEndScrollView(loopView: InfiniteLoopContentView) {
 
 }
}
 
class InfiniteLoopContentView: UICollectionView {
 
 private var perContentSize: CGFloat {
 return contentSize.width / 3;
 }
 
 weak var infiniteDelegate: InfiniteLoopContentViewDelegate!
 
 private var perCount = 0;
 
 private var isAutoScroll = false;
 
 private let runDiration: Double = 3.2;
 
 weak fileprivate var pageControl: UIPageControl!
 
 var beginTimer = true {
 didSet{
  runTimer();
 }
 }
 
 private var width: CGFloat {
 frame.width
 }
 private var height: CGFloat {
 frame.height
 }
 
 private func runTimer() -> Void {
 if beginTimer {
  NSObject.cancelPreviousPerformRequests(withTarget: self);
  perform(#selector(runTimerAction), with: nil, afterDelay: runDiration);
 }else {
  NSObject.cancelPreviousPerformRequests(withTarget: self);
  isAutoScroll = false;
 }
 }
 
 
 
 @objc func runTimerAction() -> Void {
 if perCount <= 1 || contentSize.width < self.width {
  return;
 }
 let offsetx = contentOffset.x;
 guard let indexPath = indexPathForItem(at: .init(x: offsetx + width/2, y: height/2)) else{
  return;
 }
 isAutoScroll = true;
 var next = indexPath.row + 1;
 if next >= (perCount * 3 - 1) {
  next = perCount * 3 - 1;
  
  UIView.animate(withDuration: 0.3, animations: { 
  self.scrollToItem(at: .init(row: next, section: 0), at: .centeredHorizontally, animated: false);
  }) { (finished) in
  self.pageControl?.currentPage = self.perCount - 1;
  self.contentOffset = .init(x: (self.perCount - 1) * Int(self.width), y: 0);
  }
  
 }else{
  scrollToItem(at: .init(row: next, section: 0), at: .centeredHorizontally, animated: true);
  pageControl?.currentPage = next % perCount;
 }
 perform(#selector(runTimerAction), with: nil, afterDelay: runDiration);
 
 }
 
 override init(frame: CGRect, collectionViewLayout layout: UICollectionViewLayout) {
 super.init(frame: frame, collectionViewLayout: layout);
 if let subLayout = layout as? UICollectionViewFlowLayout {
  subLayout.scrollDirection = .horizontal;
  subLayout.minimumLineSpacing = 0;
  subLayout.minimumInteritemSpacing = 0;
  subLayout.itemSize = .init(width: width, height: height);
 }
 showsHorizontalScrollIndicator = false;
 showsVerticalScrollIndicator = false;
 isPagingEnabled = true;
 delegate = self;
 dataSource = self;
 backgroundColor = UIColor.systemBackground;
 
 runTimer();
 }
 
 deinit {
 infiniteDelegate = nil;
 beginTimer = false;
 }
 
 
 required init?(coder: NSCoder) {
 fatalError("init(coder:) has not been implemented")
 }
 
 override func layoutSubviews() {
 super.layoutSubviews();
 
 if perCount <= 1 || isAutoScroll {
  return;
 }
 
 if contentSize.width < self.width {
  return;
 }
 
 
 let contentOffset = self.contentOffset;
 
 if contentOffset.x >= (perContentSize * 2) {
  let offset = contentOffset.x - (perContentSize * 2);
  self.contentOffset = .init(x: perContentSize + offset, y: 0);
 }else if contentOffset.x < perContentSize {
  let offset = Int(contentOffset.x) % Int(perContentSize); 
  self.contentOffset = .init(x: perContentSize + CGFloat(offset), y: 0);
 }
 pageControl?.currentPage = Int((contentOffset.x + width/2) / width) % perCount;
 
 
 }
}
 
extension InfiniteLoopContentView: UICollectionViewDelegateFlowLayout,UICollectionViewDataSource{
 // MARK: - collection view delegate and dataSource
 
 func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
 perCount = infiniteDelegate?.numberCountOfRows(loopView: self) ?? 0
 if perCount == 1 {
  return perCount;
 }
 return perCount * 3;
 }
 
 func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
 return collectionView.bounds.size;
 }
 
 func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
 return infiniteDelegate.infiniteLoopView(loopView: self, index: indexPath.row % perCount);
 }
 
 func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
 infiniteDelegate.infiniteLoopView(loopView: self, didSelectedIndexPath: indexPath.row % perCount);
 }
 
}
 
extension InfiniteLoopContentView {
 func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
 beginTimer = false;
 }
 func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
 beginTimer = true;
 infiniteDelegate?.didEndScrollView(loopView: self);
 
 }
 func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
 if !decelerate {
  scrollViewDidEndDecelerating(scrollView);
 }
 }
 func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) {
 scrollViewDidEndDecelerating(scrollView);
 }
}

這個(gè)是循環(huán)的主要代碼,這里需要注意一下如果只有一條數(shù)據(jù)是禁止循環(huán)的。如果需要一張循環(huán),自己可以實(shí)現(xiàn)以下。

使用的方法和UICollectionView一樣,我們來看具體使用方式:

import UIKit
 
class MainViewController: UIViewController {
 
 
 
 var loopView: InfiniteLoopContentView!
 
 
 override func viewDidLoad() {
 super.viewDidLoad()
 
 // Do any additional setup after loading the view.
 
 
 let layout = UICollectionViewFlowLayout();
 loopView = InfiniteLoopContentView(frame: .init(x: 0, y: 200, width: view.frame.width, height: 200), collectionViewLayout: layout);
 view.addSubview(loopView);
 loopView.infiniteDelegate = self;
 loopView.register(LoopViewCell.self, forCellWithReuseIdentifier: "cell");
 loopView.reloadData();
 }
 
 
 
}
 
extension MainViewController: InfiniteLoopContentViewDelegate{
 func infiniteLoopView(loopView: InfiniteLoopContentView, index: Int) -> UICollectionViewCell {
 let cell = loopView.dequeueReusableCell(withReuseIdentifier: "cell", for: .init(row: index, section: 0)) as! LoopViewCell;
 cell.imageView.image = UIImage(named: (index + 1).description);
 
 return cell;
 }
 
 func numberCountOfRows(loopView: InfiniteLoopContentView) -> Int {
 return 3;
 }
 
 func infiniteLoopView(loopView: InfiniteLoopContentView, didSelectedIndexPath index: Int) {
 
 }
 
 
}
 
 
class LoopViewCell: UICollectionViewCell {
 var imageView: UIImageView!
 override init(frame: CGRect) {
 super.init(frame: frame);
 imageView = UIImageView(frame: bounds);
 imageView.contentMode = .scaleAspectFit;
 addSubview(imageView);
 backgroundColor = UIColor.black
 }
 
 required init?(coder: NSCoder) {
 fatalError("init(coder:) has not been implemented")
 }
}

這是SwiftUI創(chuàng)建的工程,所以我們可以只用使用最新的Canvars來預(yù)覽效果就好。如下:

struct ContentView: View {
 var body: some View {
 
 ViewController()
 }
}
 
struct ContentView_Previews: PreviewProvider {
 static var previews: some View {
 ContentView()
 }
}
 
struct ViewController: UIViewControllerRepresentable {
 func makeUIViewController(context: Context) -> MainViewController {
 MainViewController()
 }
 
 func updateUIViewController(_ uiViewController: MainViewController, context: Context) {
 
 }
 
 typealias UIViewControllerType = MainViewController 
 
 
}

預(yù)覽的效果如下:

最后上傳上Demo,猛戳這里

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • switch實(shí)現(xiàn)一個(gè)兩數(shù)的運(yùn)算代碼示例

    switch實(shí)現(xiàn)一個(gè)兩數(shù)的運(yùn)算代碼示例

    這篇文章主要介紹了switch實(shí)現(xiàn)一個(gè)兩數(shù)的運(yùn)算代碼示例,需要的朋友可以參考下
    2017-06-06
  • Swift能代替Objective-C嗎?

    Swift能代替Objective-C嗎?

    這是我在網(wǎng)上上看到的答案,復(fù)制粘貼過來和大家分享一下,因?yàn)槲液秃芏嗳艘粯雍荜P(guān)心Swift的出現(xiàn)對(duì)Mac開發(fā)的影響和對(duì)Objective-C的影響。
    2014-09-09
  • Swift類和對(duì)象的底層探索分析

    Swift類和對(duì)象的底層探索分析

    這篇文章主要為大家介紹了Swift類和對(duì)象的底層探索分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-09-09
  • swift?cell自定義左滑手勢(shì)處理方法

    swift?cell自定義左滑手勢(shì)處理方法

    這篇文章主要介紹了swift?cell自定義左滑手勢(shì)處理,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-12-12
  • SwiftUI使用Paths和AnimatableData實(shí)現(xiàn)酷炫的顏色切換動(dòng)畫

    SwiftUI使用Paths和AnimatableData實(shí)現(xiàn)酷炫的顏色切換動(dòng)畫

    這篇文章主要介紹了SwiftUI使用Paths和AnimatableData實(shí)現(xiàn)酷炫的顏色切換動(dòng)畫,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧
    2020-05-05
  • Swift 4.2使用self做為變量名淺析

    Swift 4.2使用self做為變量名淺析

    這篇文章主要給大家介紹了關(guān)于Swift 4.2使用self做為變量名的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2018-09-09
  • swift中可選值?和!使用的方法示例

    swift中可選值?和!使用的方法示例

    這篇文章主要給大家介紹了關(guān)于swift中可選值?和!使用的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。
    2017-11-11
  • Swift實(shí)現(xiàn)代碼混淆詳解

    Swift實(shí)現(xiàn)代碼混淆詳解

    本文詳細(xì)講解了Swift實(shí)現(xiàn)代碼混淆的方法,文中通過示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧<BR>
    2021-11-11
  • Swift?列舉內(nèi)存管理與異常處理具體代碼

    Swift?列舉內(nèi)存管理與異常處理具體代碼

    內(nèi)存管理和異常處理在任何編程語(yǔ)言中都是核心概念。盡管有很多教程解釋了Swift自動(dòng)引用計(jì)數(shù)的基本原理,但我發(fā)現(xiàn)沒有一個(gè)可以從編譯器的角度對(duì)其進(jìn)行解釋。在本文中,我們將通過詳細(xì)代碼列舉學(xué)習(xí)Swift內(nèi)存管理與異常處理
    2021-11-11
  • 本地推送通知UserNotifications在Swift中的實(shí)現(xiàn)方式

    本地推送通知UserNotifications在Swift中的實(shí)現(xiàn)方式

    這篇文章主要介紹了本地推送通知UserNotifications在Swift中的實(shí)現(xiàn)方式,想了解消息推送的同學(xué),一定要看一下
    2021-04-04

最新評(píng)論

永济市| 万安县| 北流市| 丁青县| 甘南县| 三都| 独山县| 泰来县| 临湘市| 凤庆县| 新邵县| 竹溪县| 青河县| 吴桥县| 六枝特区| 肥城市| 阳曲县| 习水县| 元谋县| 卫辉市| 搜索| 江永县| 乌拉特前旗| 离岛区| 本溪市| 西和县| 江源县| 金乡县| 文水县| 商城县| 江城| 馆陶县| 明星| 西畴县| 建始县| 潼关县| 汽车| 哈巴河县| 海盐县| 策勒县| 安丘市|