├── .gitignore ├── LICENSE ├── README.md ├── WRCycleScrollView ├── WRCycleCell.swift ├── WRCycleScrollView.swift ├── WRPageControl.swift └── WRProxy.swift ├── WRCycleScrollViewDemo ├── Podfile ├── Podfile.lock ├── Pods │ ├── Kingfisher │ │ ├── LICENSE │ │ ├── README.md │ │ └── Sources │ │ │ ├── AnimatedImageView.swift │ │ │ ├── Box.swift │ │ │ ├── CacheSerializer.swift │ │ │ ├── Filter.swift │ │ │ ├── Image.swift │ │ │ ├── ImageCache.swift │ │ │ ├── ImageDownloader.swift │ │ │ ├── ImagePrefetcher.swift │ │ │ ├── ImageProcessor.swift │ │ │ ├── ImageTransition.swift │ │ │ ├── ImageView+Kingfisher.swift │ │ │ ├── Indicator.swift │ │ │ ├── Kingfisher.h │ │ │ ├── Kingfisher.swift │ │ │ ├── KingfisherManager.swift │ │ │ ├── KingfisherOptionsInfo.swift │ │ │ ├── RequestModifier.swift │ │ │ ├── Resource.swift │ │ │ ├── String+MD5.swift │ │ │ ├── ThreadHelper.swift │ │ │ └── UIButton+Kingfisher.swift │ ├── Manifest.lock │ ├── Pods.xcodeproj │ │ └── project.pbxproj │ └── Target Support Files │ │ ├── Kingfisher │ │ ├── Info.plist │ │ ├── Kingfisher-dummy.m │ │ ├── Kingfisher-prefix.pch │ │ ├── Kingfisher-umbrella.h │ │ ├── Kingfisher.modulemap │ │ └── Kingfisher.xcconfig │ │ └── Pods-WRCycleScrollViewDemo │ │ ├── Info.plist │ │ ├── Pods-WRCycleScrollViewDemo-acknowledgements.markdown │ │ ├── Pods-WRCycleScrollViewDemo-acknowledgements.plist │ │ ├── Pods-WRCycleScrollViewDemo-dummy.m │ │ ├── Pods-WRCycleScrollViewDemo-frameworks.sh │ │ ├── Pods-WRCycleScrollViewDemo-resources.sh │ │ ├── Pods-WRCycleScrollViewDemo-umbrella.h │ │ ├── Pods-WRCycleScrollViewDemo.debug.xcconfig │ │ ├── Pods-WRCycleScrollViewDemo.modulemap │ │ └── Pods-WRCycleScrollViewDemo.release.xcconfig ├── WRCycleScrollViewDemo.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ └── contents.xcworkspacedata ├── WRCycleScrollViewDemo.xcworkspace │ └── contents.xcworkspacedata ├── WRCycleScrollViewDemo │ ├── AppDelegate.swift │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ ├── Contents.json │ │ │ ├── wr@2x.png │ │ │ └── wr@3x.png │ │ ├── Contents.json │ │ ├── LaunchImage.launchimage │ │ │ └── Contents.json │ │ ├── dot │ │ │ ├── Contents.json │ │ │ ├── currentDot.imageset │ │ │ │ ├── Contents.json │ │ │ │ ├── slider_current_dot.png │ │ │ │ ├── slider_current_dot@2x.png │ │ │ │ └── slider_current_dot@3x.png │ │ │ └── defaultDot.imageset │ │ │ │ ├── Contents.json │ │ │ │ ├── slider_default_dot.png │ │ │ │ ├── slider_default_dot@2x.png │ │ │ │ └── slider_default_dot@3x.png │ │ ├── image11.imageset │ │ │ ├── Contents.json │ │ │ └── image11.png │ │ ├── image12.imageset │ │ │ ├── Contents.json │ │ │ └── image12.jpg │ │ ├── image13.imageset │ │ │ ├── Contents.json │ │ │ └── image13.png │ │ ├── image14.imageset │ │ │ ├── Contents.json │ │ │ └── image14.jpg │ │ ├── image15.imageset │ │ │ ├── Contents.json │ │ │ └── image15.jpg │ │ ├── localImg1.imageset │ │ │ ├── 1.png │ │ │ └── Contents.json │ │ ├── localImg10.imageset │ │ │ ├── Contents.json │ │ │ └── localImg10.jpg │ │ ├── localImg2.imageset │ │ │ ├── 2.png │ │ │ └── Contents.json │ │ ├── localImg3.imageset │ │ │ ├── 3.png │ │ │ └── Contents.json │ │ ├── localImg4.imageset │ │ │ ├── 4.png │ │ │ └── Contents.json │ │ ├── localImg5.imageset │ │ │ ├── 5.png │ │ │ └── Contents.json │ │ ├── localImg6.imageset │ │ │ ├── 6.jpg │ │ │ └── Contents.json │ │ ├── localImg7.imageset │ │ │ ├── Contents.json │ │ │ └── localImg7.jpg │ │ ├── localImg8.imageset │ │ │ ├── Contents.json │ │ │ └── localImg8.jpg │ │ ├── localImg9.imageset │ │ │ ├── Contents.json │ │ │ └── localImg9.jpg │ │ └── placeholder │ │ │ ├── Contents.json │ │ │ └── placeholder_720x360.imageset │ │ │ ├── Contents.json │ │ │ ├── placeholder_720x360@2x.png │ │ │ └── placeholder_720x360@3x.png │ ├── Base.lproj │ │ └── LaunchScreen.storyboard │ ├── CustomDotController.swift │ ├── DemoListController.swift │ ├── Info.plist │ ├── LocalImgController.swift │ ├── NoEndlessController.swift │ ├── NoPageControlController.swift │ ├── SBController.swift │ ├── ServerImgController.swift │ ├── StandaloneDotController.swift │ ├── StoryBoardController.storyboard │ ├── WRCycleScrollView │ │ ├── WRCycleCell.swift │ │ ├── WRCycleScrollView.swift │ │ ├── WRPageControl.swift │ │ └── WRProxy.swift │ ├── WRNavigationBar │ │ └── WRNavigationBar.swift │ └── ZhiHuController.swift └── WRCycleScrollViewDemoTests │ ├── Info.plist │ └── WRCycleScrollViewDemoTests.swift └── screenshots ├── StoryBoard创建.gif ├── WRCycleScrollView.png ├── demos.png ├── 不无限轮播.gif ├── 不显示pageControl.gif ├── 本地图片轮播.gif ├── 知乎日报.gif └── 网络图片轮播.gif /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata/ 19 | 20 | ## Other 21 | *.moved-aside 22 | *.xcuserstate 23 | 24 | ## Obj-C/Swift specific 25 | *.hmap 26 | *.ipa 27 | *.dSYM.zip 28 | *.dSYM 29 | 30 | ## Playgrounds 31 | timeline.xctimeline 32 | playground.xcworkspace 33 | 34 | # Swift Package Manager 35 | # 36 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 37 | # Packages/ 38 | .build/ 39 | 40 | # CocoaPods 41 | # 42 | # We recommend against adding the Pods directory to your .gitignore. However 43 | # you should judge for yourself, the pros and cons are mentioned at: 44 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 45 | # 46 | # Pods/ 47 | 48 | # Carthage 49 | # 50 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 51 | # Carthage/Checkouts 52 | 53 | Carthage/Build 54 | 55 | # fastlane 56 | # 57 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 58 | # screenshots whenever they are needed. 59 | # For more information about the recommended setup visit: 60 | # https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md 61 | 62 | fastlane/report.xml 63 | fastlane/Preview.html 64 | fastlane/screenshots 65 | fastlane/test_output 66 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![image](https://github.com/wangrui460/WRCycleScrollView/raw/master/screenshots/WRCycleScrollView.png) 2 | 3 | #####[强烈推荐使用:WRNavigationBar 超简单!!! 一行代码设置状态栏、导航栏按钮、标题、颜色、透明度,移动等](https://github.com/wangrui460/WRNavigationBar) 4 | ------------------------------------------------------------ 5 | ## iOS 技术交流 6 | 我创建了一个 微信 iOS 技术交流群,欢迎小伙伴们加入一起交流学习~ 7 | 8 | 可以加我微信我拉你进去(备注iOS),我的微信号 wr1204607318 9 | 10 | ## Requirements 11 | - iOS 8+ 12 | - Xcode 8+ 13 | 14 | 15 | ## Demo 16 | --- 17 | demo列表 18 | 19 | ![demo列表](https://github.com/wangrui460/WRCycleScrollView/raw/master/screenshots/demos.png) 20 | 21 | --- 22 | 知乎日报 23 | 24 | ![知乎日报](https://github.com/wangrui460/WRCycleScrollView/raw/master/screenshots/知乎日报.gif) 25 | 26 | --- 27 | 本地图片轮播 28 | 29 | ![本地图片轮播](https://github.com/wangrui460/WRCycleScrollView/raw/master/screenshots/本地图片轮播.gif) 30 | 31 | --- 32 | 网络图片轮播 33 | 34 | ![网络图片轮播](https://github.com/wangrui460/WRCycleScrollView/raw/master/screenshots/网络图片轮播.gif) 35 | 36 | --- 37 | StoryBoard创建 38 | 39 | ![StoryBoard创建](https://github.com/wangrui460/WRCycleScrollView/raw/master/screenshots/StoryBoard创建.gif) 40 | 41 | --- 42 | 不无限轮播 43 | 44 | ![不无限轮播](https://github.com/wangrui460/WRCycleScrollView/raw/master/screenshots/不无限轮播.gif) 45 | 46 | --- 47 | 不显示pageControl 48 | 49 | ![不显示pageControl](https://github.com/wangrui460/WRCycleScrollView/raw/master/screenshots/不显示pageControl.gif) 50 | 51 | 52 | ## Installation 53 | 54 | > **手动拖入** 55 | > 将 WRCycleScrollView 文件夹拽入项目中即可使用 56 | 57 | ## How To Use 58 | 59 |

60 | var cycleScrollView:WRCycleScrollView?
61 | let height = 520 * kScreenWidth / 1080.0
62 | let frame = CGRect(x: 0, y: 150, width: kScreenWidth, height: height)
63 | // 可加载网络图片或者本地图片
64 | let serverImages = ["http://p.lrlz.com/data/upload/mobile/special/s252/s252_05471521705899113.png",              "http://p.lrlz.com/data/upload/mobile/special/s303/s303_05442007678060723.png",                  "http://p.lrlz.com/data/upload/mobile/special/s303/s303_05442007587372591.png",                    "http://p.lrlz.com/data/upload/mobile/special/s303/s303_05442007388249407.png",                    "http://p.lrlz.com/data/upload/mobile/special/s303/s303_05442007470310935.png"]
65 | // 构造方法
66 | cycleScrollView = WRCycleScrollView(frame: frame, type: .SERVER, imgs: serverImages)
67 | view.addSubview(cycleScrollView!)
68 | // 添加代理
69 | cycleScrollView?.delegate = self
70 | 
71 | 72 | 代理方法 73 |

74 | extension ServerImgController: WRCycleScrollViewDelegate
75 | {
76 |     /// 点击图片事件
77 |     func cycleScrollViewDidSelect(at index:Int, cycleScrollView:WRCycleScrollView)
78 |     {
79 |         print("点击了第\(index+1)个图片")
80 |     }
81 |     
82 |     /// 图片滚动事件
83 |     func cycleScrollViewDidScroll(to index:Int, cycleScrollView:WRCycleScrollView)
84 |     {
85 |         print("滚动到了第\(index+1)个图片")
86 |     }
87 | }
88 | 
89 | 90 | 91 | 92 | ## License 93 | 94 | WRCycleScrollView is available under the MIT license. See the LICENSE file for more info. 95 | 96 | -------------------------------------------------------------------------------- /WRCycleScrollView/WRCycleCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WRCycleCell.swift 3 | // WRCycleScrollViewDemo 4 | // 5 | // Created by wangrui on 2017/5/12. 6 | // Copyright © 2017年 wangrui. All rights reserved. 7 | // 8 | // Github地址:https://github.com/wangrui460/WRCycleScrollView 9 | 10 | import UIKit 11 | import Kingfisher 12 | 13 | class WRCycleCell: UICollectionViewCell 14 | { 15 | //======================================================= 16 | // MARK: 对外提供的属性 17 | //======================================================= 18 | var imgSource:ImgSource = ImgSource.LOCAL(name: "placeholder") { 19 | didSet { 20 | switch imgSource { 21 | case let .SERVER(url): 22 | imgView.kf.setImage(with: url) 23 | case let .LOCAL(name): 24 | imgView.image = UIImage(named: name) 25 | } 26 | } 27 | } 28 | 29 | var placeholderImage: UIImage? 30 | 31 | var descText:String? { 32 | didSet { 33 | descLabel.isHidden = (descText == nil) ? true : false 34 | bottomView.isHidden = (descText == nil) ? true : false 35 | descLabel.text = descText 36 | } 37 | } 38 | 39 | override var frame: CGRect { 40 | didSet { 41 | bounds.size = frame.size 42 | } 43 | } 44 | 45 | var imageContentModel:UIViewContentMode = .scaleAspectFill { 46 | didSet { 47 | imgView.contentMode = imageContentModel 48 | } 49 | } 50 | 51 | var descLabelFont: UIFont = UIFont(name: "Helvetica-Bold", size: 18)! { 52 | didSet { 53 | descLabel.font = descLabelFont 54 | } 55 | } 56 | var descLabelTextColor: UIColor = UIColor.white { 57 | didSet { 58 | descLabel.textColor = descLabelTextColor 59 | } 60 | } 61 | var descLabelHeight: CGFloat = 60 { 62 | didSet { 63 | descLabel.frame.size.height = descLabelHeight 64 | } 65 | } 66 | var descLabelTextAlignment:NSTextAlignment = .left { 67 | didSet { 68 | descLabel.textAlignment = descLabelTextAlignment 69 | } 70 | } 71 | var bottomViewBackgroundColor: UIColor = UIColor.black.withAlphaComponent(0.5) { 72 | didSet { 73 | bottomView.backgroundColor = bottomViewBackgroundColor 74 | } 75 | } 76 | 77 | //======================================================= 78 | // MARK: 内部属性 79 | //======================================================= 80 | fileprivate var imgView:UIImageView! 81 | fileprivate var descLabel:UILabel! 82 | fileprivate var bottomView:UIView! 83 | 84 | //======================================================= 85 | // MARK: 构造方法 86 | //======================================================= 87 | override init(frame: CGRect) 88 | { 89 | super.init(frame: frame) 90 | backgroundColor = UIColor.white 91 | setupImgView() 92 | setupDescLabel() 93 | setupBottomView() 94 | } 95 | required init?(coder aDecoder: NSCoder) { 96 | fatalError("init(coder:) has not been implemented") 97 | } 98 | deinit { 99 | print("WRCycleCell deinit") 100 | } 101 | 102 | 103 | //======================================================= 104 | // MARK: 内部方法(layoutSubviews) 105 | //======================================================= 106 | override func layoutSubviews() 107 | { 108 | super.layoutSubviews() 109 | imgView.frame = self.bounds 110 | 111 | if let _ = descText 112 | { 113 | let margin:CGFloat = 16 114 | let labelWidth = imgView.bounds.width - 2 * margin 115 | let labelHeight = descLabelHeight 116 | let labelY = bounds.height - labelHeight 117 | descLabel.frame = CGRect(x: margin, y: labelY, width: labelWidth, height: labelHeight) 118 | bottomView.frame = CGRect(x: 0, y: labelY, width: imgView.bounds.width, height: labelHeight) 119 | bringSubview(toFront: descLabel) 120 | } 121 | } 122 | } 123 | 124 | //======================================================= 125 | // MARK: - 基本控件(图片、描述文字、底部view) 126 | //======================================================= 127 | extension WRCycleCell 128 | { 129 | fileprivate func setupImgView() 130 | { 131 | imgView = UIImageView() 132 | imgView.contentMode = imageContentModel 133 | imgView.clipsToBounds = true 134 | addSubview(imgView) 135 | } 136 | 137 | fileprivate func setupDescLabel() 138 | { 139 | descLabel = UILabel() 140 | descLabel.text = descText 141 | descLabel.numberOfLines = 0 142 | descLabel.font = descLabelFont 143 | descLabel.textColor = descLabelTextColor 144 | descLabel.frame.size.height = descLabelHeight 145 | descLabel.textAlignment = descLabelTextAlignment 146 | addSubview(descLabel) 147 | descLabel.isHidden = true 148 | } 149 | 150 | fileprivate func setupBottomView() 151 | { 152 | bottomView = UIView() 153 | bottomView.backgroundColor = bottomViewBackgroundColor 154 | addSubview(bottomView) 155 | bottomView.isHidden = true 156 | } 157 | } 158 | 159 | -------------------------------------------------------------------------------- /WRCycleScrollView/WRPageControl.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WRPageControl.swift 3 | // WRCycleScrollViewDemo 4 | // 5 | // Created by itwangrui on 2017/11/14. 6 | // Copyright © 2017年 wangrui. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | private let WRPageControlPointWidth: CGFloat = 7 12 | private let WRPageControlPointHeight: CGFloat = 20 13 | 14 | class WRPageControl: UIPageControl 15 | { 16 | var currentImage: UIImage? 17 | var defaultImage: UIImage? 18 | var pointSpace: CGFloat = 15 19 | var pageSize: CGSize { 20 | get { 21 | if let curImage = currentImage, let defImage = defaultImage { 22 | let pageH = curImage.size.height 23 | let defDotW = defImage.size.width 24 | let curDotW = curImage.size.width 25 | let pageW = CGFloat(numberOfPages - 1) * (pointSpace + defDotW + defDotW) + curDotW 26 | return CGSize(width: pageW, height: pageH) 27 | } 28 | else { 29 | let pageW = CGFloat(numberOfPages - 1) * (pointSpace + WRPageControlPointWidth) + WRPageControlPointWidth 30 | return CGSize(width: pageW, height: WRPageControlPointHeight) 31 | } 32 | } 33 | } 34 | 35 | override var currentPage: Int { 36 | didSet { 37 | updatePageControl() 38 | } 39 | } 40 | 41 | init(frame: CGRect, currentImage: UIImage?, defaultImage: UIImage?) 42 | { 43 | super.init(frame: frame) 44 | self.currentImage = currentImage 45 | self.defaultImage = defaultImage 46 | } 47 | required init?(coder aDecoder: NSCoder) { 48 | fatalError("init(coder:) has not been implemented") 49 | } 50 | } 51 | 52 | 53 | // MARK: - update index 54 | extension WRPageControl 55 | { 56 | func updatePageControl() 57 | { 58 | for index in 0.. CGSize 92 | { 93 | var newSize = CGSize(width: 0, height: 0) 94 | if let curImage = currentImage, let defImage = defaultImage { 95 | if currentIndex == currentPage { 96 | newSize = curImage.size 97 | } else { 98 | newSize = defImage.size 99 | } 100 | } else { 101 | newSize = CGSize(width: WRPageControlPointWidth, height: WRPageControlPointWidth) 102 | } 103 | return newSize 104 | } 105 | } 106 | 107 | 108 | -------------------------------------------------------------------------------- /WRCycleScrollView/WRProxy.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WRProxy.swift 3 | // WRCycleScrollViewDemo 4 | // 5 | // Created by wangrui on 2017/5/15. 6 | // Copyright © 2017年 wangrui. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | ///////////////////////////////////////////////////////////////////////////// 12 | // MARK: 数据 相关 13 | ///////////////////////////////////////////////////////////////////////////// 14 | 15 | // 图片资源 16 | enum ImgSource { 17 | case SERVER(url:URL) 18 | case LOCAL(name:String) 19 | } 20 | 21 | // 图片类型 22 | enum ImgType:Int { 23 | case SERVER = 0 // default 24 | case LOCAL = 1 25 | } 26 | 27 | struct Proxy 28 | { 29 | var imgType:ImgType = .SERVER 30 | var imgArray:[ImgSource] = [ImgSource]() 31 | 32 | // 下标法获取imgArray中对应索引的ImgSource eg: proxy[0] == imgArray[0] 33 | subscript (index:Int) -> ImgSource { 34 | get { 35 | return imgArray[index] 36 | } 37 | } 38 | 39 | // 构造方法 40 | init(type:ImgType, array:[String]) 41 | { 42 | imgType = type 43 | if imgType == .SERVER 44 | { 45 | imgArray = array.map({ (urlStr) -> ImgSource in 46 | return ImgSource.SERVER(url: URL(string: urlStr)!) 47 | }) 48 | } 49 | else 50 | { 51 | imgArray = array.map({ (name) -> ImgSource in 52 | return ImgSource.LOCAL(name: name) 53 | }) 54 | } 55 | } 56 | } 57 | 58 | 59 | ///////////////////////////////////////////////////////////////////////////// 60 | // MARK: pageControl 相关 61 | ///////////////////////////////////////////////////////////////////////////// 62 | 63 | private let WRPageControlMargin: CGFloat = 15 64 | private let WRPageControlPointWidth: CGFloat = 2 65 | 66 | enum PageControlAliment { 67 | case CenterBottom 68 | case RightBottom 69 | case LeftBottom 70 | } 71 | 72 | protocol PageControlAlimentProtocol 73 | { 74 | // Property in protocol must have explicit { get } or { get set } specifier 75 | var pageControlAliment: PageControlAliment { get set } 76 | var pageControlPointSpace: CGFloat { get set } 77 | func relayoutPageControl(pageControl: WRPageControl) 78 | func relayoutPageControl(pageControl: WRPageControl, outerFrame:CGRect) 79 | } 80 | 81 | extension PageControlAlimentProtocol where Self : UIView 82 | { // TODO: 等待优化 83 | func relayoutPageControl(pageControl: WRPageControl) 84 | { 85 | if pageControl.isHidden == false 86 | { 87 | let pageH:CGFloat = 20//pageControl.pageSize.height 88 | let pageY = bounds.height - pageH 89 | let pageW = pageControl.pageSize.width 90 | var pageX:CGFloat = 0 91 | 92 | switch self.pageControlAliment { 93 | case .CenterBottom: 94 | pageX = CGFloat(self.bounds.width / 2) - pageW * 0.5 95 | case .RightBottom: 96 | pageX = bounds.width - pageW - WRPageControlMargin 97 | case .LeftBottom: 98 | pageX = bounds.origin.x + WRPageControlMargin 99 | } 100 | pageControl.frame = CGRect(x:pageX, y:pageY, width:pageW, height:pageH) 101 | } 102 | } 103 | func relayoutPageControl(pageControl: WRPageControl, outerFrame:CGRect) 104 | { 105 | if pageControl.isHidden == false { 106 | pageControl.frame = CGRect(x:outerFrame.origin.x, y:outerFrame.origin.y, width:pageControl.pageSize.width, height:pageControl.pageSize.height) 107 | } 108 | } 109 | } 110 | 111 | 112 | ///////////////////////////////////////////////////////////////////////////// 113 | // MARK: 无限轮播 相关 114 | ///////////////////////////////////////////////////////////////////////////// 115 | 116 | protocol EndlessScrollProtocol 117 | { 118 | ///////////////////////////////// 119 | /// 是否自动滚动 120 | var isAutoScroll: Bool { get set } 121 | /// 自动滚动的时间间隔 122 | var autoScrollInterval: Double { get set } 123 | /// 用于自动滚动的定时器 124 | var timer:Timer? { get set } 125 | 126 | ///////////////////////////////// 127 | /// 是否无限轮播 128 | var isEndlessScroll: Bool { get set } 129 | /// 无线轮播中,一组图片最多轮播多少次 130 | var endlessScrollTimes: Int { get } 131 | /// 真实的cell数量 132 | var itemsInSection: Int { get } 133 | 134 | /** 设置定时器,用于自动滚动 */ 135 | func setupTimer() 136 | 137 | /** 滚动到第一个cell (在无限轮播中就是中间的那个cell) */ 138 | func changeToFirstCycleCell(animated: Bool, collectionView: UICollectionView) 139 | } 140 | 141 | extension EndlessScrollProtocol where Self : UIView 142 | { 143 | func changeCycleCell(collectionView: UICollectionView) 144 | { 145 | guard itemsInSection != 0 else { 146 | return 147 | } 148 | 149 | let flowLayout = collectionView.collectionViewLayout as! UICollectionViewFlowLayout 150 | let curItem = Int(collectionView.contentOffset.x / flowLayout.itemSize.width) 151 | if curItem == itemsInSection - 1 152 | { 153 | let animated = (isEndlessScroll == true) ? false : true 154 | changeToFirstCycleCell(animated: animated, collectionView: collectionView) 155 | } 156 | else 157 | { 158 | let indexPath = IndexPath(item: curItem + 1, section: 0) 159 | collectionView.scrollToItem(at: indexPath, at: .init(rawValue: 0), animated: true) 160 | } 161 | } 162 | 163 | func changeToFirstCycleCell(animated: Bool, collectionView: UICollectionView) 164 | { 165 | guard itemsInSection != 0 else { 166 | return 167 | } 168 | 169 | let firstItem = (isEndlessScroll == true) ? (itemsInSection / 2) : 0 170 | let indexPath = IndexPath(item: firstItem, section: 0) 171 | collectionView.scrollToItem(at: indexPath, at: .init(rawValue: 0), animated: animated) 172 | } 173 | } 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | 3 | #官方Cocoapods的源 4 | #source 'https://github.com/CocoaPods/Specs.git' 5 | 6 | #本地私有源 7 | #source 'https://git.oschina.net/scx/JLSpecs.git' 8 | 9 | # 本项目地址 10 | #source https://github.com/wangrui460 11 | 12 | # 修改过得第三方库 13 | 14 | 15 | platform :ios, '8.0' 16 | use_frameworks! 17 | 18 | target 'WRCycleScrollViewDemo' do 19 | 20 | #swift 21 | pod 'Kingfisher' 22 | 23 | end 24 | -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Kingfisher (3.6.2) 3 | 4 | DEPENDENCIES: 5 | - Kingfisher 6 | 7 | SPEC CHECKSUMS: 8 | Kingfisher: 2c94e72c6830622c71d06adf4ea024c37d316830 9 | 10 | PODFILE CHECKSUM: 6daf8f1d76fe64163802c6a83d3d697d2866e5e0 11 | 12 | COCOAPODS: 1.2.1 13 | -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/Pods/Kingfisher/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 Wei Wang 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/Pods/Kingfisher/README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | Kingfisher 4 | 5 |

6 | 7 |

8 | 9 | 10 | 11 | 12 | 13 | 14 | codebeat badge 15 | 16 |

17 | 18 | Kingfisher is a lightweight, pure-Swift library for downloading and caching images from the web. This project is heavily inspired by the popular [SDWebImage](https://github.com/rs/SDWebImage). It provides you a chance to use a pure-Swift alternative in your next app. 19 | 20 | ## Features 21 | 22 | - [x] Asynchronous image downloading and caching. 23 | - [x] `URLSession`-based networking. Basic image processors and filters supplied. 24 | - [x] Multiple-layer cache for both memory and disk. 25 | - [x] Cancelable downloading and processing tasks to improve performance. 26 | - [x] Independent components. Use the downloader or caching system separately as you need. 27 | - [x] Prefetching images and showing them from cache later when necessary. 28 | - [x] Extensions for `UIImageView`, `NSImage` and `UIButton` to directly set an image from a URL. 29 | - [x] Built-in transition animation when setting images. 30 | - [x] Extensible image processing and image format support. 31 | 32 | The simplest use-case is setting an image to an image view with the `UIImageView` extension: 33 | 34 | ```swift 35 | let url = URL(string: "url_of_your_image") 36 | imageView.kf.setImage(with: url) 37 | ``` 38 | 39 | Kingfisher will download the image from `url`, send it to both the memory cache and the disk cache, and display it in `imageView`. When you use the same code later, the image will be retrieved from cache and shown immediately. 40 | 41 | ## Requirements 42 | 43 | - iOS 8.0+ / macOS 10.10+ / tvOS 9.0+ / watchOS 2.0+ 44 | - Swift 3 (Kingfisher 3.x), Swift 2.3 (Kingfisher 2.x) 45 | 46 | Main development of Kingfisher will support Swift 3. Only critical bug fixes will be made for Kingfisher 2.x. 47 | 48 | [Kingfisher 3.0 Migration Guide](https://github.com/onevcat/Kingfisher/wiki/Kingfisher-3.0-Migration-Guide) - If you are upgrading to Kingfisher 3.x from an earlier version, please read this for more information. 49 | 50 | ## Next Steps 51 | 52 | We prepared a [wiki page](https://github.com/onevcat/Kingfisher/wiki). You can find tons of useful things there. 53 | 54 | * [Installation Guide](https://github.com/onevcat/Kingfisher/wiki/Installation-Guide) - Follow it to integrate Kingfisher into your project. 55 | * [Cheat Sheet](https://github.com/onevcat/Kingfisher/wiki/Cheat-Sheet)- Curious about what Kingfisher could do and how would it look like when used in your project? See this page for useful code snippets. If you are already familiar with Kingfisher, you could also learn new tricks to improve the way you use Kingfisher! 56 | * [API Reference](http://onevcat.github.io/Kingfisher/) - Lastly, please remember to read the full whenever you may need a more detailed reference. 57 | 58 | ## Other 59 | 60 | ### Future of Kingfisher 61 | 62 | I want to keep Kingfisher lightweight. This framework will focus on providing a simple solution for downloading and caching images. This doesn’t mean the framework can’t be improved. Kingfisher is far from perfect, so necessary and useful updates will be made to make it better. 63 | 64 | ### Developments and Tests 65 | 66 | Any contributing and pull requests are warmly welcome. However, before you plan to implement some features or try to fix an uncertain issue, it is recommended to open a discussion first. 67 | 68 | The test images are contained in another project to keep this project repo fast and slim. You could run `./setup.sh` in the root folder of Kingfisher to clone the test images when you need to run the tests target. It would be appreciated if your pull requests could build and with all tests green. :) 69 | 70 | ### About the logo 71 | 72 | The logo of Kingfisher is inspired by [Tangram (七巧板)](http://en.wikipedia.org/wiki/Tangram), a dissection puzzle consisting of seven flat shapes from China. I believe she's a kingfisher bird instead of a swift, but someone insists that she is a pigeon. I guess I should give her a name. Hi, guys, do you have any suggestions? 73 | 74 | ### Contact 75 | 76 | Follow and contact me on [Twitter](http://twitter.com/onevcat) or [Sina Weibo](http://weibo.com/onevcat). If you find an issue, just [open a ticket](https://github.com/onevcat/Kingfisher/issues/new). Pull requests are warmly welcome as well. 77 | 78 | ### License 79 | 80 | Kingfisher is released under the MIT license. See LICENSE for details. 81 | 82 | 83 | -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/Pods/Kingfisher/Sources/AnimatedImageView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AnimatableImageView.swift 3 | // Kingfisher 4 | // 5 | // Created by bl4ckra1sond3tre on 4/22/16. 6 | // 7 | // The AnimatableImageView, AnimatedFrame and Animator is a modified version of 8 | // some classes from kaishin's Gifu project (https://github.com/kaishin/Gifu) 9 | // 10 | // The MIT License (MIT) 11 | // 12 | // Copyright (c) 2017 Reda Lemeden. 13 | // 14 | // Permission is hereby granted, free of charge, to any person obtaining a copy of 15 | // this software and associated documentation files (the "Software"), to deal in 16 | // the Software without restriction, including without limitation the rights to 17 | // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 18 | // the Software, and to permit persons to whom the Software is furnished to do so, 19 | // subject to the following conditions: 20 | // 21 | // The above copyright notice and this permission notice shall be included in all 22 | // copies or substantial portions of the Software. 23 | // 24 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 25 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 26 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 27 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 28 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 29 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 30 | // 31 | // The name and characters used in the demo of this software are property of their 32 | // respective owners. 33 | 34 | import UIKit 35 | import ImageIO 36 | 37 | /// `AnimatedImageView` is a subclass of `UIImageView` for displaying animated image. 38 | open class AnimatedImageView: UIImageView { 39 | 40 | /// Proxy object for prevending a reference cycle between the CADDisplayLink and AnimatedImageView. 41 | class TargetProxy { 42 | private weak var target: AnimatedImageView? 43 | 44 | init(target: AnimatedImageView) { 45 | self.target = target 46 | } 47 | 48 | @objc func onScreenUpdate() { 49 | target?.updateFrame() 50 | } 51 | } 52 | 53 | // MARK: - Public property 54 | /// Whether automatically play the animation when the view become visible. Default is true. 55 | public var autoPlayAnimatedImage = true 56 | 57 | /// The size of the frame cache. 58 | public var framePreloadCount = 10 59 | 60 | /// Specifies whether the GIF frames should be pre-scaled to save memory. Default is true. 61 | public var needsPrescaling = true 62 | 63 | /// The animation timer's run loop mode. Default is `NSRunLoopCommonModes`. Set this property to `NSDefaultRunLoopMode` will make the animation pause during UIScrollView scrolling. 64 | public var runLoopMode = RunLoopMode.commonModes { 65 | willSet { 66 | if runLoopMode == newValue { 67 | return 68 | } else { 69 | stopAnimating() 70 | displayLink.remove(from: .main, forMode: runLoopMode) 71 | displayLink.add(to: .main, forMode: newValue) 72 | startAnimating() 73 | } 74 | } 75 | } 76 | 77 | // MARK: - Private property 78 | /// `Animator` instance that holds the frames of a specific image in memory. 79 | private var animator: Animator? 80 | 81 | /// A flag to avoid invalidating the displayLink on deinit if it was never created, because displayLink is so lazy. :D 82 | private var isDisplayLinkInitialized: Bool = false 83 | 84 | /// A display link that keeps calling the `updateFrame` method on every screen refresh. 85 | private lazy var displayLink: CADisplayLink = { 86 | self.isDisplayLinkInitialized = true 87 | let displayLink = CADisplayLink(target: TargetProxy(target: self), selector: #selector(TargetProxy.onScreenUpdate)) 88 | displayLink.add(to: .main, forMode: self.runLoopMode) 89 | displayLink.isPaused = true 90 | return displayLink 91 | }() 92 | 93 | // MARK: - Override 94 | override open var image: Image? { 95 | didSet { 96 | if image != oldValue { 97 | reset() 98 | } 99 | setNeedsDisplay() 100 | layer.setNeedsDisplay() 101 | } 102 | } 103 | 104 | deinit { 105 | if isDisplayLinkInitialized { 106 | displayLink.invalidate() 107 | } 108 | } 109 | 110 | override open var isAnimating: Bool { 111 | if isDisplayLinkInitialized { 112 | return !displayLink.isPaused 113 | } else { 114 | return super.isAnimating 115 | } 116 | } 117 | 118 | /// Starts the animation. 119 | override open func startAnimating() { 120 | if self.isAnimating { 121 | return 122 | } else { 123 | displayLink.isPaused = false 124 | } 125 | } 126 | 127 | /// Stops the animation. 128 | override open func stopAnimating() { 129 | super.stopAnimating() 130 | if isDisplayLinkInitialized { 131 | displayLink.isPaused = true 132 | } 133 | } 134 | 135 | override open func display(_ layer: CALayer) { 136 | if let currentFrame = animator?.currentFrame { 137 | layer.contents = currentFrame.cgImage 138 | } else { 139 | layer.contents = image?.cgImage 140 | } 141 | } 142 | 143 | override open func didMoveToWindow() { 144 | super.didMoveToWindow() 145 | didMove() 146 | } 147 | 148 | override open func didMoveToSuperview() { 149 | super.didMoveToSuperview() 150 | didMove() 151 | } 152 | 153 | // This is for back compatibility that using regular UIImageView to show GIF. 154 | override func shouldPreloadAllGIF() -> Bool { 155 | return false 156 | } 157 | 158 | // MARK: - Private method 159 | /// Reset the animator. 160 | private func reset() { 161 | animator = nil 162 | if let imageSource = image?.kf.imageSource?.imageRef { 163 | animator = Animator(imageSource: imageSource, contentMode: contentMode, size: bounds.size, framePreloadCount: framePreloadCount) 164 | animator?.needsPrescaling = needsPrescaling 165 | animator?.prepareFramesAsynchronously() 166 | } 167 | didMove() 168 | } 169 | 170 | private func didMove() { 171 | if autoPlayAnimatedImage && animator != nil { 172 | if let _ = superview, let _ = window { 173 | startAnimating() 174 | } else { 175 | stopAnimating() 176 | } 177 | } 178 | } 179 | 180 | /// Update the current frame with the displayLink duration. 181 | private func updateFrame() { 182 | if animator?.updateCurrentFrame(duration: displayLink.duration) ?? false { 183 | layer.setNeedsDisplay() 184 | } 185 | } 186 | } 187 | 188 | /// Keeps a reference to an `Image` instance and its duration as a GIF frame. 189 | struct AnimatedFrame { 190 | var image: Image? 191 | let duration: TimeInterval 192 | 193 | static let null: AnimatedFrame = AnimatedFrame(image: .none, duration: 0.0) 194 | } 195 | 196 | // MARK: - Animator 197 | class Animator { 198 | // MARK: Private property 199 | fileprivate let size: CGSize 200 | fileprivate let maxFrameCount: Int 201 | fileprivate let imageSource: CGImageSource 202 | 203 | fileprivate var animatedFrames = [AnimatedFrame]() 204 | fileprivate let maxTimeStep: TimeInterval = 1.0 205 | fileprivate var frameCount = 0 206 | fileprivate var currentFrameIndex = 0 207 | fileprivate var currentPreloadIndex = 0 208 | fileprivate var timeSinceLastFrameChange: TimeInterval = 0.0 209 | fileprivate var needsPrescaling = true 210 | 211 | /// Loop count of animatd image. 212 | private var loopCount = 0 213 | 214 | var currentFrame: UIImage? { 215 | return frame(at: currentFrameIndex) 216 | } 217 | 218 | var contentMode = UIViewContentMode.scaleToFill 219 | 220 | private lazy var preloadQueue: DispatchQueue = { 221 | return DispatchQueue(label: "com.onevcat.Kingfisher.Animator.preloadQueue") 222 | }() 223 | 224 | /** 225 | Init an animator with image source reference. 226 | 227 | - parameter imageSource: The reference of animated image. 228 | - parameter contentMode: Content mode of AnimatedImageView. 229 | - parameter size: Size of AnimatedImageView. 230 | - parameter framePreloadCount: Frame cache size. 231 | 232 | - returns: The animator object. 233 | */ 234 | init(imageSource source: CGImageSource, contentMode mode: UIViewContentMode, size: CGSize, framePreloadCount count: Int) { 235 | self.imageSource = source 236 | self.contentMode = mode 237 | self.size = size 238 | self.maxFrameCount = count 239 | } 240 | 241 | func frame(at index: Int) -> Image? { 242 | return animatedFrames[safe: index]?.image 243 | } 244 | 245 | func prepareFramesAsynchronously() { 246 | preloadQueue.async { [weak self] in 247 | self?.prepareFrames() 248 | } 249 | } 250 | 251 | private func prepareFrames() { 252 | frameCount = CGImageSourceGetCount(imageSource) 253 | 254 | if let properties = CGImageSourceCopyProperties(imageSource, nil), 255 | let gifInfo = (properties as NSDictionary)[kCGImagePropertyGIFDictionary as String] as? NSDictionary, 256 | let loopCount = gifInfo[kCGImagePropertyGIFLoopCount as String] as? Int 257 | { 258 | self.loopCount = loopCount 259 | } 260 | 261 | let frameToProcess = min(frameCount, maxFrameCount) 262 | animatedFrames.reserveCapacity(frameToProcess) 263 | animatedFrames = (0.. AnimatedFrame { 268 | 269 | guard let imageRef = CGImageSourceCreateImageAtIndex(imageSource, index, nil) else { 270 | return AnimatedFrame.null 271 | } 272 | 273 | let defaultGIFFrameDuration = 0.100 274 | let frameDuration = imageSource.kf.gifProperties(at: index).map { 275 | gifInfo -> Double in 276 | 277 | let unclampedDelayTime = gifInfo[kCGImagePropertyGIFUnclampedDelayTime as String] as Double? 278 | let delayTime = gifInfo[kCGImagePropertyGIFDelayTime as String] as Double? 279 | let duration = unclampedDelayTime ?? delayTime ?? 0.0 280 | 281 | /** 282 | http://opensource.apple.com/source/WebCore/WebCore-7600.1.25/platform/graphics/cg/ImageSourceCG.cpp 283 | Many annoying ads specify a 0 duration to make an image flash as quickly as 284 | possible. We follow Safari and Firefox's behavior and use a duration of 100 ms 285 | for any frames that specify a duration of <= 10 ms. 286 | See and for more information. 287 | 288 | See also: http://nullsleep.tumblr.com/post/16524517190/animated-gif-minimum-frame-delay-browser. 289 | */ 290 | return duration > 0.011 ? duration : defaultGIFFrameDuration 291 | } ?? defaultGIFFrameDuration 292 | 293 | let image = Image(cgImage: imageRef) 294 | let scaledImage: Image? 295 | 296 | if needsPrescaling { 297 | scaledImage = image.kf.resize(to: size, for: contentMode) 298 | } else { 299 | scaledImage = image 300 | } 301 | 302 | return AnimatedFrame(image: scaledImage, duration: frameDuration) 303 | } 304 | 305 | /** 306 | Updates the current frame if necessary using the frame timer and the duration of each frame in `animatedFrames`. 307 | */ 308 | func updateCurrentFrame(duration: CFTimeInterval) -> Bool { 309 | timeSinceLastFrameChange += min(maxTimeStep, duration) 310 | guard let frameDuration = animatedFrames[safe: currentFrameIndex]?.duration, frameDuration <= timeSinceLastFrameChange else { 311 | return false 312 | } 313 | 314 | timeSinceLastFrameChange -= frameDuration 315 | 316 | let lastFrameIndex = currentFrameIndex 317 | currentFrameIndex += 1 318 | currentFrameIndex = currentFrameIndex % animatedFrames.count 319 | 320 | if animatedFrames.count < frameCount { 321 | preloadFrameAsynchronously(at: lastFrameIndex) 322 | } 323 | return true 324 | } 325 | 326 | private func preloadFrameAsynchronously(at index: Int) { 327 | preloadQueue.async { [weak self] in 328 | self?.preloadFrame(at: index) 329 | } 330 | } 331 | 332 | private func preloadFrame(at index: Int) { 333 | animatedFrames[index] = prepareFrame(at: currentPreloadIndex) 334 | currentPreloadIndex += 1 335 | currentPreloadIndex = currentPreloadIndex % frameCount 336 | } 337 | } 338 | 339 | extension CGImageSource: KingfisherCompatible { } 340 | extension Kingfisher where Base: CGImageSource { 341 | func gifProperties(at index: Int) -> [String: Double]? { 342 | let properties = CGImageSourceCopyPropertiesAtIndex(base, index, nil) as Dictionary? 343 | return properties?[kCGImagePropertyGIFDictionary] as? [String: Double] 344 | } 345 | } 346 | 347 | extension Array { 348 | subscript(safe index: Int) -> Element? { 349 | return indices ~= index ? self[index] : nil 350 | } 351 | } 352 | 353 | private func pure(_ value: T) -> [T] { 354 | return [value] 355 | } 356 | -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/Pods/Kingfisher/Sources/Box.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Box.swift 3 | // Kingfisher 4 | // 5 | // Created by WANG WEI on 2016/09/12. 6 | // Copyright © 2016年 Wei Wang. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class Box { 12 | let value: T 13 | init(value: T) { 14 | self.value = value 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/Pods/Kingfisher/Sources/CacheSerializer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CacheSerializer.swift 3 | // Kingfisher 4 | // 5 | // Created by Wei Wang on 2016/09/02. 6 | // 7 | // Copyright (c) 2017 Wei Wang 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | import Foundation 28 | 29 | /// An `CacheSerializer` would be used to convert some data to an image object for 30 | /// retrieving from disk cache and vice versa for storing to disk cache. 31 | public protocol CacheSerializer { 32 | 33 | /// Get the serialized data from a provided image 34 | /// and optional original data for caching to disk. 35 | /// 36 | /// 37 | /// - parameter image: The image needed to be serialized. 38 | /// - parameter original: The original data which is just downloaded. 39 | /// If the image is retrieved from cache instead of 40 | /// downloaded, it will be `nil`. 41 | /// 42 | /// - returns: A data which will be stored to cache, or `nil` when no valid 43 | /// data could be serialized. 44 | func data(with image: Image, original: Data?) -> Data? 45 | 46 | /// Get an image deserialized from provided data. 47 | /// 48 | /// - parameter data: The data from which an image should be deserialized. 49 | /// - parameter options: Options for deserialization. 50 | /// 51 | /// - returns: An image deserialized or `nil` when no valid image 52 | /// could be deserialized. 53 | func image(with data: Data, options: KingfisherOptionsInfo?) -> Image? 54 | } 55 | 56 | 57 | /// `DefaultCacheSerializer` is a basic `CacheSerializer` used in default cache of 58 | /// Kingfisher. It could serialize and deserialize PNG, JEPG and GIF images. For 59 | /// image other than these formats, a normalized `pngRepresentation` will be used. 60 | public struct DefaultCacheSerializer: CacheSerializer { 61 | 62 | public static let `default` = DefaultCacheSerializer() 63 | private init() {} 64 | 65 | public func data(with image: Image, original: Data?) -> Data? { 66 | let imageFormat = original?.kf.imageFormat ?? .unknown 67 | 68 | let data: Data? 69 | switch imageFormat { 70 | case .PNG: data = image.kf.pngRepresentation() 71 | case .JPEG: data = image.kf.jpegRepresentation(compressionQuality: 1.0) 72 | case .GIF: data = image.kf.gifRepresentation() 73 | case .unknown: data = original ?? image.kf.normalized.kf.pngRepresentation() 74 | } 75 | 76 | return data 77 | } 78 | 79 | public func image(with data: Data, options: KingfisherOptionsInfo?) -> Image? { 80 | let options = options ?? KingfisherEmptyOptionsInfo 81 | return Kingfisher.image( 82 | data: data, 83 | scale: options.scaleFactor, 84 | preloadAllGIFData: options.preloadAllGIFData, 85 | onlyFirstFrame: options.onlyLoadFirstFrame) 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/Pods/Kingfisher/Sources/Filter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Filter.swift 3 | // Kingfisher 4 | // 5 | // Created by Wei Wang on 2016/08/31. 6 | // 7 | // Copyright (c) 2017 Wei Wang 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | 28 | 29 | import CoreImage 30 | import Accelerate 31 | 32 | // Reuse the same CI Context for all CI drawing. 33 | private let ciContext = CIContext(options: nil) 34 | 35 | /// Transformer method which will be used in to provide a `Filter`. 36 | public typealias Transformer = (CIImage) -> CIImage? 37 | 38 | /// Supply a filter to create an `ImageProcessor`. 39 | public protocol CIImageProcessor: ImageProcessor { 40 | var filter: Filter { get } 41 | } 42 | 43 | extension CIImageProcessor { 44 | public func process(item: ImageProcessItem, options: KingfisherOptionsInfo) -> Image? { 45 | switch item { 46 | case .image(let image): 47 | return image.kf.apply(filter) 48 | case .data(_): 49 | return (DefaultImageProcessor.default >> self).process(item: item, options: options) 50 | } 51 | } 52 | } 53 | 54 | /// Wrapper for a `Transformer` of CIImage filters. 55 | public struct Filter { 56 | 57 | let transform: Transformer 58 | 59 | public init(tranform: @escaping Transformer) { 60 | self.transform = tranform 61 | } 62 | 63 | /// Tint filter which will apply a tint color to images. 64 | public static var tint: (Color) -> Filter = { 65 | color in 66 | Filter { input in 67 | let colorFilter = CIFilter(name: "CIConstantColorGenerator")! 68 | colorFilter.setValue(CIColor(color: color), forKey: kCIInputColorKey) 69 | 70 | let colorImage = colorFilter.outputImage 71 | let filter = CIFilter(name: "CISourceOverCompositing")! 72 | filter.setValue(colorImage, forKey: kCIInputImageKey) 73 | filter.setValue(input, forKey: kCIInputBackgroundImageKey) 74 | return filter.outputImage?.cropping(to: input.extent) 75 | } 76 | } 77 | 78 | public typealias ColorElement = (CGFloat, CGFloat, CGFloat, CGFloat) 79 | 80 | /// Color control filter which will apply color control change to images. 81 | public static var colorControl: (ColorElement) -> Filter = { 82 | brightness, contrast, saturation, inputEV in 83 | Filter { input in 84 | let paramsColor = [kCIInputBrightnessKey: brightness, 85 | kCIInputContrastKey: contrast, 86 | kCIInputSaturationKey: saturation] 87 | 88 | let blackAndWhite = input.applyingFilter("CIColorControls", withInputParameters: paramsColor) 89 | let paramsExposure = [kCIInputEVKey: inputEV] 90 | return blackAndWhite.applyingFilter("CIExposureAdjust", withInputParameters: paramsExposure) 91 | } 92 | 93 | } 94 | } 95 | 96 | extension Kingfisher where Base: Image { 97 | /// Apply a `Filter` containing `CIImage` transformer to `self`. 98 | /// 99 | /// - parameter filter: The filter used to transform `self`. 100 | /// 101 | /// - returns: A transformed image by input `Filter`. 102 | /// 103 | /// - Note: Only CG-based images are supported. If any error happens during transforming, `self` will be returned. 104 | public func apply(_ filter: Filter) -> Image { 105 | 106 | guard let cgImage = cgImage else { 107 | assertionFailure("[Kingfisher] Tint image only works for CG-based image.") 108 | return base 109 | } 110 | 111 | let inputImage = CIImage(cgImage: cgImage) 112 | guard let outputImage = filter.transform(inputImage) else { 113 | return base 114 | } 115 | 116 | guard let result = ciContext.createCGImage(outputImage, from: outputImage.extent) else { 117 | assertionFailure("[Kingfisher] Can not make an tint image within context.") 118 | return base 119 | } 120 | 121 | #if os(macOS) 122 | return fixedForRetinaPixel(cgImage: result, to: size) 123 | #else 124 | return Image(cgImage: result, scale: base.scale, orientation: base.imageOrientation) 125 | #endif 126 | } 127 | 128 | } 129 | 130 | public extension Image { 131 | 132 | /// Apply a `Filter` containing `CIImage` transformer to `self`. 133 | /// 134 | /// - parameter filter: The filter used to transform `self`. 135 | /// 136 | /// - returns: A transformed image by input `Filter`. 137 | /// 138 | /// - Note: Only CG-based images are supported. If any error happens during transforming, `self` will be returned. 139 | @available(*, deprecated, 140 | message: "Extensions directly on Image are deprecated. Use `kf.apply` instead.", 141 | renamed: "kf.apply") 142 | public func kf_apply(_ filter: Filter) -> Image { 143 | return kf.apply(filter) 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/Pods/Kingfisher/Sources/ImagePrefetcher.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ImagePrefetcher.swift 3 | // Kingfisher 4 | // 5 | // Created by Claire Knight on 24/02/2016 6 | // 7 | // Copyright (c) 2017 Wei Wang 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | 28 | #if os(macOS) 29 | import AppKit 30 | #else 31 | import UIKit 32 | #endif 33 | 34 | 35 | /// Progress update block of prefetcher. 36 | /// 37 | /// - `skippedResources`: An array of resources that are already cached before the prefetching starting. 38 | /// - `failedResources`: An array of resources that fail to be downloaded. It could because of being cancelled while downloading, encountered an error when downloading or the download not being started at all. 39 | /// - `completedResources`: An array of resources that are downloaded and cached successfully. 40 | public typealias PrefetcherProgressBlock = ((_ skippedResources: [Resource], _ failedResources: [Resource], _ completedResources: [Resource]) -> ()) 41 | 42 | /// Completion block of prefetcher. 43 | /// 44 | /// - `skippedResources`: An array of resources that are already cached before the prefetching starting. 45 | /// - `failedResources`: An array of resources that fail to be downloaded. It could because of being cancelled while downloading, encountered an error when downloading or the download not being started at all. 46 | /// - `completedResources`: An array of resources that are downloaded and cached successfully. 47 | public typealias PrefetcherCompletionHandler = ((_ skippedResources: [Resource], _ failedResources: [Resource], _ completedResources: [Resource]) -> ()) 48 | 49 | /// `ImagePrefetcher` represents a downloading manager for requesting many images via URLs, then caching them. 50 | /// This is useful when you know a list of image resources and want to download them before showing. 51 | public class ImagePrefetcher { 52 | 53 | /// The maximum concurrent downloads to use when prefetching images. Default is 5. 54 | public var maxConcurrentDownloads = 5 55 | 56 | private let prefetchResources: [Resource] 57 | private let optionsInfo: KingfisherOptionsInfo 58 | private var progressBlock: PrefetcherProgressBlock? 59 | private var completionHandler: PrefetcherCompletionHandler? 60 | 61 | private var tasks = [URL: RetrieveImageDownloadTask]() 62 | 63 | private var pendingResources: ArraySlice 64 | private var skippedResources = [Resource]() 65 | private var completedResources = [Resource]() 66 | private var failedResources = [Resource]() 67 | 68 | private var stopped = false 69 | 70 | // The created manager used for prefetch. We will use the helper method in manager. 71 | private let manager: KingfisherManager 72 | 73 | private var finished: Bool { 74 | return failedResources.count + skippedResources.count + completedResources.count == prefetchResources.count && self.tasks.isEmpty 75 | } 76 | 77 | /** 78 | Init an image prefetcher with an array of URLs. 79 | 80 | The prefetcher should be initiated with a list of prefetching targets. The URLs list is immutable. 81 | After you get a valid `ImagePrefetcher` object, you could call `start()` on it to begin the prefetching process. 82 | The images already cached will be skipped without downloading again. 83 | 84 | - parameter urls: The URLs which should be prefetched. 85 | - parameter options: A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more. 86 | - parameter progressBlock: Called every time an resource is downloaded, skipped or cancelled. 87 | - parameter completionHandler: Called when the whole prefetching process finished. 88 | 89 | - returns: An `ImagePrefetcher` object. 90 | 91 | - Note: By default, the `ImageDownloader.defaultDownloader` and `ImageCache.defaultCache` will be used as 92 | the downloader and cache target respectively. You can specify another downloader or cache by using a customized `KingfisherOptionsInfo`. 93 | Both the progress and completion block will be invoked in main thread. The `CallbackDispatchQueue` in `optionsInfo` will be ignored in this method. 94 | */ 95 | public convenience init(urls: [URL], 96 | options: KingfisherOptionsInfo? = nil, 97 | progressBlock: PrefetcherProgressBlock? = nil, 98 | completionHandler: PrefetcherCompletionHandler? = nil) 99 | { 100 | let resources: [Resource] = urls.map { $0 } 101 | self.init(resources: resources, options: options, progressBlock: progressBlock, completionHandler: completionHandler) 102 | } 103 | 104 | /** 105 | Init an image prefetcher with an array of resources. 106 | 107 | The prefetcher should be initiated with a list of prefetching targets. The resources list is immutable. 108 | After you get a valid `ImagePrefetcher` object, you could call `start()` on it to begin the prefetching process. 109 | The images already cached will be skipped without downloading again. 110 | 111 | - parameter resources: The resources which should be prefetched. See `Resource` type for more. 112 | - parameter options: A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more. 113 | - parameter progressBlock: Called every time an resource is downloaded, skipped or cancelled. 114 | - parameter completionHandler: Called when the whole prefetching process finished. 115 | 116 | - returns: An `ImagePrefetcher` object. 117 | 118 | - Note: By default, the `ImageDownloader.defaultDownloader` and `ImageCache.defaultCache` will be used as 119 | the downloader and cache target respectively. You can specify another downloader or cache by using a customized `KingfisherOptionsInfo`. 120 | Both the progress and completion block will be invoked in main thread. The `CallbackDispatchQueue` in `optionsInfo` will be ignored in this method. 121 | */ 122 | public init(resources: [Resource], 123 | options: KingfisherOptionsInfo? = nil, 124 | progressBlock: PrefetcherProgressBlock? = nil, 125 | completionHandler: PrefetcherCompletionHandler? = nil) 126 | { 127 | prefetchResources = resources 128 | pendingResources = ArraySlice(resources) 129 | 130 | // We want all callbacks from main queue, so we ignore the call back queue in options 131 | let optionsInfoWithoutQueue = options?.removeAllMatchesIgnoringAssociatedValue(.callbackDispatchQueue(nil)) 132 | self.optionsInfo = optionsInfoWithoutQueue ?? KingfisherEmptyOptionsInfo 133 | 134 | let cache = self.optionsInfo.targetCache 135 | let downloader = self.optionsInfo.downloader 136 | manager = KingfisherManager(downloader: downloader, cache: cache) 137 | 138 | self.progressBlock = progressBlock 139 | self.completionHandler = completionHandler 140 | } 141 | 142 | /** 143 | Start to download the resources and cache them. This can be useful for background downloading 144 | of assets that are required for later use in an app. This code will not try and update any UI 145 | with the results of the process. 146 | */ 147 | public func start() 148 | { 149 | // Since we want to handle the resources cancellation in main thread only. 150 | DispatchQueue.main.safeAsync { 151 | 152 | guard !self.stopped else { 153 | assertionFailure("You can not restart the same prefetcher. Try to create a new prefetcher.") 154 | self.handleComplete() 155 | return 156 | } 157 | 158 | guard self.maxConcurrentDownloads > 0 else { 159 | assertionFailure("There should be concurrent downloads value should be at least 1.") 160 | self.handleComplete() 161 | return 162 | } 163 | 164 | guard self.prefetchResources.count > 0 else { 165 | self.handleComplete() 166 | return 167 | } 168 | 169 | let initialConcurentDownloads = min(self.prefetchResources.count, self.maxConcurrentDownloads) 170 | for _ in 0 ..< initialConcurentDownloads { 171 | if let resource = self.pendingResources.popFirst() { 172 | self.startPrefetching(resource) 173 | } 174 | } 175 | } 176 | } 177 | 178 | 179 | /** 180 | Stop current downloading progress, and cancel any future prefetching activity that might be occuring. 181 | */ 182 | public func stop() { 183 | DispatchQueue.main.safeAsync { 184 | 185 | if self.finished { return } 186 | 187 | self.stopped = true 188 | self.tasks.forEach { (_, task) -> () in 189 | task.cancel() 190 | } 191 | } 192 | } 193 | 194 | func downloadAndCache(_ resource: Resource) { 195 | 196 | let downloadTaskCompletionHandler: CompletionHandler = { (image, error, _, _) -> () in 197 | self.tasks.removeValue(forKey: resource.downloadURL) 198 | if let _ = error { 199 | self.failedResources.append(resource) 200 | } else { 201 | self.completedResources.append(resource) 202 | } 203 | 204 | self.reportProgress() 205 | if self.stopped { 206 | if self.tasks.isEmpty { 207 | self.failedResources.append(contentsOf: self.pendingResources) 208 | self.handleComplete() 209 | } 210 | } else { 211 | self.reportCompletionOrStartNext() 212 | } 213 | } 214 | 215 | let downloadTask = manager.downloadAndCacheImage( 216 | with: resource.downloadURL, 217 | forKey: resource.cacheKey, 218 | retrieveImageTask: RetrieveImageTask(), 219 | progressBlock: nil, 220 | completionHandler: downloadTaskCompletionHandler, 221 | options: optionsInfo) 222 | 223 | if let downloadTask = downloadTask { 224 | tasks[resource.downloadURL] = downloadTask 225 | } 226 | } 227 | 228 | func append(cached resource: Resource) { 229 | skippedResources.append(resource) 230 | 231 | reportProgress() 232 | reportCompletionOrStartNext() 233 | } 234 | 235 | func startPrefetching(_ resource: Resource) 236 | { 237 | if optionsInfo.forceRefresh { 238 | downloadAndCache(resource) 239 | } else { 240 | let alreadyInCache = manager.cache.isImageCached(forKey: resource.cacheKey, 241 | processorIdentifier: optionsInfo.processor.identifier).cached 242 | 243 | if alreadyInCache { 244 | append(cached: resource) 245 | } else { 246 | downloadAndCache(resource) 247 | } 248 | } 249 | } 250 | 251 | func reportProgress() { 252 | progressBlock?(skippedResources, failedResources, completedResources) 253 | } 254 | 255 | func reportCompletionOrStartNext() { 256 | if let resource = pendingResources.popFirst() { 257 | startPrefetching(resource) 258 | } else { 259 | guard tasks.isEmpty else { return } 260 | handleComplete() 261 | } 262 | } 263 | 264 | func handleComplete() { 265 | completionHandler?(skippedResources, failedResources, completedResources) 266 | completionHandler = nil 267 | progressBlock = nil 268 | } 269 | } 270 | -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/Pods/Kingfisher/Sources/ImageTransition.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ImageTransition.swift 3 | // Kingfisher 4 | // 5 | // Created by Wei Wang on 15/9/18. 6 | // 7 | // Copyright (c) 2017 Wei Wang 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #if os(macOS) 28 | // Not implemented for macOS and watchOS yet. 29 | 30 | import AppKit 31 | 32 | /// Image transition is not supported on macOS. 33 | public enum ImageTransition { 34 | case none 35 | var duration: TimeInterval { 36 | return 0 37 | } 38 | } 39 | 40 | #elseif os(watchOS) 41 | import UIKit 42 | /// Image transition is not supported on watchOS. 43 | public enum ImageTransition { 44 | case none 45 | var duration: TimeInterval { 46 | return 0 47 | } 48 | } 49 | #else 50 | import UIKit 51 | 52 | /** 53 | Transition effect which will be used when an image downloaded and set by `UIImageView` extension API in Kingfisher. 54 | You can assign an enum value with transition duration as an item in `KingfisherOptionsInfo` 55 | to enable the animation transition. 56 | 57 | Apple's UIViewAnimationOptions is used under the hood. 58 | For custom transition, you should specified your own transition options, animations and 59 | comletion handler as well. 60 | */ 61 | public enum ImageTransition { 62 | /// No animation transistion. 63 | case none 64 | 65 | /// Fade in the loaded image. 66 | case fade(TimeInterval) 67 | 68 | /// Flip from left transition. 69 | case flipFromLeft(TimeInterval) 70 | 71 | /// Flip from right transition. 72 | case flipFromRight(TimeInterval) 73 | 74 | /// Flip from top transition. 75 | case flipFromTop(TimeInterval) 76 | 77 | /// Flip from bottom transition. 78 | case flipFromBottom(TimeInterval) 79 | 80 | /// Custom transition. 81 | case custom(duration: TimeInterval, 82 | options: UIViewAnimationOptions, 83 | animations: ((UIImageView, UIImage) -> Void)?, 84 | completion: ((Bool) -> Void)?) 85 | 86 | var duration: TimeInterval { 87 | switch self { 88 | case .none: return 0 89 | case .fade(let duration): return duration 90 | 91 | case .flipFromLeft(let duration): return duration 92 | case .flipFromRight(let duration): return duration 93 | case .flipFromTop(let duration): return duration 94 | case .flipFromBottom(let duration): return duration 95 | 96 | case .custom(let duration, _, _, _): return duration 97 | } 98 | } 99 | 100 | var animationOptions: UIViewAnimationOptions { 101 | switch self { 102 | case .none: return [] 103 | case .fade(_): return .transitionCrossDissolve 104 | 105 | case .flipFromLeft(_): return .transitionFlipFromLeft 106 | case .flipFromRight(_): return .transitionFlipFromRight 107 | case .flipFromTop(_): return .transitionFlipFromTop 108 | case .flipFromBottom(_): return .transitionFlipFromBottom 109 | 110 | case .custom(_, let options, _, _): return options 111 | } 112 | } 113 | 114 | var animations: ((UIImageView, UIImage) -> Void)? { 115 | switch self { 116 | case .custom(_, _, let animations, _): return animations 117 | default: return { $0.image = $1 } 118 | } 119 | } 120 | 121 | var completion: ((Bool) -> Void)? { 122 | switch self { 123 | case .custom(_, _, _, let completion): return completion 124 | default: return nil 125 | } 126 | } 127 | } 128 | #endif 129 | -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/Pods/Kingfisher/Sources/Indicator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Indicator.swift 3 | // Kingfisher 4 | // 5 | // Created by João D. Moreira on 30/08/16. 6 | // 7 | // Copyright (c) 2017 Wei Wang 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #if os(macOS) 28 | import AppKit 29 | #else 30 | import UIKit 31 | #endif 32 | 33 | #if os(macOS) 34 | public typealias IndicatorView = NSView 35 | #else 36 | public typealias IndicatorView = UIView 37 | #endif 38 | 39 | public enum IndicatorType { 40 | /// No indicator. 41 | case none 42 | /// Use system activity indicator. 43 | case activity 44 | /// Use an image as indicator. GIF is supported. 45 | case image(imageData: Data) 46 | /// Use a custom indicator, which conforms to the `Indicator` protocol. 47 | case custom(indicator: Indicator) 48 | } 49 | 50 | // MARK: - Indicator Protocol 51 | public protocol Indicator { 52 | func startAnimatingView() 53 | func stopAnimatingView() 54 | 55 | var viewCenter: CGPoint { get set } 56 | var view: IndicatorView { get } 57 | } 58 | 59 | extension Indicator { 60 | #if os(macOS) 61 | public var viewCenter: CGPoint { 62 | get { 63 | let frame = view.frame 64 | return CGPoint(x: frame.origin.x + frame.size.width / 2.0, y: frame.origin.y + frame.size.height / 2.0 ) 65 | } 66 | set { 67 | let frame = view.frame 68 | let newFrame = CGRect(x: newValue.x - frame.size.width / 2.0, 69 | y: newValue.y - frame.size.height / 2.0, 70 | width: frame.size.width, 71 | height: frame.size.height) 72 | view.frame = newFrame 73 | } 74 | } 75 | #else 76 | public var viewCenter: CGPoint { 77 | get { 78 | return view.center 79 | } 80 | set { 81 | view.center = newValue 82 | } 83 | } 84 | #endif 85 | } 86 | 87 | // MARK: - ActivityIndicator 88 | // Displays a NSProgressIndicator / UIActivityIndicatorView 89 | struct ActivityIndicator: Indicator { 90 | 91 | #if os(macOS) 92 | private let activityIndicatorView: NSProgressIndicator 93 | #else 94 | private let activityIndicatorView: UIActivityIndicatorView 95 | #endif 96 | 97 | var view: IndicatorView { 98 | return activityIndicatorView 99 | } 100 | 101 | func startAnimatingView() { 102 | #if os(macOS) 103 | activityIndicatorView.startAnimation(nil) 104 | #else 105 | activityIndicatorView.startAnimating() 106 | #endif 107 | activityIndicatorView.isHidden = false 108 | } 109 | 110 | func stopAnimatingView() { 111 | #if os(macOS) 112 | activityIndicatorView.stopAnimation(nil) 113 | #else 114 | activityIndicatorView.stopAnimating() 115 | #endif 116 | activityIndicatorView.isHidden = true 117 | } 118 | 119 | init() { 120 | #if os(macOS) 121 | activityIndicatorView = NSProgressIndicator(frame: CGRect(x: 0, y: 0, width: 16, height: 16)) 122 | activityIndicatorView.controlSize = .small 123 | activityIndicatorView.style = .spinningStyle 124 | #else 125 | #if os(tvOS) 126 | let indicatorStyle = UIActivityIndicatorViewStyle.white 127 | #else 128 | let indicatorStyle = UIActivityIndicatorViewStyle.gray 129 | #endif 130 | activityIndicatorView = UIActivityIndicatorView(activityIndicatorStyle:indicatorStyle) 131 | activityIndicatorView.autoresizingMask = [.flexibleLeftMargin, .flexibleRightMargin, .flexibleBottomMargin, .flexibleTopMargin] 132 | #endif 133 | } 134 | } 135 | 136 | // MARK: - ImageIndicator 137 | // Displays an ImageView. Supports gif 138 | struct ImageIndicator: Indicator { 139 | private let animatedImageIndicatorView: ImageView 140 | 141 | var view: IndicatorView { 142 | return animatedImageIndicatorView 143 | } 144 | 145 | init?(imageData data: Data, processor: ImageProcessor = DefaultImageProcessor.default, options: KingfisherOptionsInfo = KingfisherEmptyOptionsInfo) { 146 | 147 | var options = options 148 | // Use normal image view to show gif, so we need to preload all gif data. 149 | if !options.preloadAllGIFData { 150 | options.append(.preloadAllGIFData) 151 | } 152 | 153 | guard let image = processor.process(item: .data(data), options: options) else { 154 | return nil 155 | } 156 | 157 | animatedImageIndicatorView = ImageView() 158 | animatedImageIndicatorView.image = image 159 | 160 | #if os(macOS) 161 | // Need for gif to animate on macOS 162 | self.animatedImageIndicatorView.imageScaling = .scaleNone 163 | self.animatedImageIndicatorView.canDrawSubviewsIntoLayer = true 164 | #else 165 | animatedImageIndicatorView.contentMode = .center 166 | 167 | animatedImageIndicatorView.autoresizingMask = [.flexibleLeftMargin, 168 | .flexibleRightMargin, 169 | .flexibleBottomMargin, 170 | .flexibleTopMargin] 171 | #endif 172 | } 173 | 174 | func startAnimatingView() { 175 | #if os(macOS) 176 | animatedImageIndicatorView.animates = true 177 | #else 178 | animatedImageIndicatorView.startAnimating() 179 | #endif 180 | animatedImageIndicatorView.isHidden = false 181 | } 182 | 183 | func stopAnimatingView() { 184 | #if os(macOS) 185 | animatedImageIndicatorView.animates = false 186 | #else 187 | animatedImageIndicatorView.stopAnimating() 188 | #endif 189 | animatedImageIndicatorView.isHidden = true 190 | } 191 | } 192 | -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/Pods/Kingfisher/Sources/Kingfisher.h: -------------------------------------------------------------------------------- 1 | // 2 | // Kingfisher.h 3 | // Kingfisher 4 | // 5 | // Created by Wei Wang on 15/4/6. 6 | // 7 | // Copyright (c) 2017 Wei Wang 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #import 28 | 29 | //! Project version number for Kingfisher. 30 | FOUNDATION_EXPORT double KingfisherVersionNumber; 31 | 32 | //! Project version string for Kingfisher. 33 | FOUNDATION_EXPORT const unsigned char KingfisherVersionString[]; 34 | 35 | // In this header, you should import all the public headers of your framework using statements like #import 36 | 37 | 38 | -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/Pods/Kingfisher/Sources/Kingfisher.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Kingfisher.swift 3 | // Kingfisher 4 | // 5 | // Created by Wei Wang on 16/9/14. 6 | // 7 | // Copyright (c) 2017 Wei Wang 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | import Foundation 28 | import ImageIO 29 | 30 | #if os(macOS) 31 | import AppKit 32 | public typealias Image = NSImage 33 | public typealias Color = NSColor 34 | public typealias ImageView = NSImageView 35 | typealias Button = NSButton 36 | #else 37 | import UIKit 38 | public typealias Image = UIImage 39 | public typealias Color = UIColor 40 | #if !os(watchOS) 41 | public typealias ImageView = UIImageView 42 | typealias Button = UIButton 43 | #endif 44 | #endif 45 | 46 | public final class Kingfisher { 47 | public let base: Base 48 | public init(_ base: Base) { 49 | self.base = base 50 | } 51 | } 52 | 53 | /** 54 | A type that has Kingfisher extensions. 55 | */ 56 | public protocol KingfisherCompatible { 57 | associatedtype CompatibleType 58 | var kf: CompatibleType { get } 59 | } 60 | 61 | public extension KingfisherCompatible { 62 | public var kf: Kingfisher { 63 | get { return Kingfisher(self) } 64 | } 65 | } 66 | 67 | extension Image: KingfisherCompatible { } 68 | #if !os(watchOS) 69 | extension ImageView: KingfisherCompatible { } 70 | extension Button: KingfisherCompatible { } 71 | #endif 72 | -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/Pods/Kingfisher/Sources/KingfisherManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // KingfisherManager.swift 3 | // Kingfisher 4 | // 5 | // Created by Wei Wang on 15/4/6. 6 | // 7 | // Copyright (c) 2017 Wei Wang 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #if os(macOS) 28 | import AppKit 29 | #else 30 | import UIKit 31 | #endif 32 | 33 | public typealias DownloadProgressBlock = ((_ receivedSize: Int64, _ totalSize: Int64) -> ()) 34 | public typealias CompletionHandler = ((_ image: Image?, _ error: NSError?, _ cacheType: CacheType, _ imageURL: URL?) -> ()) 35 | 36 | /// RetrieveImageTask represents a task of image retrieving process. 37 | /// It contains an async task of getting image from disk and from network. 38 | public class RetrieveImageTask { 39 | 40 | public static let empty = RetrieveImageTask() 41 | 42 | // If task is canceled before the download task started (which means the `downloadTask` is nil), 43 | // the download task should not begin. 44 | var cancelledBeforeDownloadStarting: Bool = false 45 | 46 | /// The disk retrieve task in this image task. Kingfisher will try to look up in cache first. This task represent the cache search task. 47 | @available(*, deprecated, 48 | message: "diskRetrieveTask is not in use anymore. You cannot cancel a disk retrieve task anymore once it started.") 49 | public var diskRetrieveTask: RetrieveImageDiskTask? 50 | 51 | /// The network retrieve task in this image task. 52 | public var downloadTask: RetrieveImageDownloadTask? 53 | 54 | /** 55 | Cancel current task. If this task is already done, do nothing. 56 | */ 57 | public func cancel() { 58 | if let downloadTask = downloadTask { 59 | downloadTask.cancel() 60 | } else { 61 | cancelledBeforeDownloadStarting = true 62 | } 63 | } 64 | } 65 | 66 | /// Error domain of Kingfisher 67 | public let KingfisherErrorDomain = "com.onevcat.Kingfisher.Error" 68 | 69 | /// Main manager class of Kingfisher. It connects Kingfisher downloader and cache. 70 | /// You can use this class to retrieve an image via a specified URL from web or cache. 71 | public class KingfisherManager { 72 | 73 | /// Shared manager used by the extensions across Kingfisher. 74 | public static let shared = KingfisherManager() 75 | 76 | /// Cache used by this manager 77 | public var cache: ImageCache 78 | 79 | /// Downloader used by this manager 80 | public var downloader: ImageDownloader 81 | 82 | convenience init() { 83 | self.init(downloader: .default, cache: .default) 84 | } 85 | 86 | init(downloader: ImageDownloader, cache: ImageCache) { 87 | self.downloader = downloader 88 | self.cache = cache 89 | } 90 | 91 | /** 92 | Get an image with resource. 93 | If KingfisherOptions.None is used as `options`, Kingfisher will seek the image in memory and disk first. 94 | If not found, it will download the image at `resource.downloadURL` and cache it with `resource.cacheKey`. 95 | These default behaviors could be adjusted by passing different options. See `KingfisherOptions` for more. 96 | 97 | - parameter resource: Resource object contains information such as `cacheKey` and `downloadURL`. 98 | - parameter options: A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more. 99 | - parameter progressBlock: Called every time downloaded data changed. This could be used as a progress UI. 100 | - parameter completionHandler: Called when the whole retrieving process finished. 101 | 102 | - returns: A `RetrieveImageTask` task object. You can use this object to cancel the task. 103 | */ 104 | @discardableResult 105 | public func retrieveImage(with resource: Resource, 106 | options: KingfisherOptionsInfo?, 107 | progressBlock: DownloadProgressBlock?, 108 | completionHandler: CompletionHandler?) -> RetrieveImageTask 109 | { 110 | let task = RetrieveImageTask() 111 | 112 | if let options = options, options.forceRefresh { 113 | _ = downloadAndCacheImage( 114 | with: resource.downloadURL, 115 | forKey: resource.cacheKey, 116 | retrieveImageTask: task, 117 | progressBlock: progressBlock, 118 | completionHandler: completionHandler, 119 | options: options) 120 | } else { 121 | tryToRetrieveImageFromCache( 122 | forKey: resource.cacheKey, 123 | with: resource.downloadURL, 124 | retrieveImageTask: task, 125 | progressBlock: progressBlock, 126 | completionHandler: completionHandler, 127 | options: options) 128 | } 129 | 130 | return task 131 | } 132 | 133 | @discardableResult 134 | func downloadAndCacheImage(with url: URL, 135 | forKey key: String, 136 | retrieveImageTask: RetrieveImageTask, 137 | progressBlock: DownloadProgressBlock?, 138 | completionHandler: CompletionHandler?, 139 | options: KingfisherOptionsInfo?) -> RetrieveImageDownloadTask? 140 | { 141 | let options = options ?? KingfisherEmptyOptionsInfo 142 | let downloader = options.downloader 143 | return downloader.downloadImage(with: url, retrieveImageTask: retrieveImageTask, options: options, 144 | progressBlock: { receivedSize, totalSize in 145 | progressBlock?(receivedSize, totalSize) 146 | }, 147 | completionHandler: { image, error, imageURL, originalData in 148 | 149 | let targetCache = options.targetCache 150 | if let error = error, error.code == KingfisherError.notModified.rawValue { 151 | // Not modified. Try to find the image from cache. 152 | // (The image should be in cache. It should be guaranteed by the framework users.) 153 | targetCache.retrieveImage(forKey: key, options: options, completionHandler: { (cacheImage, cacheType) -> () in 154 | completionHandler?(cacheImage, nil, cacheType, url) 155 | }) 156 | return 157 | } 158 | 159 | if let image = image, let originalData = originalData { 160 | targetCache.store(image, 161 | original: originalData, 162 | forKey: key, 163 | processorIdentifier:options.processor.identifier, 164 | cacheSerializer: options.cacheSerializer, 165 | toDisk: !options.cacheMemoryOnly, 166 | completionHandler: nil) 167 | } 168 | 169 | completionHandler?(image, error, .none, url) 170 | 171 | }) 172 | } 173 | 174 | func tryToRetrieveImageFromCache(forKey key: String, 175 | with url: URL, 176 | retrieveImageTask: RetrieveImageTask, 177 | progressBlock: DownloadProgressBlock?, 178 | completionHandler: CompletionHandler?, 179 | options: KingfisherOptionsInfo?) 180 | { 181 | let diskTaskCompletionHandler: CompletionHandler = { (image, error, cacheType, imageURL) -> () in 182 | completionHandler?(image, error, cacheType, imageURL) 183 | } 184 | 185 | let targetCache = options?.targetCache ?? cache 186 | targetCache.retrieveImage(forKey: key, options: options, 187 | completionHandler: { image, cacheType in 188 | if image != nil { 189 | diskTaskCompletionHandler(image, nil, cacheType, url) 190 | } else if let options = options, options.onlyFromCache { 191 | let error = NSError(domain: KingfisherErrorDomain, code: KingfisherError.notCached.rawValue, userInfo: nil) 192 | diskTaskCompletionHandler(nil, error, .none, url) 193 | } else { 194 | self.downloadAndCacheImage( 195 | with: url, 196 | forKey: key, 197 | retrieveImageTask: retrieveImageTask, 198 | progressBlock: progressBlock, 199 | completionHandler: diskTaskCompletionHandler, 200 | options: options) 201 | } 202 | } 203 | ) 204 | } 205 | } 206 | -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/Pods/Kingfisher/Sources/KingfisherOptionsInfo.swift: -------------------------------------------------------------------------------- 1 | // 2 | // KingfisherOptionsInfo.swift 3 | // Kingfisher 4 | // 5 | // Created by Wei Wang on 15/4/23. 6 | // 7 | // Copyright (c) 2017 Wei Wang 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #if os(macOS) 28 | import AppKit 29 | #else 30 | import UIKit 31 | #endif 32 | 33 | 34 | /** 35 | * KingfisherOptionsInfo is a typealias for [KingfisherOptionsInfoItem]. You can use the enum of option item with value to control some behaviors of Kingfisher. 36 | */ 37 | public typealias KingfisherOptionsInfo = [KingfisherOptionsInfoItem] 38 | let KingfisherEmptyOptionsInfo = [KingfisherOptionsInfoItem]() 39 | 40 | /** 41 | Items could be added into KingfisherOptionsInfo. 42 | */ 43 | public enum KingfisherOptionsInfoItem { 44 | /// The associated value of this member should be an ImageCache object. Kingfisher will use the specified 45 | /// cache object when handling related operations, including trying to retrieve the cached images and store 46 | /// the downloaded image to it. 47 | case targetCache(ImageCache) 48 | 49 | /// The associated value of this member should be an ImageDownloader object. Kingfisher will use this 50 | /// downloader to download the images. 51 | case downloader(ImageDownloader) 52 | 53 | /// Member for animation transition when using UIImageView. Kingfisher will use the `ImageTransition` of 54 | /// this enum to animate the image in if it is downloaded from web. The transition will not happen when the 55 | /// image is retrieved from either memory or disk cache by default. If you need to do the transition even when 56 | /// the image being retrieved from cache, set `ForceTransition` as well. 57 | case transition(ImageTransition) 58 | 59 | /// Associated `Float` value will be set as the priority of image download task. The value for it should be 60 | /// between 0.0~1.0. If this option not set, the default value (`NSURLSessionTaskPriorityDefault`) will be used. 61 | case downloadPriority(Float) 62 | 63 | /// If set, `Kingfisher` will ignore the cache and try to fire a download task for the resource. 64 | case forceRefresh 65 | 66 | /// If set, setting the image to an image view will happen with transition even when retrieved from cache. 67 | /// See `Transition` option for more. 68 | case forceTransition 69 | 70 | /// If set, `Kingfisher` will only cache the value in memory but not in disk. 71 | case cacheMemoryOnly 72 | 73 | /// If set, `Kingfisher` will only try to retrieve the image from cache not from network. 74 | case onlyFromCache 75 | 76 | /// Decode the image in background thread before using. 77 | case backgroundDecode 78 | 79 | /// The associated value of this member will be used as the target queue of dispatch callbacks when 80 | /// retrieving images from cache. If not set, `Kingfisher` will use main quese for callbacks. 81 | case callbackDispatchQueue(DispatchQueue?) 82 | 83 | /// The associated value of this member will be used as the scale factor when converting retrieved data to an image. 84 | /// It is the image scale, instead of your screen scale. You may need to specify the correct scale when you dealing 85 | /// with 2x or 3x retina images. 86 | case scaleFactor(CGFloat) 87 | 88 | /// Whether all the GIF data should be preloaded. Default it false, which means following frames will be 89 | /// loaded on need. If true, all the GIF data will be loaded and decoded into memory. This option is mainly 90 | /// used for back compatibility internally. You should not set it directly. `AnimatedImageView` will not preload 91 | /// all data, while a normal image view (`UIImageView` or `NSImageView`) will load all data. Choose to use 92 | /// corresponding image view type instead of setting this option. 93 | case preloadAllGIFData 94 | 95 | /// The `ImageDownloadRequestModifier` contained will be used to change the request before it being sent. 96 | /// This is the last chance you can modify the request. You can modify the request for some customizing purpose, 97 | /// such as adding auth token to the header, do basic HTTP auth or something like url mapping. The original request 98 | /// will be sent without any modification by default. 99 | case requestModifier(ImageDownloadRequestModifier) 100 | 101 | /// Processor for processing when the downloading finishes, a processor will convert the downloaded data to an image 102 | /// and/or apply some filter on it. If a cache is connected to the downloader (it happenes when you are using 103 | /// KingfisherManager or the image extension methods), the converted image will also be sent to cache as well as the 104 | /// image view. `DefaultImageProcessor.default` will be used by default. 105 | case processor(ImageProcessor) 106 | 107 | /// Supply an `CacheSerializer` to convert some data to an image object for 108 | /// retrieving from disk cache or vice versa for storing to disk cache. 109 | /// `DefaultCacheSerializer.default` will be used by default. 110 | case cacheSerializer(CacheSerializer) 111 | 112 | /// Keep the existing image while setting another image to an image view. 113 | /// By setting this option, the placeholder image parameter of imageview extension method 114 | /// will be ignored and the current image will be kept while loading or downloading the new image. 115 | case keepCurrentImageWhileLoading 116 | 117 | /// If set, Kingfisher will only load the first frame from a GIF file as a single image. 118 | /// Loading a lot of GIFs may take too much memory. It will be useful when you want to display a 119 | /// static preview of the first frame from a GIF image. 120 | /// This option will be ignored if the target image is not GIF. 121 | case onlyLoadFirstFrame 122 | } 123 | 124 | precedencegroup ItemComparisonPrecedence { 125 | associativity: none 126 | higherThan: LogicalConjunctionPrecedence 127 | } 128 | 129 | infix operator <== : ItemComparisonPrecedence 130 | 131 | // This operator returns true if two `KingfisherOptionsInfoItem` enum is the same, without considering the associated values. 132 | func <== (lhs: KingfisherOptionsInfoItem, rhs: KingfisherOptionsInfoItem) -> Bool { 133 | switch (lhs, rhs) { 134 | case (.targetCache(_), .targetCache(_)): return true 135 | case (.downloader(_), .downloader(_)): return true 136 | case (.transition(_), .transition(_)): return true 137 | case (.downloadPriority(_), .downloadPriority(_)): return true 138 | case (.forceRefresh, .forceRefresh): return true 139 | case (.forceTransition, .forceTransition): return true 140 | case (.cacheMemoryOnly, .cacheMemoryOnly): return true 141 | case (.onlyFromCache, .onlyFromCache): return true 142 | case (.backgroundDecode, .backgroundDecode): return true 143 | case (.callbackDispatchQueue(_), .callbackDispatchQueue(_)): return true 144 | case (.scaleFactor(_), .scaleFactor(_)): return true 145 | case (.preloadAllGIFData, .preloadAllGIFData): return true 146 | case (.requestModifier(_), .requestModifier(_)): return true 147 | case (.processor(_), .processor(_)): return true 148 | case (.cacheSerializer(_), .cacheSerializer(_)): return true 149 | case (.keepCurrentImageWhileLoading, .keepCurrentImageWhileLoading): return true 150 | case (.onlyLoadFirstFrame, .onlyLoadFirstFrame): return true 151 | default: return false 152 | } 153 | } 154 | 155 | extension Collection where Iterator.Element == KingfisherOptionsInfoItem { 156 | func firstMatchIgnoringAssociatedValue(_ target: Iterator.Element) -> Iterator.Element? { 157 | return index { $0 <== target }.flatMap { self[$0] } 158 | } 159 | 160 | func removeAllMatchesIgnoringAssociatedValue(_ target: Iterator.Element) -> [Iterator.Element] { 161 | return self.filter { !($0 <== target) } 162 | } 163 | } 164 | 165 | public extension Collection where Iterator.Element == KingfisherOptionsInfoItem { 166 | /// The target `ImageCache` which is used. 167 | public var targetCache: ImageCache { 168 | if let item = firstMatchIgnoringAssociatedValue(.targetCache(.default)), 169 | case .targetCache(let cache) = item 170 | { 171 | return cache 172 | } 173 | return ImageCache.default 174 | } 175 | 176 | /// The `ImageDownloader` which is specified. 177 | public var downloader: ImageDownloader { 178 | if let item = firstMatchIgnoringAssociatedValue(.downloader(.default)), 179 | case .downloader(let downloader) = item 180 | { 181 | return downloader 182 | } 183 | return ImageDownloader.default 184 | } 185 | 186 | /// Member for animation transition when using UIImageView. 187 | public var transition: ImageTransition { 188 | if let item = firstMatchIgnoringAssociatedValue(.transition(.none)), 189 | case .transition(let transition) = item 190 | { 191 | return transition 192 | } 193 | return ImageTransition.none 194 | } 195 | 196 | /// A `Float` value set as the priority of image download task. The value for it should be 197 | /// between 0.0~1.0. 198 | public var downloadPriority: Float { 199 | if let item = firstMatchIgnoringAssociatedValue(.downloadPriority(0)), 200 | case .downloadPriority(let priority) = item 201 | { 202 | return priority 203 | } 204 | return URLSessionTask.defaultPriority 205 | } 206 | 207 | /// Whether an image will be always downloaded again or not. 208 | public var forceRefresh: Bool { 209 | return contains{ $0 <== .forceRefresh } 210 | } 211 | 212 | /// Whether the transition should always happen or not. 213 | public var forceTransition: Bool { 214 | return contains{ $0 <== .forceTransition } 215 | } 216 | 217 | /// Whether cache the image only in memory or not. 218 | public var cacheMemoryOnly: Bool { 219 | return contains{ $0 <== .cacheMemoryOnly } 220 | } 221 | 222 | /// Whether only load the images from cache or not. 223 | public var onlyFromCache: Bool { 224 | return contains{ $0 <== .onlyFromCache } 225 | } 226 | 227 | /// Whether the image should be decoded in background or not. 228 | public var backgroundDecode: Bool { 229 | return contains{ $0 <== .backgroundDecode } 230 | } 231 | 232 | /// Whether the image data should be all loaded at once if it is a GIF. 233 | public var preloadAllGIFData: Bool { 234 | return contains { $0 <== .preloadAllGIFData } 235 | } 236 | 237 | /// The queue of callbacks should happen from Kingfisher. 238 | public var callbackDispatchQueue: DispatchQueue { 239 | if let item = firstMatchIgnoringAssociatedValue(.callbackDispatchQueue(nil)), 240 | case .callbackDispatchQueue(let queue) = item 241 | { 242 | return queue ?? DispatchQueue.main 243 | } 244 | return DispatchQueue.main 245 | } 246 | 247 | /// The scale factor which should be used for the image. 248 | public var scaleFactor: CGFloat { 249 | if let item = firstMatchIgnoringAssociatedValue(.scaleFactor(0)), 250 | case .scaleFactor(let scale) = item 251 | { 252 | return scale 253 | } 254 | return 1.0 255 | } 256 | 257 | /// The `ImageDownloadRequestModifier` will be used before sending a download request. 258 | public var modifier: ImageDownloadRequestModifier { 259 | if let item = firstMatchIgnoringAssociatedValue(.requestModifier(NoModifier.default)), 260 | case .requestModifier(let modifier) = item 261 | { 262 | return modifier 263 | } 264 | return NoModifier.default 265 | } 266 | 267 | /// `ImageProcessor` for processing when the downloading finishes. 268 | public var processor: ImageProcessor { 269 | if let item = firstMatchIgnoringAssociatedValue(.processor(DefaultImageProcessor.default)), 270 | case .processor(let processor) = item 271 | { 272 | return processor 273 | } 274 | return DefaultImageProcessor.default 275 | } 276 | 277 | /// `CacheSerializer` to convert image to data for storing in cache. 278 | public var cacheSerializer: CacheSerializer { 279 | if let item = firstMatchIgnoringAssociatedValue(.cacheSerializer(DefaultCacheSerializer.default)), 280 | case .cacheSerializer(let cacheSerializer) = item 281 | { 282 | return cacheSerializer 283 | } 284 | return DefaultCacheSerializer.default 285 | } 286 | 287 | /// Keep the existing image while setting another image to an image view. 288 | /// Or the placeholder will be used while downloading. 289 | public var keepCurrentImageWhileLoading: Bool { 290 | return contains { $0 <== .keepCurrentImageWhileLoading } 291 | } 292 | 293 | public var onlyLoadFirstFrame: Bool { 294 | return contains { $0 <== .onlyLoadFirstFrame } 295 | } 296 | } 297 | -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/Pods/Kingfisher/Sources/RequestModifier.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RequestModifier.swift 3 | // Kingfisher 4 | // 5 | // Created by Wei Wang on 2016/09/05. 6 | // 7 | // Copyright (c) 2017 Wei Wang 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | import Foundation 28 | 29 | /// Request modifier of image downloader. 30 | public protocol ImageDownloadRequestModifier { 31 | func modified(for request: URLRequest) -> URLRequest? 32 | } 33 | 34 | struct NoModifier: ImageDownloadRequestModifier { 35 | static let `default` = NoModifier() 36 | private init() {} 37 | func modified(for request: URLRequest) -> URLRequest? { 38 | return request 39 | } 40 | } 41 | 42 | public struct AnyModifier: ImageDownloadRequestModifier { 43 | 44 | let block: (URLRequest) -> URLRequest? 45 | 46 | public func modified(for request: URLRequest) -> URLRequest? { 47 | return block(request) 48 | } 49 | 50 | public init(modify: @escaping (URLRequest) -> URLRequest? ) { 51 | block = modify 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/Pods/Kingfisher/Sources/Resource.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Resource.swift 3 | // Kingfisher 4 | // 5 | // Created by Wei Wang on 15/4/6. 6 | // 7 | // Copyright (c) 2017 Wei Wang 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | import Foundation 28 | 29 | 30 | /// `Resource` protocol defines how to download and cache a resource from network. 31 | public protocol Resource { 32 | /// The key used in cache. 33 | var cacheKey: String { get } 34 | 35 | /// The target image URL. 36 | var downloadURL: URL { get } 37 | } 38 | 39 | /** 40 | ImageResource is a simple combination of `downloadURL` and `cacheKey`. 41 | 42 | When passed to image view set methods, Kingfisher will try to download the target 43 | image from the `downloadURL`, and then store it with the `cacheKey` as the key in cache. 44 | */ 45 | public struct ImageResource: Resource { 46 | /// The key used in cache. 47 | public let cacheKey: String 48 | 49 | /// The target image URL. 50 | public let downloadURL: URL 51 | 52 | /** 53 | Create a resource. 54 | 55 | - parameter downloadURL: The target image URL. 56 | - parameter cacheKey: The cache key. If `nil`, Kingfisher will use the `absoluteString` of `downloadURL` as the key. 57 | 58 | - returns: A resource. 59 | */ 60 | public init(downloadURL: URL, cacheKey: String? = nil) { 61 | self.downloadURL = downloadURL 62 | self.cacheKey = cacheKey ?? downloadURL.absoluteString 63 | } 64 | } 65 | 66 | /** 67 | URL conforms to `Resource` in Kingfisher. 68 | The `absoluteString` of this URL is used as `cacheKey`. And the URL itself will be used as `downloadURL`. 69 | If you need customize the url and/or cache key, use `ImageResource` instead. 70 | */ 71 | extension URL: Resource { 72 | public var cacheKey: String { return absoluteString } 73 | public var downloadURL: URL { return self } 74 | } 75 | -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/Pods/Kingfisher/Sources/String+MD5.swift: -------------------------------------------------------------------------------- 1 | // 2 | // String+MD5.swift 3 | // Kingfisher 4 | // 5 | // To date, adding CommonCrypto to a Swift framework is problematic. See: 6 | // http://stackoverflow.com/questions/25248598/importing-commoncrypto-in-a-swift-framework 7 | // We're using a subset and modified version of CryptoSwift as an alternative. 8 | // The following is an altered source version that only includes MD5. The original software can be found at: 9 | // https://github.com/krzyzanowskim/CryptoSwift 10 | // This is the original copyright notice: 11 | 12 | /* 13 | Copyright (C) 2014 Marcin Krzyżanowski 14 | This software is provided 'as-is', without any express or implied warranty. 15 | In no event will the authors be held liable for any damages arising from the use of this software. 16 | Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 17 | - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required. 18 | - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 19 | - This notice may not be removed or altered from any source or binary distribution. 20 | */ 21 | 22 | import Foundation 23 | 24 | public struct StringProxy { 25 | fileprivate let base: String 26 | init(proxy: String) { 27 | base = proxy 28 | } 29 | } 30 | 31 | extension String: KingfisherCompatible { 32 | public typealias CompatibleType = StringProxy 33 | public var kf: CompatibleType { 34 | return StringProxy(proxy: self) 35 | } 36 | } 37 | 38 | extension StringProxy { 39 | var md5: String { 40 | if let data = base.data(using: .utf8, allowLossyConversion: true) { 41 | 42 | let message = data.withUnsafeBytes { bytes -> [UInt8] in 43 | return Array(UnsafeBufferPointer(start: bytes, count: data.count)) 44 | } 45 | 46 | let MD5Calculator = MD5(message) 47 | let MD5Data = MD5Calculator.calculate() 48 | 49 | var MD5String = String() 50 | for c in MD5Data { 51 | MD5String += String(format: "%02x", c) 52 | } 53 | return MD5String 54 | 55 | } else { 56 | return base 57 | } 58 | } 59 | } 60 | 61 | 62 | /** array of bytes, little-endian representation */ 63 | func arrayOfBytes(_ value: T, length: Int? = nil) -> [UInt8] { 64 | let totalBytes = length ?? (MemoryLayout.size * 8) 65 | 66 | let valuePointer = UnsafeMutablePointer.allocate(capacity: 1) 67 | valuePointer.pointee = value 68 | 69 | let bytes = valuePointer.withMemoryRebound(to: UInt8.self, capacity: totalBytes) { (bytesPointer) -> [UInt8] in 70 | var bytes = [UInt8](repeating: 0, count: totalBytes) 71 | for j in 0...size, totalBytes) { 72 | bytes[totalBytes - 1 - j] = (bytesPointer + j).pointee 73 | } 74 | return bytes 75 | } 76 | 77 | valuePointer.deinitialize() 78 | valuePointer.deallocate(capacity: 1) 79 | 80 | return bytes 81 | } 82 | 83 | extension Int { 84 | /** Array of bytes with optional padding (little-endian) */ 85 | func bytes(_ totalBytes: Int = MemoryLayout.size) -> [UInt8] { 86 | return arrayOfBytes(self, length: totalBytes) 87 | } 88 | 89 | } 90 | 91 | extension NSMutableData { 92 | 93 | /** Convenient way to append bytes */ 94 | func appendBytes(_ arrayOfBytes: [UInt8]) { 95 | append(arrayOfBytes, length: arrayOfBytes.count) 96 | } 97 | 98 | } 99 | 100 | protocol HashProtocol { 101 | var message: Array { get } 102 | 103 | /** Common part for hash calculation. Prepare header data. */ 104 | func prepare(_ len: Int) -> Array 105 | } 106 | 107 | extension HashProtocol { 108 | 109 | func prepare(_ len: Int) -> Array { 110 | var tmpMessage = message 111 | 112 | // Step 1. Append Padding Bits 113 | tmpMessage.append(0x80) // append one bit (UInt8 with one bit) to message 114 | 115 | // append "0" bit until message length in bits ≡ 448 (mod 512) 116 | var msgLength = tmpMessage.count 117 | var counter = 0 118 | 119 | while msgLength % len != (len - 8) { 120 | counter += 1 121 | msgLength += 1 122 | } 123 | 124 | tmpMessage += Array(repeating: 0, count: counter) 125 | return tmpMessage 126 | } 127 | } 128 | 129 | func toUInt32Array(_ slice: ArraySlice) -> Array { 130 | var result = Array() 131 | result.reserveCapacity(16) 132 | 133 | for idx in stride(from: slice.startIndex, to: slice.endIndex, by: MemoryLayout.size) { 134 | let d0 = UInt32(slice[idx.advanced(by: 3)]) << 24 135 | let d1 = UInt32(slice[idx.advanced(by: 2)]) << 16 136 | let d2 = UInt32(slice[idx.advanced(by: 1)]) << 8 137 | let d3 = UInt32(slice[idx]) 138 | let val: UInt32 = d0 | d1 | d2 | d3 139 | 140 | result.append(val) 141 | } 142 | return result 143 | } 144 | 145 | struct BytesIterator: IteratorProtocol { 146 | 147 | let chunkSize: Int 148 | let data: [UInt8] 149 | 150 | init(chunkSize: Int, data: [UInt8]) { 151 | self.chunkSize = chunkSize 152 | self.data = data 153 | } 154 | 155 | var offset = 0 156 | 157 | mutating func next() -> ArraySlice? { 158 | let end = min(chunkSize, data.count - offset) 159 | let result = data[offset.. 0 ? result : nil 162 | } 163 | } 164 | 165 | struct BytesSequence: Sequence { 166 | let chunkSize: Int 167 | let data: [UInt8] 168 | 169 | func makeIterator() -> BytesIterator { 170 | return BytesIterator(chunkSize: chunkSize, data: data) 171 | } 172 | } 173 | 174 | func rotateLeft(_ value: UInt32, bits: UInt32) -> UInt32 { 175 | return ((value << bits) & 0xFFFFFFFF) | (value >> (32 - bits)) 176 | } 177 | 178 | class MD5: HashProtocol { 179 | 180 | static let size = 16 // 128 / 8 181 | let message: [UInt8] 182 | 183 | init (_ message: [UInt8]) { 184 | self.message = message 185 | } 186 | 187 | /** specifies the per-round shift amounts */ 188 | private let shifts: [UInt32] = [7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 189 | 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 190 | 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 191 | 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21] 192 | 193 | /** binary integer part of the sines of integers (Radians) */ 194 | private let sines: [UInt32] = [0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, 195 | 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501, 196 | 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, 197 | 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821, 198 | 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, 199 | 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8, 200 | 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, 201 | 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a, 202 | 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, 203 | 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70, 204 | 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x4881d05, 205 | 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665, 206 | 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, 207 | 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1, 208 | 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, 209 | 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391] 210 | 211 | private let hashes: [UInt32] = [0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476] 212 | 213 | func calculate() -> [UInt8] { 214 | var tmpMessage = prepare(64) 215 | tmpMessage.reserveCapacity(tmpMessage.count + 4) 216 | 217 | // hash values 218 | var hh = hashes 219 | 220 | // Step 2. Append Length a 64-bit representation of lengthInBits 221 | let lengthInBits = (message.count * 8) 222 | let lengthBytes = lengthInBits.bytes(64 / 8) 223 | tmpMessage += lengthBytes.reversed() 224 | 225 | // Process the message in successive 512-bit chunks: 226 | let chunkSizeBytes = 512 / 8 // 64 227 | 228 | for chunk in BytesSequence(chunkSize: chunkSizeBytes, data: tmpMessage) { 229 | // break chunk into sixteen 32-bit words M[j], 0 ≤ j ≤ 15 230 | var M = toUInt32Array(chunk) 231 | assert(M.count == 16, "Invalid array") 232 | 233 | // Initialize hash value for this chunk: 234 | var A: UInt32 = hh[0] 235 | var B: UInt32 = hh[1] 236 | var C: UInt32 = hh[2] 237 | var D: UInt32 = hh[3] 238 | 239 | var dTemp: UInt32 = 0 240 | 241 | // Main loop 242 | for j in 0 ..< sines.count { 243 | var g = 0 244 | var F: UInt32 = 0 245 | 246 | switch j { 247 | case 0...15: 248 | F = (B & C) | ((~B) & D) 249 | g = j 250 | break 251 | case 16...31: 252 | F = (D & B) | (~D & C) 253 | g = (5 * j + 1) % 16 254 | break 255 | case 32...47: 256 | F = B ^ C ^ D 257 | g = (3 * j + 5) % 16 258 | break 259 | case 48...63: 260 | F = C ^ (B | (~D)) 261 | g = (7 * j) % 16 262 | break 263 | default: 264 | break 265 | } 266 | dTemp = D 267 | D = C 268 | C = B 269 | B = B &+ rotateLeft((A &+ F &+ sines[j] &+ M[g]), bits: shifts[j]) 270 | A = dTemp 271 | } 272 | 273 | hh[0] = hh[0] &+ A 274 | hh[1] = hh[1] &+ B 275 | hh[2] = hh[2] &+ C 276 | hh[3] = hh[3] &+ D 277 | } 278 | 279 | var result = [UInt8]() 280 | result.reserveCapacity(hh.count / 4) 281 | 282 | hh.forEach { 283 | let itemLE = $0.littleEndian 284 | let r1 = UInt8(itemLE & 0xff) 285 | let r2 = UInt8((itemLE >> 8) & 0xff) 286 | let r3 = UInt8((itemLE >> 16) & 0xff) 287 | let r4 = UInt8((itemLE >> 24) & 0xff) 288 | result += [r1, r2, r3, r4] 289 | } 290 | return result 291 | } 292 | } 293 | -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/Pods/Kingfisher/Sources/ThreadHelper.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ThreadHelper.swift 3 | // Kingfisher 4 | // 5 | // Created by Wei Wang on 15/10/9. 6 | // 7 | // Copyright (c) 2017 Wei Wang 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | import Foundation 28 | 29 | extension DispatchQueue { 30 | // This method will dispatch the `block` to self. 31 | // If `self` is the main queue, and current thread is main thread, the block 32 | // will be invoked immediately instead of being dispatched. 33 | func safeAsync(_ block: @escaping ()->()) { 34 | if self === DispatchQueue.main && Thread.isMainThread { 35 | block() 36 | } else { 37 | async { block() } 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/Pods/Manifest.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Kingfisher (3.6.2) 3 | 4 | DEPENDENCIES: 5 | - Kingfisher 6 | 7 | SPEC CHECKSUMS: 8 | Kingfisher: 2c94e72c6830622c71d06adf4ea024c37d316830 9 | 10 | PODFILE CHECKSUM: 6daf8f1d76fe64163802c6a83d3d697d2866e5e0 11 | 12 | COCOAPODS: 1.2.1 13 | -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/Pods/Target Support Files/Kingfisher/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 3.6.2 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/Pods/Target Support Files/Kingfisher/Kingfisher-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Kingfisher : NSObject 3 | @end 4 | @implementation PodsDummy_Kingfisher 5 | @end 6 | -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/Pods/Target Support Files/Kingfisher/Kingfisher-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/Pods/Target Support Files/Kingfisher/Kingfisher-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | #import "Kingfisher.h" 14 | 15 | FOUNDATION_EXPORT double KingfisherVersionNumber; 16 | FOUNDATION_EXPORT const unsigned char KingfisherVersionString[]; 17 | 18 | -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/Pods/Target Support Files/Kingfisher/Kingfisher.modulemap: -------------------------------------------------------------------------------- 1 | framework module Kingfisher { 2 | umbrella header "Kingfisher-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/Pods/Target Support Files/Kingfisher/Kingfisher.xcconfig: -------------------------------------------------------------------------------- 1 | CONFIGURATION_BUILD_DIR = $PODS_CONFIGURATION_BUILD_DIR/Kingfisher 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Public" 4 | OTHER_LDFLAGS = -framework "CFNetwork" 5 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" 6 | PODS_BUILD_DIR = $BUILD_DIR 7 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 8 | PODS_ROOT = ${SRCROOT} 9 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/Kingfisher 10 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 11 | SKIP_INSTALL = YES 12 | SWIFT_VERSION = 3.0 13 | -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/Pods/Target Support Files/Pods-WRCycleScrollViewDemo/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/Pods/Target Support Files/Pods-WRCycleScrollViewDemo/Pods-WRCycleScrollViewDemo-acknowledgements.markdown: -------------------------------------------------------------------------------- 1 | # Acknowledgements 2 | This application makes use of the following third party libraries: 3 | 4 | ## Kingfisher 5 | 6 | The MIT License (MIT) 7 | 8 | Copyright (c) 2017 Wei Wang 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy 11 | of this software and associated documentation files (the "Software"), to deal 12 | in the Software without restriction, including without limitation the rights 13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | copies of the Software, and to permit persons to whom the Software is 15 | furnished to do so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in all 18 | copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | SOFTWARE. 27 | 28 | 29 | Generated by CocoaPods - https://cocoapods.org 30 | -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/Pods/Target Support Files/Pods-WRCycleScrollViewDemo/Pods-WRCycleScrollViewDemo-acknowledgements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreferenceSpecifiers 6 | 7 | 8 | FooterText 9 | This application makes use of the following third party libraries: 10 | Title 11 | Acknowledgements 12 | Type 13 | PSGroupSpecifier 14 | 15 | 16 | FooterText 17 | The MIT License (MIT) 18 | 19 | Copyright (c) 2017 Wei Wang 20 | 21 | Permission is hereby granted, free of charge, to any person obtaining a copy 22 | of this software and associated documentation files (the "Software"), to deal 23 | in the Software without restriction, including without limitation the rights 24 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 25 | copies of the Software, and to permit persons to whom the Software is 26 | furnished to do so, subject to the following conditions: 27 | 28 | The above copyright notice and this permission notice shall be included in all 29 | copies or substantial portions of the Software. 30 | 31 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 32 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 33 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 34 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 35 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 36 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 37 | SOFTWARE. 38 | 39 | 40 | License 41 | MIT 42 | Title 43 | Kingfisher 44 | Type 45 | PSGroupSpecifier 46 | 47 | 48 | FooterText 49 | Generated by CocoaPods - https://cocoapods.org 50 | Title 51 | 52 | Type 53 | PSGroupSpecifier 54 | 55 | 56 | StringsTable 57 | Acknowledgements 58 | Title 59 | Acknowledgements 60 | 61 | 62 | -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/Pods/Target Support Files/Pods-WRCycleScrollViewDemo/Pods-WRCycleScrollViewDemo-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_WRCycleScrollViewDemo : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_WRCycleScrollViewDemo 5 | @end 6 | -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/Pods/Target Support Files/Pods-WRCycleScrollViewDemo/Pods-WRCycleScrollViewDemo-frameworks.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 5 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 6 | 7 | SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" 8 | 9 | install_framework() 10 | { 11 | if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then 12 | local source="${BUILT_PRODUCTS_DIR}/$1" 13 | elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then 14 | local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" 15 | elif [ -r "$1" ]; then 16 | local source="$1" 17 | fi 18 | 19 | local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 20 | 21 | if [ -L "${source}" ]; then 22 | echo "Symlinked..." 23 | source="$(readlink "${source}")" 24 | fi 25 | 26 | # use filter instead of exclude so missing patterns dont' throw errors 27 | echo "rsync -av --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" 28 | rsync -av --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" 29 | 30 | local basename 31 | basename="$(basename -s .framework "$1")" 32 | binary="${destination}/${basename}.framework/${basename}" 33 | if ! [ -r "$binary" ]; then 34 | binary="${destination}/${basename}" 35 | fi 36 | 37 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 38 | if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then 39 | strip_invalid_archs "$binary" 40 | fi 41 | 42 | # Resign the code if required by the build settings to avoid unstable apps 43 | code_sign_if_enabled "${destination}/$(basename "$1")" 44 | 45 | # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. 46 | if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then 47 | local swift_runtime_libs 48 | swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u && exit ${PIPESTATUS[0]}) 49 | for lib in $swift_runtime_libs; do 50 | echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" 51 | rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" 52 | code_sign_if_enabled "${destination}/${lib}" 53 | done 54 | fi 55 | } 56 | 57 | # Signs a framework with the provided identity 58 | code_sign_if_enabled() { 59 | if [ -n "${EXPANDED_CODE_SIGN_IDENTITY}" -a "${CODE_SIGNING_REQUIRED}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then 60 | # Use the current code_sign_identitiy 61 | echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" 62 | local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS} --preserve-metadata=identifier,entitlements '$1'" 63 | 64 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 65 | code_sign_cmd="$code_sign_cmd &" 66 | fi 67 | echo "$code_sign_cmd" 68 | eval "$code_sign_cmd" 69 | fi 70 | } 71 | 72 | # Strip invalid architectures 73 | strip_invalid_archs() { 74 | binary="$1" 75 | # Get architectures for current file 76 | archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | rev)" 77 | stripped="" 78 | for arch in $archs; do 79 | if ! [[ "${VALID_ARCHS}" == *"$arch"* ]]; then 80 | # Strip non-valid architectures in-place 81 | lipo -remove "$arch" -output "$binary" "$binary" || exit 1 82 | stripped="$stripped $arch" 83 | fi 84 | done 85 | if [[ "$stripped" ]]; then 86 | echo "Stripped $binary of architectures:$stripped" 87 | fi 88 | } 89 | 90 | 91 | if [[ "$CONFIGURATION" == "Debug" ]]; then 92 | install_framework "$BUILT_PRODUCTS_DIR/Kingfisher/Kingfisher.framework" 93 | fi 94 | if [[ "$CONFIGURATION" == "Release" ]]; then 95 | install_framework "$BUILT_PRODUCTS_DIR/Kingfisher/Kingfisher.framework" 96 | fi 97 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 98 | wait 99 | fi 100 | -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/Pods/Target Support Files/Pods-WRCycleScrollViewDemo/Pods-WRCycleScrollViewDemo-resources.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 5 | 6 | RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt 7 | > "$RESOURCES_TO_COPY" 8 | 9 | XCASSET_FILES=() 10 | 11 | case "${TARGETED_DEVICE_FAMILY}" in 12 | 1,2) 13 | TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone" 14 | ;; 15 | 1) 16 | TARGET_DEVICE_ARGS="--target-device iphone" 17 | ;; 18 | 2) 19 | TARGET_DEVICE_ARGS="--target-device ipad" 20 | ;; 21 | 3) 22 | TARGET_DEVICE_ARGS="--target-device tv" 23 | ;; 24 | 4) 25 | TARGET_DEVICE_ARGS="--target-device watch" 26 | ;; 27 | *) 28 | TARGET_DEVICE_ARGS="--target-device mac" 29 | ;; 30 | esac 31 | 32 | install_resource() 33 | { 34 | if [[ "$1" = /* ]] ; then 35 | RESOURCE_PATH="$1" 36 | else 37 | RESOURCE_PATH="${PODS_ROOT}/$1" 38 | fi 39 | if [[ ! -e "$RESOURCE_PATH" ]] ; then 40 | cat << EOM 41 | error: Resource "$RESOURCE_PATH" not found. Run 'pod install' to update the copy resources script. 42 | EOM 43 | exit 1 44 | fi 45 | case $RESOURCE_PATH in 46 | *.storyboard) 47 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" 48 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} 49 | ;; 50 | *.xib) 51 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" 52 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} 53 | ;; 54 | *.framework) 55 | echo "mkdir -p ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 56 | mkdir -p "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 57 | echo "rsync -av $RESOURCE_PATH ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 58 | rsync -av "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 59 | ;; 60 | *.xcdatamodel) 61 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH"`.mom\"" 62 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodel`.mom" 63 | ;; 64 | *.xcdatamodeld) 65 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd\"" 66 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd" 67 | ;; 68 | *.xcmappingmodel) 69 | echo "xcrun mapc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm\"" 70 | xcrun mapc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm" 71 | ;; 72 | *.xcassets) 73 | ABSOLUTE_XCASSET_FILE="$RESOURCE_PATH" 74 | XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE") 75 | ;; 76 | *) 77 | echo "$RESOURCE_PATH" 78 | echo "$RESOURCE_PATH" >> "$RESOURCES_TO_COPY" 79 | ;; 80 | esac 81 | } 82 | 83 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 84 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 85 | if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then 86 | mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 87 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 88 | fi 89 | rm -f "$RESOURCES_TO_COPY" 90 | 91 | if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "$XCASSET_FILES" ] 92 | then 93 | # Find all other xcassets (this unfortunately includes those of path pods and other targets). 94 | OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d) 95 | while read line; do 96 | if [[ $line != "${PODS_ROOT}*" ]]; then 97 | XCASSET_FILES+=("$line") 98 | fi 99 | done <<<"$OTHER_XCASSETS" 100 | 101 | printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 102 | fi 103 | -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/Pods/Target Support Files/Pods-WRCycleScrollViewDemo/Pods-WRCycleScrollViewDemo-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double Pods_WRCycleScrollViewDemoVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char Pods_WRCycleScrollViewDemoVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/Pods/Target Support Files/Pods-WRCycleScrollViewDemo/Pods-WRCycleScrollViewDemo.debug.xcconfig: -------------------------------------------------------------------------------- 1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES 2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/Kingfisher" 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 4 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 5 | OTHER_CFLAGS = $(inherited) -iquote "$PODS_CONFIGURATION_BUILD_DIR/Kingfisher/Kingfisher.framework/Headers" 6 | OTHER_LDFLAGS = $(inherited) -framework "Kingfisher" 7 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" 8 | PODS_BUILD_DIR = $BUILD_DIR 9 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 10 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 11 | PODS_ROOT = ${SRCROOT}/Pods 12 | -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/Pods/Target Support Files/Pods-WRCycleScrollViewDemo/Pods-WRCycleScrollViewDemo.modulemap: -------------------------------------------------------------------------------- 1 | framework module Pods_WRCycleScrollViewDemo { 2 | umbrella header "Pods-WRCycleScrollViewDemo-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/Pods/Target Support Files/Pods-WRCycleScrollViewDemo/Pods-WRCycleScrollViewDemo.release.xcconfig: -------------------------------------------------------------------------------- 1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES 2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/Kingfisher" 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 4 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 5 | OTHER_CFLAGS = $(inherited) -iquote "$PODS_CONFIGURATION_BUILD_DIR/Kingfisher/Kingfisher.framework/Headers" 6 | OTHER_LDFLAGS = $(inherited) -framework "Kingfisher" 7 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" 8 | PODS_BUILD_DIR = $BUILD_DIR 9 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 10 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 11 | PODS_ROOT = ${SRCROOT}/Pods 12 | -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/WRCycleScrollViewDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/WRCycleScrollViewDemo.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/WRCycleScrollViewDemo/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // WRCycleScrollViewDemo 4 | // 5 | // Created by wangrui on 2017/5/12. 6 | // Copyright © 2017年 wangrui. All rights reserved. 7 | // 8 | // Github地址:https://github.com/wangrui460/WRCycleScrollView 9 | 10 | import UIKit 11 | 12 | let MainNavBarColor = UIColor.init(red: 0/255.0, green: 175/255.0, blue: 240/255.0, alpha: 1) 13 | let kScreenWidth = UIScreen.main.bounds.width 14 | let kScreenHeight = UIScreen.main.bounds.height 15 | let kTabBarHeight = 49 16 | let kNavBarBottom = 64 17 | let kNavBarHeight = 44 18 | 19 | @UIApplicationMain 20 | class AppDelegate: UIResponder, UIApplicationDelegate 21 | { 22 | var window: UIWindow? 23 | 24 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool 25 | { 26 | 27 | window = UIWindow.init(frame: UIScreen.main.bounds) 28 | window?.backgroundColor = UIColor.white 29 | 30 | let nav = BaseNavigationController.init(rootViewController: DemoListController()) 31 | window?.rootViewController = nav 32 | window?.makeKeyAndVisible() 33 | 34 | setNavBarAppearence() 35 | 36 | return true 37 | } 38 | 39 | func setNavBarAppearence() 40 | { 41 | UIColor.defaultStatusBarStyle = .lightContent 42 | UIColor.defaultNavBarTintColor = UIColor.white 43 | UIColor.defaultNavBarBarTintColor = MainNavBarColor 44 | UIColor.defaultNavBarTitleColor = UIColor.white 45 | } 46 | } 47 | 48 | class BaseNavigationController: UINavigationController 49 | { 50 | override func pushViewController(_ viewController: UIViewController, animated: Bool) 51 | { 52 | if childViewControllers.count > 0 { 53 | viewController.hidesBottomBarWhenPushed = true 54 | } 55 | super.pushViewController(viewController, animated: animated) 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/WRCycleScrollViewDemo/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "60x60", 35 | "idiom" : "iphone", 36 | "filename" : "wr@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "60x60", 41 | "idiom" : "iphone", 42 | "filename" : "wr@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "idiom" : "ipad", 47 | "size" : "20x20", 48 | "scale" : "1x" 49 | }, 50 | { 51 | "idiom" : "ipad", 52 | "size" : "20x20", 53 | "scale" : "2x" 54 | }, 55 | { 56 | "idiom" : "ipad", 57 | "size" : "29x29", 58 | "scale" : "1x" 59 | }, 60 | { 61 | "idiom" : "ipad", 62 | "size" : "29x29", 63 | "scale" : "2x" 64 | }, 65 | { 66 | "idiom" : "ipad", 67 | "size" : "40x40", 68 | "scale" : "1x" 69 | }, 70 | { 71 | "idiom" : "ipad", 72 | "size" : "40x40", 73 | "scale" : "2x" 74 | }, 75 | { 76 | "idiom" : "ipad", 77 | "size" : "76x76", 78 | "scale" : "1x" 79 | }, 80 | { 81 | "idiom" : "ipad", 82 | "size" : "76x76", 83 | "scale" : "2x" 84 | }, 85 | { 86 | "idiom" : "ipad", 87 | "size" : "83.5x83.5", 88 | "scale" : "2x" 89 | }, 90 | { 91 | "idiom" : "ios-marketing", 92 | "size" : "1024x1024", 93 | "scale" : "1x" 94 | } 95 | ], 96 | "info" : { 97 | "version" : 1, 98 | "author" : "xcode" 99 | } 100 | } -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/WRCycleScrollViewDemo/Assets.xcassets/AppIcon.appiconset/wr@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangrui460/WRCycleScrollView/d48d12003bb5c5d7c3e52abec63510b5b5c692e5/WRCycleScrollViewDemo/WRCycleScrollViewDemo/Assets.xcassets/AppIcon.appiconset/wr@2x.png -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/WRCycleScrollViewDemo/Assets.xcassets/AppIcon.appiconset/wr@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangrui460/WRCycleScrollView/d48d12003bb5c5d7c3e52abec63510b5b5c692e5/WRCycleScrollViewDemo/WRCycleScrollViewDemo/Assets.xcassets/AppIcon.appiconset/wr@3x.png -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/WRCycleScrollViewDemo/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/WRCycleScrollViewDemo/Assets.xcassets/LaunchImage.launchimage/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "orientation" : "portrait", 5 | "idiom" : "ipad", 6 | "minimum-system-version" : "7.0", 7 | "extent" : "full-screen", 8 | "scale" : "2x" 9 | }, 10 | { 11 | "orientation" : "landscape", 12 | "idiom" : "ipad", 13 | "minimum-system-version" : "7.0", 14 | "extent" : "full-screen", 15 | "scale" : "1x" 16 | }, 17 | { 18 | "orientation" : "landscape", 19 | "idiom" : "ipad", 20 | "minimum-system-version" : "7.0", 21 | "extent" : "full-screen", 22 | "scale" : "2x" 23 | }, 24 | { 25 | "orientation" : "portrait", 26 | "idiom" : "iphone", 27 | "minimum-system-version" : "7.0", 28 | "scale" : "2x" 29 | }, 30 | { 31 | "orientation" : "portrait", 32 | "idiom" : "iphone", 33 | "minimum-system-version" : "7.0", 34 | "subtype" : "retina4", 35 | "scale" : "2x" 36 | }, 37 | { 38 | "orientation" : "portrait", 39 | "idiom" : "ipad", 40 | "minimum-system-version" : "7.0", 41 | "extent" : "full-screen", 42 | "scale" : "1x" 43 | } 44 | ], 45 | "info" : { 46 | "version" : 1, 47 | "author" : "xcode" 48 | } 49 | } -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/WRCycleScrollViewDemo/Assets.xcassets/dot/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/WRCycleScrollViewDemo/Assets.xcassets/dot/currentDot.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "slider_current_dot.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "slider_current_dot@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "slider_current_dot@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/WRCycleScrollViewDemo/Assets.xcassets/dot/currentDot.imageset/slider_current_dot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangrui460/WRCycleScrollView/d48d12003bb5c5d7c3e52abec63510b5b5c692e5/WRCycleScrollViewDemo/WRCycleScrollViewDemo/Assets.xcassets/dot/currentDot.imageset/slider_current_dot.png -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/WRCycleScrollViewDemo/Assets.xcassets/dot/currentDot.imageset/slider_current_dot@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangrui460/WRCycleScrollView/d48d12003bb5c5d7c3e52abec63510b5b5c692e5/WRCycleScrollViewDemo/WRCycleScrollViewDemo/Assets.xcassets/dot/currentDot.imageset/slider_current_dot@2x.png -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/WRCycleScrollViewDemo/Assets.xcassets/dot/currentDot.imageset/slider_current_dot@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangrui460/WRCycleScrollView/d48d12003bb5c5d7c3e52abec63510b5b5c692e5/WRCycleScrollViewDemo/WRCycleScrollViewDemo/Assets.xcassets/dot/currentDot.imageset/slider_current_dot@3x.png -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/WRCycleScrollViewDemo/Assets.xcassets/dot/defaultDot.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "slider_default_dot.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "slider_default_dot@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "slider_default_dot@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/WRCycleScrollViewDemo/Assets.xcassets/dot/defaultDot.imageset/slider_default_dot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangrui460/WRCycleScrollView/d48d12003bb5c5d7c3e52abec63510b5b5c692e5/WRCycleScrollViewDemo/WRCycleScrollViewDemo/Assets.xcassets/dot/defaultDot.imageset/slider_default_dot.png -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/WRCycleScrollViewDemo/Assets.xcassets/dot/defaultDot.imageset/slider_default_dot@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangrui460/WRCycleScrollView/d48d12003bb5c5d7c3e52abec63510b5b5c692e5/WRCycleScrollViewDemo/WRCycleScrollViewDemo/Assets.xcassets/dot/defaultDot.imageset/slider_default_dot@2x.png -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/WRCycleScrollViewDemo/Assets.xcassets/dot/defaultDot.imageset/slider_default_dot@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangrui460/WRCycleScrollView/d48d12003bb5c5d7c3e52abec63510b5b5c692e5/WRCycleScrollViewDemo/WRCycleScrollViewDemo/Assets.xcassets/dot/defaultDot.imageset/slider_default_dot@3x.png -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/WRCycleScrollViewDemo/Assets.xcassets/image11.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "image11.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/WRCycleScrollViewDemo/Assets.xcassets/image11.imageset/image11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangrui460/WRCycleScrollView/d48d12003bb5c5d7c3e52abec63510b5b5c692e5/WRCycleScrollViewDemo/WRCycleScrollViewDemo/Assets.xcassets/image11.imageset/image11.png -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/WRCycleScrollViewDemo/Assets.xcassets/image12.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "image12.jpg", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/WRCycleScrollViewDemo/Assets.xcassets/image12.imageset/image12.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangrui460/WRCycleScrollView/d48d12003bb5c5d7c3e52abec63510b5b5c692e5/WRCycleScrollViewDemo/WRCycleScrollViewDemo/Assets.xcassets/image12.imageset/image12.jpg -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/WRCycleScrollViewDemo/Assets.xcassets/image13.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "image13.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/WRCycleScrollViewDemo/Assets.xcassets/image13.imageset/image13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangrui460/WRCycleScrollView/d48d12003bb5c5d7c3e52abec63510b5b5c692e5/WRCycleScrollViewDemo/WRCycleScrollViewDemo/Assets.xcassets/image13.imageset/image13.png -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/WRCycleScrollViewDemo/Assets.xcassets/image14.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "image14.jpg", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/WRCycleScrollViewDemo/Assets.xcassets/image14.imageset/image14.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangrui460/WRCycleScrollView/d48d12003bb5c5d7c3e52abec63510b5b5c692e5/WRCycleScrollViewDemo/WRCycleScrollViewDemo/Assets.xcassets/image14.imageset/image14.jpg -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/WRCycleScrollViewDemo/Assets.xcassets/image15.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "image15.jpg", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/WRCycleScrollViewDemo/Assets.xcassets/image15.imageset/image15.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangrui460/WRCycleScrollView/d48d12003bb5c5d7c3e52abec63510b5b5c692e5/WRCycleScrollViewDemo/WRCycleScrollViewDemo/Assets.xcassets/image15.imageset/image15.jpg -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/WRCycleScrollViewDemo/Assets.xcassets/localImg1.imageset/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangrui460/WRCycleScrollView/d48d12003bb5c5d7c3e52abec63510b5b5c692e5/WRCycleScrollViewDemo/WRCycleScrollViewDemo/Assets.xcassets/localImg1.imageset/1.png -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/WRCycleScrollViewDemo/Assets.xcassets/localImg1.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "1.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/WRCycleScrollViewDemo/Assets.xcassets/localImg10.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "localImg10.jpg", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/WRCycleScrollViewDemo/Assets.xcassets/localImg10.imageset/localImg10.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangrui460/WRCycleScrollView/d48d12003bb5c5d7c3e52abec63510b5b5c692e5/WRCycleScrollViewDemo/WRCycleScrollViewDemo/Assets.xcassets/localImg10.imageset/localImg10.jpg -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/WRCycleScrollViewDemo/Assets.xcassets/localImg2.imageset/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangrui460/WRCycleScrollView/d48d12003bb5c5d7c3e52abec63510b5b5c692e5/WRCycleScrollViewDemo/WRCycleScrollViewDemo/Assets.xcassets/localImg2.imageset/2.png -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/WRCycleScrollViewDemo/Assets.xcassets/localImg2.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "2.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/WRCycleScrollViewDemo/Assets.xcassets/localImg3.imageset/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangrui460/WRCycleScrollView/d48d12003bb5c5d7c3e52abec63510b5b5c692e5/WRCycleScrollViewDemo/WRCycleScrollViewDemo/Assets.xcassets/localImg3.imageset/3.png -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/WRCycleScrollViewDemo/Assets.xcassets/localImg3.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "3.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/WRCycleScrollViewDemo/Assets.xcassets/localImg4.imageset/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangrui460/WRCycleScrollView/d48d12003bb5c5d7c3e52abec63510b5b5c692e5/WRCycleScrollViewDemo/WRCycleScrollViewDemo/Assets.xcassets/localImg4.imageset/4.png -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/WRCycleScrollViewDemo/Assets.xcassets/localImg4.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "4.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/WRCycleScrollViewDemo/Assets.xcassets/localImg5.imageset/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangrui460/WRCycleScrollView/d48d12003bb5c5d7c3e52abec63510b5b5c692e5/WRCycleScrollViewDemo/WRCycleScrollViewDemo/Assets.xcassets/localImg5.imageset/5.png -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/WRCycleScrollViewDemo/Assets.xcassets/localImg5.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "5.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/WRCycleScrollViewDemo/Assets.xcassets/localImg6.imageset/6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangrui460/WRCycleScrollView/d48d12003bb5c5d7c3e52abec63510b5b5c692e5/WRCycleScrollViewDemo/WRCycleScrollViewDemo/Assets.xcassets/localImg6.imageset/6.jpg -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/WRCycleScrollViewDemo/Assets.xcassets/localImg6.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "6.jpg", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/WRCycleScrollViewDemo/Assets.xcassets/localImg7.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "localImg7.jpg", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/WRCycleScrollViewDemo/Assets.xcassets/localImg7.imageset/localImg7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangrui460/WRCycleScrollView/d48d12003bb5c5d7c3e52abec63510b5b5c692e5/WRCycleScrollViewDemo/WRCycleScrollViewDemo/Assets.xcassets/localImg7.imageset/localImg7.jpg -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/WRCycleScrollViewDemo/Assets.xcassets/localImg8.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "localImg8.jpg", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/WRCycleScrollViewDemo/Assets.xcassets/localImg8.imageset/localImg8.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangrui460/WRCycleScrollView/d48d12003bb5c5d7c3e52abec63510b5b5c692e5/WRCycleScrollViewDemo/WRCycleScrollViewDemo/Assets.xcassets/localImg8.imageset/localImg8.jpg -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/WRCycleScrollViewDemo/Assets.xcassets/localImg9.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "localImg9.jpg", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/WRCycleScrollViewDemo/Assets.xcassets/localImg9.imageset/localImg9.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangrui460/WRCycleScrollView/d48d12003bb5c5d7c3e52abec63510b5b5c692e5/WRCycleScrollViewDemo/WRCycleScrollViewDemo/Assets.xcassets/localImg9.imageset/localImg9.jpg -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/WRCycleScrollViewDemo/Assets.xcassets/placeholder/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/WRCycleScrollViewDemo/Assets.xcassets/placeholder/placeholder_720x360.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "placeholder_720x360@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "placeholder_720x360@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/WRCycleScrollViewDemo/Assets.xcassets/placeholder/placeholder_720x360.imageset/placeholder_720x360@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangrui460/WRCycleScrollView/d48d12003bb5c5d7c3e52abec63510b5b5c692e5/WRCycleScrollViewDemo/WRCycleScrollViewDemo/Assets.xcassets/placeholder/placeholder_720x360.imageset/placeholder_720x360@2x.png -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/WRCycleScrollViewDemo/Assets.xcassets/placeholder/placeholder_720x360.imageset/placeholder_720x360@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangrui460/WRCycleScrollView/d48d12003bb5c5d7c3e52abec63510b5b5c692e5/WRCycleScrollViewDemo/WRCycleScrollViewDemo/Assets.xcassets/placeholder/placeholder_720x360.imageset/placeholder_720x360@3x.png -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/WRCycleScrollViewDemo/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/WRCycleScrollViewDemo/CustomDotController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CustomDotController.swift 3 | // WRCycleScrollViewDemo 4 | // 5 | // Created by itwangrui on 2017/11/14. 6 | // Copyright © 2017年 wangrui. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class CustomDotController: UIViewController 12 | { 13 | var cycleScrollView:WRCycleScrollView? 14 | 15 | override func viewDidLoad() 16 | { 17 | super.viewDidLoad() 18 | view.backgroundColor = UIColor.white 19 | self.title = "网络URL" 20 | 21 | let height = 520 * kScreenWidth / 1080.0 22 | let frame = CGRect(x: 0, y: 150, width: kScreenWidth, height: height) 23 | let serverImages = ["http://p.lrlz.com/data/upload/mobile/special/s252/s252_05471521705899113.png", 24 | "http://p.lrlz.com/data/upload/mobile/special/s303/s303_05442007678060723.png", 25 | "http://p.lrlz.com/data/upload/mobile/special/s303/s303_05442007587372591.png", 26 | "http://p.lrlz.com/data/upload/mobile/special/s303/s303_05442007388249407.png", 27 | "http://p.lrlz.com/data/upload/mobile/special/s303/s303_05442007470310935.png"] 28 | cycleScrollView = WRCycleScrollView(frame: frame, type: .SERVER, imgs: serverImages) 29 | cycleScrollView?.defaultPageDotImage = UIImage(named: "defaultDot") 30 | cycleScrollView?.currentPageDotImage = UIImage(named: "currentDot") 31 | view.addSubview(cycleScrollView!) 32 | cycleScrollView?.delegate = self 33 | } 34 | } 35 | 36 | extension CustomDotController: WRCycleScrollViewDelegate 37 | { 38 | /// 点击图片事件 39 | func cycleScrollViewDidSelect(at index:Int, cycleScrollView:WRCycleScrollView) 40 | { 41 | print("点击了第\(index+1)个图片") 42 | } 43 | /// 图片滚动事件 44 | func cycleScrollViewDidScroll(to index:Int, cycleScrollView:WRCycleScrollView) 45 | { 46 | print("滚动到了第\(index+1)个图片") 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/WRCycleScrollViewDemo/DemoListController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DemoListController.swift 3 | // WRCycleScrollViewDemo 4 | // 5 | // Created by wangrui on 2017/4/19. 6 | // Copyright © 2017年 wangrui. All rights reserved. 7 | // 8 | // Github地址:https://github.com/wangrui460/WRCycleScrollView 9 | 10 | import UIKit 11 | 12 | class DemoListController: UIViewController 13 | { 14 | lazy var tableView:UITableView = UITableView(frame: CGRect.init(x: 0, y: 0, width: kScreenWidth, height: self.view.bounds.height), style: .plain) 15 | 16 | override func viewDidLoad() { 17 | super.viewDidLoad() 18 | view.backgroundColor = UIColor.green 19 | title = "WRCycleScrollView" 20 | view.addSubview(tableView) 21 | tableView.dataSource = self 22 | tableView.delegate = self 23 | } 24 | } 25 | 26 | // MARK: - tableView delegate / dataSource 27 | extension DemoListController: UITableViewDelegate, UITableViewDataSource 28 | { 29 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int 30 | { 31 | return 8 32 | } 33 | 34 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell 35 | { 36 | let cell = UITableViewCell.init(style: .default, reuseIdentifier: nil) 37 | var str:String? = nil 38 | switch indexPath.row { 39 | case 0: 40 | str = "本地图片" 41 | case 1: 42 | str = "网络URL" 43 | case 2: 44 | str = "支持StoryBoard创建" 45 | case 3: 46 | str = "不无限轮播" 47 | case 4: 48 | str = "不显示pageControl" 49 | case 5: 50 | str = "知乎日报效果" 51 | case 6: 52 | str = "自定义dot" 53 | case 7: 54 | str = "独立出dot" 55 | default: 56 | str = "" 57 | } 58 | cell.textLabel?.text = str 59 | cell.textLabel?.font = UIFont.systemFont(ofSize: 15) 60 | return cell 61 | } 62 | 63 | func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { 64 | return 60 65 | } 66 | 67 | func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) 68 | { 69 | tableView.deselectRow(at: indexPath, animated: true) 70 | switch indexPath.row { 71 | case 0: 72 | navigationController?.pushViewController(LocalImgController(), animated: true) 73 | case 1: 74 | navigationController?.pushViewController(ServerImgController(), animated: true) 75 | case 2: 76 | 77 | let SBVC:SBController = UIStoryboard.init(name: "StoryBoardController", bundle: nil).instantiateInitialViewController() as! SBController 78 | navigationController?.pushViewController(SBVC, animated: true) 79 | case 3: 80 | navigationController?.pushViewController(NoEndlessController(), animated: true) 81 | case 4: 82 | navigationController?.pushViewController(NoPageControlController(), animated: true) 83 | case 5: 84 | navigationController?.pushViewController(ZhiHuController(), animated: true) 85 | case 6: 86 | navigationController?.pushViewController(CustomDotController(), animated: true) 87 | case 7: 88 | navigationController?.pushViewController(StandaloneDotController(), animated: true) 89 | default: 90 | break 91 | } 92 | } 93 | } 94 | 95 | 96 | 97 | 98 | -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/WRCycleScrollViewDemo/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIcons 10 | 11 | CFBundleIcons~ipad 12 | 13 | CFBundleIdentifier 14 | $(PRODUCT_BUNDLE_IDENTIFIER) 15 | CFBundleInfoDictionaryVersion 16 | 6.0 17 | CFBundleName 18 | $(PRODUCT_NAME) 19 | CFBundlePackageType 20 | APPL 21 | CFBundleShortVersionString 22 | 1.0 23 | CFBundleVersion 24 | 1 25 | LSRequiresIPhoneOS 26 | 27 | NSAppTransportSecurity 28 | 29 | NSAllowsArbitraryLoads 30 | 31 | 32 | UILaunchStoryboardName 33 | LaunchScreen 34 | UIRequiredDeviceCapabilities 35 | 36 | armv7 37 | 38 | UISupportedInterfaceOrientations 39 | 40 | UIInterfaceOrientationPortrait 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | UISupportedInterfaceOrientations~ipad 45 | 46 | UIInterfaceOrientationPortrait 47 | UIInterfaceOrientationPortraitUpsideDown 48 | UIInterfaceOrientationLandscapeLeft 49 | UIInterfaceOrientationLandscapeRight 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/WRCycleScrollViewDemo/LocalImgController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // WRCycleScrollViewDemo 4 | // 5 | // Created by wangrui on 2017/5/12. 6 | // Copyright © 2017年 wangrui. All rights reserved. 7 | // 8 | // Github地址:https://github.com/wangrui460/WRCycleScrollView 9 | 10 | import UIKit 11 | 12 | class LocalImgController: UIViewController 13 | { 14 | var cycleScrollView:WRCycleScrollView? 15 | 16 | override func viewDidLoad() 17 | { 18 | super.viewDidLoad() 19 | view.backgroundColor = UIColor.white 20 | self.title = "本地图片" 21 | 22 | let height:CGFloat = 200 23 | let frame = CGRect(x: 0, y: 150, width: kScreenWidth, height: height) 24 | let localImages = ["localImg6","localImg7","localImg8","localImg9","localImg10"] 25 | let descs = ["韩国防部回应停止部署萨德:遵照最高统帅指导方针", 26 | "勒索病毒攻击再次爆发 国内校园网大面积感染", 27 | "Win10秋季更新重磅功能:跟安卓与iOS无缝连接", 28 | "《琅琊榜2》为何没有胡歌?胡歌:我看过剧本,离开是种保护", 29 | "阿米尔汗在印度的影响力,我国的哪位影视明星能与之齐名呢?"] 30 | cycleScrollView = WRCycleScrollView(frame:frame, type:.LOCAL, imgs:localImages, descs:descs) 31 | cycleScrollView!.delegate = self 32 | cycleScrollView?.descLabelFont = UIFont.boldSystemFont(ofSize: 15) 33 | cycleScrollView?.descLabelHeight = 50 34 | cycleScrollView?.pageControlAliment = .RightBottom 35 | view.addSubview(cycleScrollView!) 36 | } 37 | } 38 | 39 | extension LocalImgController: WRCycleScrollViewDelegate 40 | { 41 | /// 点击图片回调 42 | func cycleScrollViewDidSelect(at index:Int, cycleScrollView:WRCycleScrollView) 43 | { 44 | print("点击了第\(index+1)个图片") 45 | } 46 | /// 图片滚动回调 47 | func cycleScrollViewDidScroll(to index:Int, cycleScrollView:WRCycleScrollView) 48 | { 49 | print("滚动到了第\(index+1)个图片") 50 | } 51 | } 52 | 53 | -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/WRCycleScrollViewDemo/NoEndlessController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ServerImgController.swift 3 | // WRCycleScrollViewDemo 4 | // 5 | // Created by wangrui on 2017/5/12. 6 | // Copyright © 2017年 wangrui. All rights reserved. 7 | // 8 | // Github地址:https://github.com/wangrui460/WRCycleScrollView 9 | 10 | import UIKit 11 | 12 | class NoEndlessController: UIViewController 13 | { 14 | var cycleScrollView:WRCycleScrollView? 15 | 16 | override func viewDidLoad() 17 | { 18 | super.viewDidLoad() 19 | view.backgroundColor = UIColor.white 20 | self.title = "不无限轮播" 21 | 22 | let height = 520 * kScreenWidth / 1080.0 23 | let frame = CGRect(x: 0, y: 150, width: kScreenWidth, height: height) 24 | let serverImages = ["http://p.lrlz.com/data/upload/mobile/special/s252/s252_05471521705899113.png", 25 | "http://p.lrlz.com/data/upload/mobile/special/s303/s303_05442007678060723.png", 26 | "http://p.lrlz.com/data/upload/mobile/special/s303/s303_05442007587372591.png", 27 | "http://p.lrlz.com/data/upload/mobile/special/s303/s303_05442007388249407.png", 28 | "http://p.lrlz.com/data/upload/mobile/special/s303/s303_05442007470310935.png"] 29 | cycleScrollView = WRCycleScrollView(frame: frame, type: .SERVER, imgs: serverImages) 30 | view.addSubview(cycleScrollView!) 31 | cycleScrollView?.delegate = self 32 | cycleScrollView?.isEndlessScroll = false 33 | } 34 | } 35 | 36 | extension NoEndlessController: WRCycleScrollViewDelegate 37 | { 38 | /// 点击图片回调 39 | func cycleScrollViewDidSelect(at index:Int, cycleScrollView:WRCycleScrollView) 40 | { 41 | print("点击了第\(index+1)个图片") 42 | } 43 | /// 图片滚动回调 44 | func cycleScrollViewDidScroll(to index:Int, cycleScrollView:WRCycleScrollView) 45 | { 46 | print("滚动到了第\(index+1)个图片") 47 | } 48 | } 49 | 50 | -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/WRCycleScrollViewDemo/NoPageControlController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // WRCycleScrollViewDemo 4 | // 5 | // Created by wangrui on 2017/5/12. 6 | // Copyright © 2017年 wangrui. All rights reserved. 7 | // 8 | // Github地址:https://github.com/wangrui460/WRCycleScrollView 9 | 10 | import UIKit 11 | 12 | class NoPageControlController: UIViewController 13 | { 14 | var cycleScrollView:WRCycleScrollView? 15 | 16 | override func viewDidLoad() 17 | { 18 | super.viewDidLoad() 19 | view.backgroundColor = UIColor.white 20 | self.title = "不显示pageControl" 21 | 22 | let height:CGFloat = 110 23 | let frame = CGRect(x: 0, y: 150, width: kScreenWidth, height: height) 24 | let localImages = ["image11","image13","image15"] 25 | cycleScrollView = WRCycleScrollView(frame:frame, type:.LOCAL, imgs:localImages) 26 | cycleScrollView!.delegate = self 27 | cycleScrollView?.showPageControl = false 28 | cycleScrollView?.imageContentModel = .scaleToFill 29 | view.addSubview(cycleScrollView!) 30 | } 31 | } 32 | 33 | extension NoPageControlController: WRCycleScrollViewDelegate 34 | { 35 | /// 点击图片回调 36 | func cycleScrollViewDidSelect(at index:Int, cycleScrollView:WRCycleScrollView) 37 | { 38 | print("点击了第\(index+1)个图片") 39 | } 40 | /// 图片滚动回调 41 | func cycleScrollViewDidScroll(to index:Int, cycleScrollView:WRCycleScrollView) 42 | { 43 | print("滚动到了第\(index+1)个图片") 44 | } 45 | } 46 | 47 | -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/WRCycleScrollViewDemo/SBController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SBController.swift 3 | // WRCycleScrollViewDemo 4 | // 5 | // Created by wangrui on 2017/6/21. 6 | // Copyright © 2017年 wangrui. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class SBController: UIViewController 12 | { 13 | 14 | @IBOutlet weak var cycleScrollView: WRCycleScrollView! 15 | 16 | override func viewDidLoad() { 17 | super.viewDidLoad() 18 | view.backgroundColor = UIColor.white 19 | self.title = "StoryBoard创建" 20 | 21 | let localImages = ["localImg6","localImg7","localImg8","localImg9","localImg10"] 22 | let descs = ["韩国防部回应停止部署萨德:遵照最高统帅指导方针", 23 | " 勒索病毒攻击再次爆发 国内校园网大面积感染,Mac什么事都没有", 24 | "Win10秋季更新重磅功能:跟安卓与iOS无缝连接", 25 | "《琅琊榜2》为何没有胡歌?胡歌:我看过剧本,离开是种保护", 26 | "阿米尔汗在印度的影响力,我国的哪位影视明星能与之齐名呢?"] 27 | 28 | cycleScrollView.delegate = self 29 | view.addSubview(cycleScrollView) 30 | cycleScrollView.localImgArray = localImages 31 | cycleScrollView.descTextArray = descs 32 | cycleScrollView.descLabelHeight = 40 33 | cycleScrollView.descLabelFont = UIFont.systemFont(ofSize: 13) 34 | cycleScrollView.pageControlAliment = .RightBottom 35 | } 36 | } 37 | 38 | 39 | extension SBController: WRCycleScrollViewDelegate 40 | { 41 | /// 点击图片回调 42 | func cycleScrollViewDidSelect(at index:Int, cycleScrollView:WRCycleScrollView) 43 | { 44 | print("点击了第\(index+1)个图片") 45 | } 46 | /// 图片滚动回调 47 | func cycleScrollViewDidScroll(to index:Int, cycleScrollView:WRCycleScrollView) 48 | { 49 | print("滚动到了第\(index+1)个图片") 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/WRCycleScrollViewDemo/ServerImgController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ServerImgController.swift 3 | // WRCycleScrollViewDemo 4 | // 5 | // Created by wangrui on 2017/5/12. 6 | // Copyright © 2017年 wangrui. All rights reserved. 7 | // 8 | // Github地址:https://github.com/wangrui460/WRCycleScrollView 9 | 10 | import UIKit 11 | 12 | class ServerImgController: UIViewController 13 | { 14 | var cycleScrollView:WRCycleScrollView? 15 | 16 | override func viewDidLoad() 17 | { 18 | super.viewDidLoad() 19 | view.backgroundColor = UIColor.white 20 | self.title = "网络URL" 21 | 22 | let height = 520 * kScreenWidth / 1080.0 23 | let frame = CGRect(x: 0, y: 150, width: kScreenWidth, height: height) 24 | let serverImages = ["http://p.lrlz.com/data/upload/mobile/special/s252/s252_05471521705899113.png", 25 | "http://p.lrlz.com/data/upload/mobile/special/s303/s303_05442007678060723.png", 26 | "http://p.lrlz.com/data/upload/mobile/special/s303/s303_05442007587372591.png", 27 | "http://p.lrlz.com/data/upload/mobile/special/s303/s303_05442007388249407.png", 28 | "http://p.lrlz.com/data/upload/mobile/special/s303/s303_05442007470310935.png"] 29 | cycleScrollView = WRCycleScrollView(frame: frame, type: .SERVER, imgs: serverImages) 30 | view.addSubview(cycleScrollView!) 31 | cycleScrollView?.delegate = self 32 | } 33 | } 34 | 35 | extension ServerImgController: WRCycleScrollViewDelegate 36 | { 37 | /// 点击图片事件 38 | func cycleScrollViewDidSelect(at index:Int, cycleScrollView:WRCycleScrollView) 39 | { 40 | print("点击了第\(index+1)个图片") 41 | } 42 | /// 图片滚动事件 43 | func cycleScrollViewDidScroll(to index:Int, cycleScrollView:WRCycleScrollView) 44 | { 45 | print("滚动到了第\(index+1)个图片") 46 | } 47 | } 48 | 49 | -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/WRCycleScrollViewDemo/StandaloneDotController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StandaloneDotController.swift 3 | // WRCycleScrollViewDemo 4 | // 5 | // Created by itwangrui on 2017/11/14. 6 | // Copyright © 2017年 wangrui. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class StandaloneDotController: UIViewController { 12 | 13 | var cycleScrollView:WRCycleScrollView? 14 | 15 | override func viewDidLoad() 16 | { 17 | super.viewDidLoad() 18 | view.backgroundColor = UIColor.white 19 | self.title = "网络URL" 20 | 21 | let cycleScrollViewHeightTop:CGFloat = 150 22 | let cycleScrollViewHeight = 520 * kScreenWidth / 1080.0 23 | let cycleScrollViewFrame = CGRect(x: 0, y: cycleScrollViewHeightTop, width: kScreenWidth, height: cycleScrollViewHeight) 24 | let serverImages = ["http://p.lrlz.com/data/upload/mobile/special/s252/s252_05471521705899113.png", 25 | "http://p.lrlz.com/data/upload/mobile/special/s303/s303_05442007678060723.png", 26 | "http://p.lrlz.com/data/upload/mobile/special/s303/s303_05442007587372591.png", 27 | "http://p.lrlz.com/data/upload/mobile/special/s303/s303_05442007388249407.png", 28 | "http://p.lrlz.com/data/upload/mobile/special/s303/s303_05442007470310935.png"] 29 | cycleScrollView = WRCycleScrollView(frame: cycleScrollViewFrame, type: .SERVER, imgs: serverImages, defaultDotImage: UIImage(named: "defaultDot"), currentDotImage: UIImage(named: "currentDot")) 30 | view.addSubview(cycleScrollView!) 31 | cycleScrollView?.delegate = self 32 | cycleScrollView?.pageControlPointSpace = 0 33 | 34 | let top = cycleScrollViewHeightTop + cycleScrollViewHeight + 16 35 | cycleScrollView?.outerPageControlFrame = CGRect(x: 16, y: top, width: 0, height: 0) 36 | } 37 | } 38 | 39 | extension StandaloneDotController: WRCycleScrollViewDelegate 40 | { 41 | /// 点击图片事件 42 | func cycleScrollViewDidSelect(at index:Int, cycleScrollView:WRCycleScrollView) 43 | { 44 | print("点击了第\(index+1)个图片") 45 | } 46 | /// 图片滚动事件 47 | func cycleScrollViewDidScroll(to index:Int, cycleScrollView:WRCycleScrollView) 48 | { 49 | print("滚动到了第\(index+1)个图片") 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/WRCycleScrollViewDemo/StoryBoardController.storyboard: -------------------------------------------------------------------------------- 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 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/WRCycleScrollViewDemo/WRCycleScrollView/WRCycleCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WRCycleCell.swift 3 | // WRCycleScrollViewDemo 4 | // 5 | // Created by wangrui on 2017/5/12. 6 | // Copyright © 2017年 wangrui. All rights reserved. 7 | // 8 | // Github地址:https://github.com/wangrui460/WRCycleScrollView 9 | 10 | import UIKit 11 | import Kingfisher 12 | 13 | class WRCycleCell: UICollectionViewCell 14 | { 15 | //======================================================= 16 | // MARK: 对外提供的属性 17 | //======================================================= 18 | var imgSource:ImgSource = ImgSource.LOCAL(name: "placeholder") { 19 | didSet { 20 | switch imgSource { 21 | case let .SERVER(url): 22 | imgView.kf.setImage(with: url) 23 | case let .LOCAL(name): 24 | imgView.image = UIImage(named: name) 25 | } 26 | } 27 | } 28 | 29 | var placeholderImage: UIImage? 30 | 31 | var descText:String? { 32 | didSet { 33 | descLabel.isHidden = (descText == nil) ? true : false 34 | bottomView.isHidden = (descText == nil) ? true : false 35 | descLabel.text = descText 36 | } 37 | } 38 | 39 | override var frame: CGRect { 40 | didSet { 41 | bounds.size = frame.size 42 | } 43 | } 44 | 45 | var imageContentModel:UIViewContentMode = .scaleAspectFill { 46 | didSet { 47 | imgView.contentMode = imageContentModel 48 | } 49 | } 50 | 51 | var descLabelFont: UIFont = UIFont(name: "Helvetica-Bold", size: 18)! { 52 | didSet { 53 | descLabel.font = descLabelFont 54 | } 55 | } 56 | var descLabelTextColor: UIColor = UIColor.white { 57 | didSet { 58 | descLabel.textColor = descLabelTextColor 59 | } 60 | } 61 | var descLabelHeight: CGFloat = 60 { 62 | didSet { 63 | descLabel.frame.size.height = descLabelHeight 64 | } 65 | } 66 | var descLabelTextAlignment:NSTextAlignment = .left { 67 | didSet { 68 | descLabel.textAlignment = descLabelTextAlignment 69 | } 70 | } 71 | var bottomViewBackgroundColor: UIColor = UIColor.black.withAlphaComponent(0.5) { 72 | didSet { 73 | bottomView.backgroundColor = bottomViewBackgroundColor 74 | } 75 | } 76 | 77 | //======================================================= 78 | // MARK: 内部属性 79 | //======================================================= 80 | fileprivate var imgView:UIImageView! 81 | fileprivate var descLabel:UILabel! 82 | fileprivate var bottomView:UIView! 83 | 84 | //======================================================= 85 | // MARK: 构造方法 86 | //======================================================= 87 | override init(frame: CGRect) 88 | { 89 | super.init(frame: frame) 90 | backgroundColor = UIColor.white 91 | setupImgView() 92 | setupDescLabel() 93 | setupBottomView() 94 | } 95 | required init?(coder aDecoder: NSCoder) { 96 | fatalError("init(coder:) has not been implemented") 97 | } 98 | deinit { 99 | print("WRCycleCell deinit") 100 | } 101 | 102 | 103 | //======================================================= 104 | // MARK: 内部方法(layoutSubviews) 105 | //======================================================= 106 | override func layoutSubviews() 107 | { 108 | super.layoutSubviews() 109 | imgView.frame = self.bounds 110 | 111 | if let _ = descText 112 | { 113 | let margin:CGFloat = 16 114 | let labelWidth = imgView.bounds.width - 2 * margin 115 | let labelHeight = descLabelHeight 116 | let labelY = bounds.height - labelHeight 117 | descLabel.frame = CGRect(x: margin, y: labelY, width: labelWidth, height: labelHeight) 118 | bottomView.frame = CGRect(x: 0, y: labelY, width: imgView.bounds.width, height: labelHeight) 119 | bringSubview(toFront: descLabel) 120 | } 121 | } 122 | } 123 | 124 | //======================================================= 125 | // MARK: - 基本控件(图片、描述文字、底部view) 126 | //======================================================= 127 | extension WRCycleCell 128 | { 129 | fileprivate func setupImgView() 130 | { 131 | imgView = UIImageView() 132 | imgView.contentMode = imageContentModel 133 | imgView.clipsToBounds = true 134 | addSubview(imgView) 135 | } 136 | 137 | fileprivate func setupDescLabel() 138 | { 139 | descLabel = UILabel() 140 | descLabel.text = descText 141 | descLabel.numberOfLines = 0 142 | descLabel.font = descLabelFont 143 | descLabel.textColor = descLabelTextColor 144 | descLabel.frame.size.height = descLabelHeight 145 | descLabel.textAlignment = descLabelTextAlignment 146 | addSubview(descLabel) 147 | descLabel.isHidden = true 148 | } 149 | 150 | fileprivate func setupBottomView() 151 | { 152 | bottomView = UIView() 153 | bottomView.backgroundColor = bottomViewBackgroundColor 154 | addSubview(bottomView) 155 | bottomView.isHidden = true 156 | } 157 | } 158 | 159 | -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/WRCycleScrollViewDemo/WRCycleScrollView/WRPageControl.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WRPageControl.swift 3 | // WRCycleScrollViewDemo 4 | // 5 | // Created by itwangrui on 2017/11/14. 6 | // Copyright © 2017年 wangrui. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | private let WRPageControlPointWidth: CGFloat = 7 12 | private let WRPageControlPointHeight: CGFloat = 20 13 | 14 | class WRPageControl: UIPageControl 15 | { 16 | var currentImage: UIImage? 17 | var defaultImage: UIImage? 18 | var pointSpace: CGFloat = 15 19 | var pageSize: CGSize { 20 | get { 21 | if let curImage = currentImage, let defImage = defaultImage { 22 | let pageH = curImage.size.height 23 | let defDotW = defImage.size.width 24 | let curDotW = curImage.size.width 25 | let pageW = CGFloat(numberOfPages - 1) * (pointSpace + defDotW + defDotW) + curDotW 26 | return CGSize(width: pageW, height: pageH) 27 | } 28 | else { 29 | let pageW = CGFloat(numberOfPages - 1) * (pointSpace + WRPageControlPointWidth) + WRPageControlPointWidth 30 | return CGSize(width: pageW, height: WRPageControlPointHeight) 31 | } 32 | } 33 | } 34 | 35 | override var currentPage: Int { 36 | didSet { 37 | updatePageControl() 38 | } 39 | } 40 | 41 | init(frame: CGRect, currentImage: UIImage?, defaultImage: UIImage?) 42 | { 43 | super.init(frame: frame) 44 | self.currentImage = currentImage 45 | self.defaultImage = defaultImage 46 | } 47 | required init?(coder aDecoder: NSCoder) { 48 | fatalError("init(coder:) has not been implemented") 49 | } 50 | } 51 | 52 | 53 | // MARK: - update index 54 | extension WRPageControl 55 | { 56 | func updatePageControl() 57 | { 58 | for index in 0.. CGSize 92 | { 93 | var newSize = CGSize(width: 0, height: 0) 94 | if let curImage = currentImage, let defImage = defaultImage { 95 | if currentIndex == currentPage { 96 | newSize = curImage.size 97 | } else { 98 | newSize = defImage.size 99 | } 100 | } else { 101 | newSize = CGSize(width: WRPageControlPointWidth, height: WRPageControlPointWidth) 102 | } 103 | return newSize 104 | } 105 | } 106 | 107 | 108 | -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/WRCycleScrollViewDemo/WRCycleScrollView/WRProxy.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WRProxy.swift 3 | // WRCycleScrollViewDemo 4 | // 5 | // Created by wangrui on 2017/5/15. 6 | // Copyright © 2017年 wangrui. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | ///////////////////////////////////////////////////////////////////////////// 12 | // MARK: 数据 相关 13 | ///////////////////////////////////////////////////////////////////////////// 14 | 15 | // 图片资源 16 | enum ImgSource { 17 | case SERVER(url:URL) 18 | case LOCAL(name:String) 19 | } 20 | 21 | // 图片类型 22 | enum ImgType:Int { 23 | case SERVER = 0 // default 24 | case LOCAL = 1 25 | } 26 | 27 | struct Proxy 28 | { 29 | var imgType:ImgType = .SERVER 30 | var imgArray:[ImgSource] = [ImgSource]() 31 | 32 | // 下标法获取imgArray中对应索引的ImgSource eg: proxy[0] == imgArray[0] 33 | subscript (index:Int) -> ImgSource { 34 | get { 35 | return imgArray[index] 36 | } 37 | } 38 | 39 | // 构造方法 40 | init(type:ImgType, array:[String]) 41 | { 42 | imgType = type 43 | if imgType == .SERVER 44 | { 45 | imgArray = array.map({ (urlStr) -> ImgSource in 46 | return ImgSource.SERVER(url: URL(string: urlStr)!) 47 | }) 48 | } 49 | else 50 | { 51 | imgArray = array.map({ (name) -> ImgSource in 52 | return ImgSource.LOCAL(name: name) 53 | }) 54 | } 55 | } 56 | } 57 | 58 | 59 | ///////////////////////////////////////////////////////////////////////////// 60 | // MARK: pageControl 相关 61 | ///////////////////////////////////////////////////////////////////////////// 62 | 63 | private let WRPageControlMargin: CGFloat = 15 64 | private let WRPageControlPointWidth: CGFloat = 2 65 | 66 | enum PageControlAliment { 67 | case CenterBottom 68 | case RightBottom 69 | case LeftBottom 70 | } 71 | 72 | protocol PageControlAlimentProtocol 73 | { 74 | // Property in protocol must have explicit { get } or { get set } specifier 75 | var pageControlAliment: PageControlAliment { get set } 76 | var pageControlPointSpace: CGFloat { get set } 77 | func relayoutPageControl(pageControl: WRPageControl) 78 | func relayoutPageControl(pageControl: WRPageControl, outerFrame:CGRect) 79 | } 80 | 81 | extension PageControlAlimentProtocol where Self : UIView 82 | { // TODO: 等待优化 83 | func relayoutPageControl(pageControl: WRPageControl) 84 | { 85 | if pageControl.isHidden == false 86 | { 87 | let pageH:CGFloat = 20//pageControl.pageSize.height 88 | let pageY = bounds.height - pageH 89 | let pageW = pageControl.pageSize.width 90 | var pageX:CGFloat = 0 91 | 92 | switch self.pageControlAliment { 93 | case .CenterBottom: 94 | pageX = CGFloat(self.bounds.width / 2) - pageW * 0.5 95 | case .RightBottom: 96 | pageX = bounds.width - pageW - WRPageControlMargin 97 | case .LeftBottom: 98 | pageX = bounds.origin.x + WRPageControlMargin 99 | } 100 | pageControl.frame = CGRect(x:pageX, y:pageY, width:pageW, height:pageH) 101 | } 102 | } 103 | func relayoutPageControl(pageControl: WRPageControl, outerFrame:CGRect) 104 | { 105 | if pageControl.isHidden == false { 106 | pageControl.frame = CGRect(x:outerFrame.origin.x, y:outerFrame.origin.y, width:pageControl.pageSize.width, height:pageControl.pageSize.height) 107 | } 108 | } 109 | } 110 | 111 | 112 | ///////////////////////////////////////////////////////////////////////////// 113 | // MARK: 无限轮播 相关 114 | ///////////////////////////////////////////////////////////////////////////// 115 | 116 | protocol EndlessScrollProtocol 117 | { 118 | ///////////////////////////////// 119 | /// 是否自动滚动 120 | var isAutoScroll: Bool { get set } 121 | /// 自动滚动的时间间隔 122 | var autoScrollInterval: Double { get set } 123 | /// 用于自动滚动的定时器 124 | var timer:Timer? { get set } 125 | 126 | ///////////////////////////////// 127 | /// 是否无限轮播 128 | var isEndlessScroll: Bool { get set } 129 | /// 无线轮播中,一组图片最多轮播多少次 130 | var endlessScrollTimes: Int { get } 131 | /// 真实的cell数量 132 | var itemsInSection: Int { get } 133 | 134 | /** 设置定时器,用于自动滚动 */ 135 | func setupTimer() 136 | 137 | /** 滚动到第一个cell (在无限轮播中就是中间的那个cell) */ 138 | func changeToFirstCycleCell(animated: Bool, collectionView: UICollectionView) 139 | } 140 | 141 | extension EndlessScrollProtocol where Self : UIView 142 | { 143 | func changeCycleCell(collectionView: UICollectionView) 144 | { 145 | guard itemsInSection != 0 else { 146 | return 147 | } 148 | 149 | let flowLayout = collectionView.collectionViewLayout as! UICollectionViewFlowLayout 150 | let curItem = Int(collectionView.contentOffset.x / flowLayout.itemSize.width) 151 | if curItem == itemsInSection - 1 152 | { 153 | let animated = (isEndlessScroll == true) ? false : true 154 | changeToFirstCycleCell(animated: animated, collectionView: collectionView) 155 | } 156 | else 157 | { 158 | let indexPath = IndexPath(item: curItem + 1, section: 0) 159 | collectionView.scrollToItem(at: indexPath, at: .init(rawValue: 0), animated: true) 160 | } 161 | } 162 | 163 | func changeToFirstCycleCell(animated: Bool, collectionView: UICollectionView) 164 | { 165 | guard itemsInSection != 0 else { 166 | return 167 | } 168 | 169 | let firstItem = (isEndlessScroll == true) ? (itemsInSection / 2) : 0 170 | let indexPath = IndexPath(item: firstItem, section: 0) 171 | collectionView.scrollToItem(at: indexPath, at: .init(rawValue: 0), animated: animated) 172 | } 173 | } 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/WRCycleScrollViewDemo/ZhiHuController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ZhiHuController.swift 3 | // ZhihuDaily-Swift3.0 4 | // 5 | // Created by wangrui on 2017/5/5. 6 | // Copyright © 2017年 wangrui. All rights reserved. 7 | // 8 | // Github地址:https://github.com/wangrui460/WRNavigationBar_swift 9 | 10 | import UIKit 11 | 12 | private let NAVBAR_COLORCHANGE_POINT:CGFloat = -80 13 | private let IMAGE_HEIGHT:CGFloat = 240 14 | private let SCROLL_DOWN_LIMIT:CGFloat = 100 15 | private let LIMIT_OFFSET_Y:CGFloat = -(IMAGE_HEIGHT + SCROLL_DOWN_LIMIT) 16 | 17 | class ZhiHuController: UIViewController 18 | { 19 | lazy var tableView:UITableView = { 20 | let table:UITableView = UITableView(frame: CGRect.init(x: 0, y: 0, width: Int(kScreenWidth), height: Int(self.view.bounds.height)), style: .plain) 21 | table.contentInset = UIEdgeInsetsMake(IMAGE_HEIGHT-CGFloat(kNavBarBottom), 0, 0, 0); 22 | table.showsVerticalScrollIndicator = false 23 | table.delegate = self 24 | table.dataSource = self 25 | return table 26 | }() 27 | lazy var cycleScrollView:WRCycleScrollView = { 28 | let frame = CGRect(x: 0, y: -IMAGE_HEIGHT, width: CGFloat(kScreenWidth), height: IMAGE_HEIGHT) 29 | let cycleView = WRCycleScrollView(frame: frame, type: .LOCAL, imgs: nil, descs: nil) 30 | return cycleView 31 | }() 32 | 33 | override func viewDidLoad() 34 | { 35 | super.viewDidLoad() 36 | self.title = "知乎日报" 37 | view.backgroundColor = UIColor.white 38 | let localImages = ["localImg6","localImg7","localImg8","localImg9","localImg10"] 39 | let descs = ["韩国防部回应停止部署萨德:遵照最高统帅指导方针", 40 | "勒索病毒攻击再次爆发 国内校园网大面积感染", 41 | "Win10秋季更新重磅功能:跟安卓与iOS无缝连接", 42 | "《琅琊榜2》为何没有胡歌?胡歌:我看过剧本,离开是种保护", 43 | "阿米尔汗在印度的影响力,我国的哪位影视明星能与之齐名呢?"] 44 | cycleScrollView.localImgArray = localImages 45 | cycleScrollView.descTextArray = descs 46 | cycleScrollView.descLabelFont = UIFont.boldSystemFont(ofSize: 16) 47 | tableView.addSubview(cycleScrollView) 48 | view.addSubview(tableView) 49 | 50 | navBarBarTintColor = MainNavBarColor 51 | navBarBackgroundAlpha = 0 52 | navBarTintColor = .white 53 | } 54 | 55 | deinit { 56 | tableView.delegate = nil 57 | print("ZhiHuVC deinit") 58 | } 59 | } 60 | 61 | // MARK: - ScrollViewDidScroll 62 | extension ZhiHuController 63 | { 64 | func scrollViewDidScroll(_ scrollView: UIScrollView) 65 | { 66 | let offsetY = scrollView.contentOffset.y 67 | 68 | if (offsetY > NAVBAR_COLORCHANGE_POINT) 69 | { 70 | let alpha = (offsetY - NAVBAR_COLORCHANGE_POINT) / CGFloat(kNavBarBottom) 71 | navBarBackgroundAlpha = alpha 72 | } 73 | else 74 | { 75 | navBarBackgroundAlpha = 0 76 | } 77 | 78 | // 限制下拉距离 79 | if (offsetY < LIMIT_OFFSET_Y) { 80 | scrollView.contentOffset = CGPoint.init(x: 0, y: LIMIT_OFFSET_Y) 81 | } 82 | 83 | // 改变图片框的大小 (上滑的时候不改变) 84 | // 这里不能使用offsetY,因为当(offsetY < LIMIT_OFFSET_Y)的时候,y = LIMIT_OFFSET_Y 不等于 offsetY 85 | let newOffsetY = scrollView.contentOffset.y 86 | if (newOffsetY < -IMAGE_HEIGHT) 87 | { 88 | cycleScrollView.frame = CGRect(x: 0, y: newOffsetY, width: CGFloat(kScreenWidth), height: -newOffsetY) 89 | } 90 | } 91 | 92 | // private 93 | fileprivate func imageScaledToSize(image:UIImage, newSize:CGSize) -> UIImage 94 | { 95 | UIGraphicsBeginImageContext(CGSize(width: newSize.width * 2.0, height: newSize.height * 2.0)) 96 | image.draw(in: CGRect(x: 0, y: 0, width: newSize.width * 2.0, height: newSize.height * 2.0)) 97 | let newImage:UIImage = UIGraphicsGetImageFromCurrentImageContext() ?? image 98 | UIGraphicsEndImageContext() 99 | return newImage 100 | } 101 | } 102 | 103 | extension ZhiHuController: UITableViewDelegate,UITableViewDataSource 104 | { 105 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 106 | return 15 107 | } 108 | 109 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell 110 | { 111 | let cell = UITableViewCell.init(style: .default, reuseIdentifier: nil) 112 | let str = String(format: "知乎日报 %zd", indexPath.row) 113 | cell.textLabel?.text = str 114 | cell.textLabel?.font = UIFont.systemFont(ofSize: 15) 115 | return cell 116 | } 117 | 118 | func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) 119 | { 120 | tableView.deselectRow(at: indexPath, animated: true) 121 | let vc:UIViewController = UIViewController() 122 | vc.view.backgroundColor = UIColor.white 123 | let str = String(format: "知乎日报 %zd", indexPath.row) 124 | vc.title = str 125 | navigationController?.pushViewController(vc, animated: true) 126 | } 127 | } 128 | 129 | 130 | 131 | -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/WRCycleScrollViewDemoTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /WRCycleScrollViewDemo/WRCycleScrollViewDemoTests/WRCycleScrollViewDemoTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WRCycleScrollViewDemoTests.swift 3 | // WRCycleScrollViewDemoTests 4 | // 5 | // Created by wangrui on 2017/5/12. 6 | // Copyright © 2017年 wangrui. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import WRCycleScrollViewDemo 11 | 12 | class WRCycleScrollViewDemoTests: XCTestCase { 13 | 14 | override func setUp() { 15 | super.setUp() 16 | // Put setup code here. This method is called before the invocation of each test method in the class. 17 | } 18 | 19 | override func tearDown() { 20 | // Put teardown code here. This method is called after the invocation of each test method in the class. 21 | super.tearDown() 22 | } 23 | 24 | func testExample() { 25 | // This is an example of a functional test case. 26 | // Use XCTAssert and related functions to verify your tests produce the correct results. 27 | } 28 | 29 | func testPerformanceExample() { 30 | // This is an example of a performance test case. 31 | self.measure { 32 | // Put the code you want to measure the time of here. 33 | } 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /screenshots/StoryBoard创建.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangrui460/WRCycleScrollView/d48d12003bb5c5d7c3e52abec63510b5b5c692e5/screenshots/StoryBoard创建.gif -------------------------------------------------------------------------------- /screenshots/WRCycleScrollView.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangrui460/WRCycleScrollView/d48d12003bb5c5d7c3e52abec63510b5b5c692e5/screenshots/WRCycleScrollView.png -------------------------------------------------------------------------------- /screenshots/demos.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangrui460/WRCycleScrollView/d48d12003bb5c5d7c3e52abec63510b5b5c692e5/screenshots/demos.png -------------------------------------------------------------------------------- /screenshots/不无限轮播.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangrui460/WRCycleScrollView/d48d12003bb5c5d7c3e52abec63510b5b5c692e5/screenshots/不无限轮播.gif -------------------------------------------------------------------------------- /screenshots/不显示pageControl.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangrui460/WRCycleScrollView/d48d12003bb5c5d7c3e52abec63510b5b5c692e5/screenshots/不显示pageControl.gif -------------------------------------------------------------------------------- /screenshots/本地图片轮播.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangrui460/WRCycleScrollView/d48d12003bb5c5d7c3e52abec63510b5b5c692e5/screenshots/本地图片轮播.gif -------------------------------------------------------------------------------- /screenshots/知乎日报.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangrui460/WRCycleScrollView/d48d12003bb5c5d7c3e52abec63510b5b5c692e5/screenshots/知乎日报.gif -------------------------------------------------------------------------------- /screenshots/网络图片轮播.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangrui460/WRCycleScrollView/d48d12003bb5c5d7c3e52abec63510b5b5c692e5/screenshots/网络图片轮播.gif --------------------------------------------------------------------------------