Repository: yangKJ/KJBannerViewDemo Branch: master Commit: 1f98763f62bd Files: 82 Total size: 283.4 KB Directory structure: gitextract_0063ocvp/ ├── .gitignore ├── CHANGELOG.md ├── KJBannerView.podspec ├── KJBannerViewDemo/ │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Masonry/ │ │ ├── MASCompositeConstraint.h │ │ ├── MASCompositeConstraint.m │ │ ├── MASConstraint+Private.h │ │ ├── MASConstraint.h │ │ ├── MASConstraint.m │ │ ├── MASConstraintMaker.h │ │ ├── MASConstraintMaker.m │ │ ├── MASLayoutConstraint.h │ │ ├── MASLayoutConstraint.m │ │ ├── MASUtilities.h │ │ ├── MASViewAttribute.h │ │ ├── MASViewAttribute.m │ │ ├── MASViewConstraint.h │ │ ├── MASViewConstraint.m │ │ ├── Masonry.h │ │ ├── NSArray+MASAdditions.h │ │ ├── NSArray+MASAdditions.m │ │ ├── NSArray+MASShorthandAdditions.h │ │ ├── NSLayoutConstraint+MASDebugAdditions.h │ │ ├── NSLayoutConstraint+MASDebugAdditions.m │ │ ├── View+MASAdditions.h │ │ ├── View+MASAdditions.m │ │ ├── View+MASShorthandAdditions.h │ │ ├── ViewController+MASAdditions.h │ │ └── ViewController+MASAdditions.m │ ├── Modules/ │ │ ├── Controller/ │ │ │ ├── DownloadViewController.h │ │ │ ├── DownloadViewController.m │ │ │ ├── ViewController.h │ │ │ └── ViewController.m │ │ ├── Model/ │ │ │ ├── KJBannerModel.h │ │ │ └── KJBannerModel.m │ │ ├── View/ │ │ │ ├── KJCollectionViewCell.h │ │ │ └── KJCollectionViewCell.m │ │ └── ViewModel/ │ │ ├── KJViewModel.h │ │ └── KJViewModel.m │ └── Supporting Files/ │ ├── Assets.xcassets/ │ │ ├── AppIcon.appiconset/ │ │ │ └── Contents.json │ │ ├── Contents.json │ │ ├── IMG_4931.imageset/ │ │ │ ├── Contents.json │ │ │ ├── Contents.json~Add 2.0.3 │ │ │ ├── Contents.json~HEAD │ │ │ ├── IMG_4931.jpg~Add 2.0.3 │ │ │ └── IMG_4931.jpg~HEAD │ │ ├── IMG_Guitar_52.imageset/ │ │ │ └── Contents.json │ │ ├── ax.imageset/ │ │ │ └── Contents.json │ │ └── z_ds.imageset/ │ │ └── Contents.json │ ├── Base.lproj/ │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ ├── Info.plist │ └── main.m ├── KJBannerViewDemo.xcodeproj/ │ ├── project.pbxproj │ ├── project.xcworkspace/ │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata/ │ │ └── IDEWorkspaceChecks.plist │ └── xcshareddata/ │ └── xcschemes/ │ └── KJBannerViewDemo.xcscheme ├── LICENSE ├── README.md └── Sources/ ├── KJBannerHeader.h ├── KJBannerView/ │ ├── KJBannerView.h │ ├── KJBannerView.m │ ├── KJBannerViewCell.h │ ├── KJBannerViewCell.m │ ├── KJBannerViewFlowLayout.h │ ├── KJBannerViewFlowLayout.m │ ├── KJBannerViewTimer.h │ └── KJBannerViewTimer.m ├── PageControl/ │ ├── KJPageControl.h │ └── KJPageControl.m └── WebImage/ ├── KJAutoPurgingCache.h ├── KJAutoPurgingCache.m ├── KJImageCache.h ├── KJImageCache.m ├── KJNetworkManager.h ├── KJNetworkManager.m ├── KJWebImageDownloader.h ├── KJWebImageDownloader.m ├── KJWebImageHeader.h ├── UIView+KJWebImage.h └── UIView+KJWebImage.m ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ # Xcode # # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore ## User settings xcuserdata/ ## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) *.xcscmblueprint *.xccheckout ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) build/ DerivedData/ *.moved-aside *.pbxuser !default.pbxuser *.mode1v3 !default.mode1v3 *.mode2v3 !default.mode2v3 *.perspectivev3 !default.perspectivev3 ## Obj-C/Swift specific *.hmap ## App packaging *.ipa *.dSYM.zip *.dSYM # CocoaPods # # We recommend against adding the Pods directory to your .gitignore. However # you should judge for yourself, the pros and cons are mentioned at: # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control # # Pods/ # # Add this line if you want to avoid checking in source code from the Xcode workspace # *.xcworkspace # Carthage # # Add this line if you want to avoid checking in source code from Carthage dependencies. # Carthage/Checkouts Carthage/Build/ # fastlane # # It is recommended to not store the screenshots in the git repo. # Instead, use fastlane to re-generate the screenshots whenever they are needed. # For more information about the recommended setup visit: # https://docs.fastlane.tools/best-practices/source-control/#source-control fastlane/report.xml fastlane/Preview.html fastlane/screenshots/**/*.png fastlane/test_output # Code Injection # # After new code Injection tools there's a generated folder /iOSInjectionProject # https://github.com/johnno1962/injectionforxcode iOSInjectionProject/ ================================================ FILE: CHANGELOG.md ================================================ # 功能介绍 KJBannerView 是一款轮播Banner,支持gif和url混播,自带图片下载和缓存 1. 缩放无限自动循环滚动 √ 2. 支持四种方向滚动,从左往右、从右往左、从上往下、从下往上 √ 3. 自定义继承,定制不同样式,详情使用请见Demo √ 4. 支持网络动态图和网络图片和本地图片混合轮播 √ 5. 支持在Storyboard和Xib中创建并配置其属性 √ 6. 提供多种分页控件PageControl显示 √ 7. 自带缓存加载,内部封装网图下载缓存工具 √ 8. 清理指定时间段以前的图片资源数据 √ > 备注:快捷打开浏览器命令,command + shift + 鼠标左键 # 版本更新日志 ### [版本3.0.1](https://github.com/yangKJ/KJBannerViewDemo/tree/3.0.1) - 拆分bannerView分类方法至`KJBannerViewCell` - 完善预加载模板,结构命名调整 ### [版本3.0.0](https://github.com/yangKJ/KJBannerViewDemo/tree/3.0.0) - 改版重构,采用协议类似`UITableView`的处理方式 - 删除废弃代码和类等 - 抽离异步计时器`KJBannerViewTimer` ### [版本2.1.7](https://github.com/yangKJ/KJBannerViewDemo/tree/2.1.7) - 规范代码,删除计数器 - bannerScale 默认不裁剪 ### [版本2.1.6](https://github.com/yangKJ/KJBannerViewDemo/tree/2.1.6) - 解决一条数据点击不响应问题 - 优化一下,规范代码 ### [版本2.1.3](https://github.com/yangKJ/KJBannerViewDemo/tree/2.1.3) - 抽离网络加载部分,pod 'KJBannerView/Downloader' - 新增网络加载部分预渲染图片处理 `bannerPreRendering` ### [版本2.1.2](https://github.com/yangKJ/KJBannerViewDemo/tree/2.1.2) - 更高效的圆角切割,避免离屏渲染 - 更换计时器,替换为异步GCD计时器 - 新增定制特定方位圆角字段 - 修复字段重名情况 ### [版本2.1.0](https://github.com/yangKJ/KJBannerViewDemo/tree/2.1.0) - 更换动态图处理播放控件,移除数据源类型 - 修改为异步播放本地动态图 - 去除原先的 UIImageView+KJWebImage、UIButton+KJWebImage - 合并网图加载分类 UIView+KJWebImage - 新增 KJBannerTimingClearManager 清理指定时间段以前的图片资源数据 ### [版本2.0.11](https://github.com/yangKJ/KJBannerViewDemo/tree/2.0.11) - 简练代码逻辑,整理修改重复代码 - 新增 UIView+KJWebImage 显示View内容图片 ### [版本2.0.10](https://github.com/yangKJ/KJBannerViewDemo/tree/2.0.10) - 修改网图显示,新增 KJBannerWebImageHandle 关联 - 性能优化,完善逻辑处理 ### [版本2.0.9](https://github.com/yangKJ/KJBannerViewDemo/tree/2.0.9) - 移除不再使用数据,优化逻辑操作 - 新增 UIImageView+KJWebImage 图片显示处理 ### [版本2.0.8](https://github.com/yangKJ/KJBannerViewDemo/tree/2.0.8) - 解决偶尔出现 Thread 1: EXC_ARITHMETIC (code=EXC_I386_DIV, subcode=0x0) - 修复首次加载不出现问题,优化性能 ### [版本2.0.7](https://github.com/yangKJ/KJBannerViewDemo/tree/2.0.7) - KJPageView 新增显示位置属性 displayType 和 距离边界间隙 space - 完善自定义控件方式 itemClass ### [版本2.0.6](https://github.com/yangKJ/KJBannerViewDemo/tree/2.0.6) - 属性 rollType 新增从上往下和从下往上两种滚动方向 - 新增属性 showPageControl 是否显示分页控件 - 适配Masonry布局,请调用方法 kj_useMasonry ### [版本2.0.5](https://github.com/yangKJ/KJBannerViewDemo/tree/2.0.5) - KJPageView 优化修改选中 ### [版本2.0.4](https://github.com/yangKJ/KJBannerViewDemo/tree/2.0.4) - KJBannerViewLoadManager 新增是否开启异步字段 `useAsync` - 新增 KJBannerViewCacheManager+KJBannerGIF 动态图缓存相关 ### [版本2.0.3](https://github.com/yangKJ/KJBannerViewDemo/tree/2.0.3) - 新增本地动态图播放 - KJBannerView 新增是否需要动态图缓存字段 `openGIFCache` ### [版本2.0.1](https://github.com/yangKJ/KJBannerViewDemo/tree/2.0.1) - 优化自带第一条数据不显示问题 - 优化卡顿问题 - KJBannerView 修改xib属性字段名 ### [版本2.0.0](https://github.com/yangKJ/KJBannerViewDemo/tree/2.0.0) - 重写网络请求板块, - 封装网络请求工具:KJBannerViewDownloader - 缓存工具:KJBannerViewCacheManager - 网图下载工具:KJBannerViewLoadManager ### [版本1.3.8](https://github.com/yangKJ/KJBannerViewDemo/tree/1.3.8) - 新增委托方法 `kj_BannerViewDidScroll:` ### [版本1.3.7](https://github.com/yangKJ/KJBannerViewDemo/tree/1.3.7) - 新增动态图分类,替换原先的动态图播放方式 - 去掉单例,优化数据的获取方式 - 解决数据源为空的处理 ### [版本1.3.6](https://github.com/yangKJ/KJBannerViewDemo/tree/1.3.6) - KJPageView 新增属性 margin 用于方块之间微微调整 - KJPageView 新增属性 dotwidth和dotheight 用于方块尺寸调整 - KJPageView 优化解决不居中问题 ### [版本1.3.5](https://github.com/yangKJ/KJBannerViewDemo/tree/1.3.5) - 独立委托协议类KJBannerViewProtocol,归类代码更加简洁 - 新增滚动回调 `kScrollBlock` ### [版本1.3.4](https://github.com/yangKJ/KJBannerViewDemo/tree/1.3.4) - 解决只有一张图片显示异常问题 - 多线程处理gif数据,再次提升效率 ### [版本1.3.3](https://github.com/yangKJ/KJBannerViewDemo/tree/1.3.3) - 优化图片下载速率,解决卡顿问题 - 修改`kj_BannerView:BannerViewCell:ImageDatas:Index:`委托方法,解决Memory疯涨问题 ### [版本1.3.2](https://github.com/yangKJ/KJBannerViewDemo/tree/1.3.2) - 新增 NSTimer+KJSolve 解决计时器循环引用 ### [版本1.3.0](https://github.com/yangKJ/KJBannerViewDemo/tree/1.3.0) - 新增KJBannerViewDataSource委托,更方便的自定义方式 不需要再继承 KJBannerViewCell - `kj_BannerView:BannerViewCell:ImageDatas:Index:`此方法和 `itemClass` 互斥 - Banner支持在Storyboard和Xib中创建并配置其属性 - 新增裁剪网络图片从而提高效率 bannerScale ### [版本1.2.6](https://github.com/yangKJ/KJBannerViewDemo/tree/1.2.6) - KJPageView 新增大小点类型 PageControlStyleSizeDot - 优化修改网友提出的卡顿问题 - 移出 KJBannerViewCell 当中的判断处理,从而提高效率 ### [版本1.2.5](https://github.com/yangKJ/KJBannerViewDemo/tree/1.2.5) - 新增委托方法 `kj_BannerView:CurrentIndex:` 滚动时候回调 可是否隐藏自带的PageControl - 优化性能,修复重复创建PageControl ### [版本1.2.4](https://github.com/yangKJ/KJBannerViewDemo/tree/1.2.4) - 新增本地和网络图片混合,自带判断方式,去掉以前的本地判断方式 - 新增Gif图显示,支持本地图片、网络图片、网络GIF图片混合显示 - KJBannerViewImageType 控制图片的显示类型 ### [版本1.2.2](https://github.com/yangKJ/KJBannerViewDemo/tree/1.2.2) - 修改pageControl样式颜色的修改方式,从而提高效率 ### [版本1.2.1](https://github.com/yangKJ/KJBannerViewDemo/tree/1.2.1) - 再次优化,提高性能 - 新增自带Cell显示本地图片 `isLocalityImage` ### [版本1.2.0](https://github.com/yangKJ/KJBannerViewDemo/tree/1.2.0) - KJPageView 支持3种样式(圆形,长方形,正方形) - 重新整理,从而提高轮播图性能 - 自带Cell新增默认占位图,一条数据时隐藏KJPageControl ### [版本1.1.1](https://github.com/yangKJ/KJBannerViewDemo/tree/1.1.1) - 新增支持Swift宏 - 新增Block代理点击事件 KJBannerViewBlock - 新增设置滚动方向属性 `rollType` ### [版本1.1.0](https://github.com/yangKJ/KJBannerViewDemo/tree/1.1.0) - 新增支持自定义Cell - 继承KJBannerViewCell,然后在model设置数据 ### 版本1.0.2 - 新增 KJBannerView 轮播图 - banner支持缩放 - 自带图片下载和缓存相关功能,无三方依赖、轻量级组件 # 作者信息 ### [Github地址](https://github.com/yangKJ) | [简书地址](https://www.jianshu.com/u/c84c00476ab6) | [博客地址](https://blog.csdn.net/qq_34534179) | [掘金地址](https://juejin.cn/user/1987535102554472/posts) ================================================ FILE: KJBannerView.podspec ================================================ Pod::Spec.new do |s| s.name = "KJBannerView" s.version = "3.1.0" s.summary = "KJBannerView是一款自带图片下载和缓存轮播Banner,支持动态图和网图混合显示" s.homepage = "https://github.com/yangKJ/KJBannerViewDemo" s.license = "MIT" s.license = {:type => "MIT", :file => "LICENSE"} s.license = "Copyright (c) 2018 yangkejun" s.author = {"77" => "ykj310@126.com"} s.source = {:git => "https://github.com/yangKJ/KJBannerViewDemo.git", :tag => "#{s.version}"} s.platform = :ios s.requires_arc = true s.static_framework = true s.ios.deployment_target = '9.0' s.ios.pod_target_xcconfig = { 'PRODUCT_BUNDLE_IDENTIFIER' => 'com.yangkejun.KJBannerViewDemo' } s.default_subspec = 'BannerView' s.ios.source_files = 'Sources/KJBannerHeader.h' s.subspec 'BannerView' do |ss| ss.source_files = "Sources/KJBannerView/*.{h,m}","Sources/PageControl/*.{h,m}" ss.resources = "Sources/KJBannerView/*.{bundle}" end s.subspec 'Downloader' do |ss| ss.source_files = "Sources/WebImage/*.{h,m}" end end ================================================ FILE: KJBannerViewDemo/AppDelegate.h ================================================ // // AppDelegate.h // KJBannerViewDemo // // Created by 杨科军 on 2018/12/22. // Copyright © 2018 杨科军. All rights reserved. // https://github.com/yangKJ/KJBannerViewDemo #import @interface AppDelegate : UIResponder @property (strong, nonatomic) UIWindow *window; @end ================================================ FILE: KJBannerViewDemo/AppDelegate.m ================================================ // // AppDelegate.m // KJBannerViewDemo // // Created by 杨科军 on 2018/12/22. // Copyright © 2018 杨科军. All rights reserved. // https://github.com/yangKJ/KJBannerViewDemo #import "AppDelegate.h" #import "KJWebImageHeader.h" @interface AppDelegate () @end @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // Override point for customization after application launch. //清除三天前缓存的数据 [KJAutoPurgingCache autoPurgingCache:YES timingTimeType:(KJAutoPurginCacheTypeThreeDay)]; return YES; } - (void)applicationWillResignActive:(UIApplication *)application { // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. } - (void)applicationDidEnterBackground:(UIApplication *)application { // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. } - (void)applicationWillEnterForeground:(UIApplication *)application { // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. } - (void)applicationDidBecomeActive:(UIApplication *)application { // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. } - (void)applicationWillTerminate:(UIApplication *)application { // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. } @end ================================================ FILE: KJBannerViewDemo/Masonry/MASCompositeConstraint.h ================================================ // // MASCompositeConstraint.h // Masonry // // Created by Jonas Budelmann on 21/07/13. // Copyright (c) 2013 cloudling. All rights reserved. // #import "MASConstraint.h" #import "MASUtilities.h" /** * A group of MASConstraint objects */ @interface MASCompositeConstraint : MASConstraint /** * Creates a composite with a predefined array of children * * @param children child MASConstraints * * @return a composite constraint */ - (id)initWithChildren:(NSArray *)children; @end ================================================ FILE: KJBannerViewDemo/Masonry/MASCompositeConstraint.m ================================================ // // MASCompositeConstraint.m // Masonry // // Created by Jonas Budelmann on 21/07/13. // Copyright (c) 2013 cloudling. All rights reserved. // #import "MASCompositeConstraint.h" #import "MASConstraint+Private.h" @interface MASCompositeConstraint () @property (nonatomic, strong) id mas_key; @property (nonatomic, strong) NSMutableArray *childConstraints; @end @implementation MASCompositeConstraint - (id)initWithChildren:(NSArray *)children { self = [super init]; if (!self) return nil; _childConstraints = [children mutableCopy]; for (MASConstraint *constraint in _childConstraints) { constraint.delegate = self; } return self; } #pragma mark - MASConstraintDelegate - (void)constraint:(MASConstraint *)constraint shouldBeReplacedWithConstraint:(MASConstraint *)replacementConstraint { NSUInteger index = [self.childConstraints indexOfObject:constraint]; NSAssert(index != NSNotFound, @"Could not find constraint %@", constraint); [self.childConstraints replaceObjectAtIndex:index withObject:replacementConstraint]; } - (MASConstraint *)constraint:(MASConstraint __unused *)constraint addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute { id strongDelegate = self.delegate; MASConstraint *newConstraint = [strongDelegate constraint:self addConstraintWithLayoutAttribute:layoutAttribute]; newConstraint.delegate = self; [self.childConstraints addObject:newConstraint]; return newConstraint; } #pragma mark - NSLayoutConstraint multiplier proxies - (MASConstraint * (^)(CGFloat))multipliedBy { return ^id(CGFloat multiplier) { for (MASConstraint *constraint in self.childConstraints) { constraint.multipliedBy(multiplier); } return self; }; } - (MASConstraint * (^)(CGFloat))dividedBy { return ^id(CGFloat divider) { for (MASConstraint *constraint in self.childConstraints) { constraint.dividedBy(divider); } return self; }; } #pragma mark - MASLayoutPriority proxy - (MASConstraint * (^)(MASLayoutPriority))priority { return ^id(MASLayoutPriority priority) { for (MASConstraint *constraint in self.childConstraints) { constraint.priority(priority); } return self; }; } #pragma mark - NSLayoutRelation proxy - (MASConstraint * (^)(id, NSLayoutRelation))equalToWithRelation { return ^id(id attr, NSLayoutRelation relation) { for (MASConstraint *constraint in self.childConstraints.copy) { constraint.equalToWithRelation(attr, relation); } return self; }; } #pragma mark - attribute chaining - (MASConstraint *)addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute { [self constraint:self addConstraintWithLayoutAttribute:layoutAttribute]; return self; } #pragma mark - Animator proxy #if TARGET_OS_MAC && !(TARGET_OS_IPHONE || TARGET_OS_TV) - (MASConstraint *)animator { for (MASConstraint *constraint in self.childConstraints) { [constraint animator]; } return self; } #endif #pragma mark - debug helpers - (MASConstraint * (^)(id))key { return ^id(id key) { self.mas_key = key; int i = 0; for (MASConstraint *constraint in self.childConstraints) { constraint.key([NSString stringWithFormat:@"%@[%d]", key, i++]); } return self; }; } #pragma mark - NSLayoutConstraint constant setters - (void)setInsets:(MASEdgeInsets)insets { for (MASConstraint *constraint in self.childConstraints) { constraint.insets = insets; } } - (void)setInset:(CGFloat)inset { for (MASConstraint *constraint in self.childConstraints) { constraint.inset = inset; } } - (void)setOffset:(CGFloat)offset { for (MASConstraint *constraint in self.childConstraints) { constraint.offset = offset; } } - (void)setSizeOffset:(CGSize)sizeOffset { for (MASConstraint *constraint in self.childConstraints) { constraint.sizeOffset = sizeOffset; } } - (void)setCenterOffset:(CGPoint)centerOffset { for (MASConstraint *constraint in self.childConstraints) { constraint.centerOffset = centerOffset; } } #pragma mark - MASConstraint - (void)activate { for (MASConstraint *constraint in self.childConstraints) { [constraint activate]; } } - (void)deactivate { for (MASConstraint *constraint in self.childConstraints) { [constraint deactivate]; } } - (void)install { for (MASConstraint *constraint in self.childConstraints) { constraint.updateExisting = self.updateExisting; [constraint install]; } } - (void)uninstall { for (MASConstraint *constraint in self.childConstraints) { [constraint uninstall]; } } @end ================================================ FILE: KJBannerViewDemo/Masonry/MASConstraint+Private.h ================================================ // // MASConstraint+Private.h // Masonry // // Created by Nick Tymchenko on 29/04/14. // Copyright (c) 2014 cloudling. All rights reserved. // #import "MASConstraint.h" @protocol MASConstraintDelegate; @interface MASConstraint () /** * Whether or not to check for an existing constraint instead of adding constraint */ @property (nonatomic, assign) BOOL updateExisting; /** * Usually MASConstraintMaker but could be a parent MASConstraint */ @property (nonatomic, weak) id delegate; /** * Based on a provided value type, is equal to calling: * NSNumber - setOffset: * NSValue with CGPoint - setPointOffset: * NSValue with CGSize - setSizeOffset: * NSValue with MASEdgeInsets - setInsets: */ - (void)setLayoutConstantWithValue:(NSValue *)value; @end @interface MASConstraint (Abstract) /** * Sets the constraint relation to given NSLayoutRelation * returns a block which accepts one of the following: * MASViewAttribute, UIView, NSValue, NSArray * see readme for more details. */ - (MASConstraint * (^)(id, NSLayoutRelation))equalToWithRelation; /** * Override to set a custom chaining behaviour */ - (MASConstraint *)addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute; @end @protocol MASConstraintDelegate /** * Notifies the delegate when the constraint needs to be replaced with another constraint. For example * A MASViewConstraint may turn into a MASCompositeConstraint when an array is passed to one of the equality blocks */ - (void)constraint:(MASConstraint *)constraint shouldBeReplacedWithConstraint:(MASConstraint *)replacementConstraint; - (MASConstraint *)constraint:(MASConstraint *)constraint addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute; @end ================================================ FILE: KJBannerViewDemo/Masonry/MASConstraint.h ================================================ // // MASConstraint.h // Masonry // // Created by Jonas Budelmann on 22/07/13. // Copyright (c) 2013 cloudling. All rights reserved. // #import "MASUtilities.h" /** * Enables Constraints to be created with chainable syntax * Constraint can represent single NSLayoutConstraint (MASViewConstraint) * or a group of NSLayoutConstraints (MASComposisteConstraint) */ @interface MASConstraint : NSObject // Chaining Support /** * Modifies the NSLayoutConstraint constant, * only affects MASConstraints in which the first item's NSLayoutAttribute is one of the following * NSLayoutAttributeTop, NSLayoutAttributeLeft, NSLayoutAttributeBottom, NSLayoutAttributeRight */ - (MASConstraint * (^)(MASEdgeInsets insets))insets; /** * Modifies the NSLayoutConstraint constant, * only affects MASConstraints in which the first item's NSLayoutAttribute is one of the following * NSLayoutAttributeTop, NSLayoutAttributeLeft, NSLayoutAttributeBottom, NSLayoutAttributeRight */ - (MASConstraint * (^)(CGFloat inset))inset; /** * Modifies the NSLayoutConstraint constant, * only affects MASConstraints in which the first item's NSLayoutAttribute is one of the following * NSLayoutAttributeWidth, NSLayoutAttributeHeight */ - (MASConstraint * (^)(CGSize offset))sizeOffset; /** * Modifies the NSLayoutConstraint constant, * only affects MASConstraints in which the first item's NSLayoutAttribute is one of the following * NSLayoutAttributeCenterX, NSLayoutAttributeCenterY */ - (MASConstraint * (^)(CGPoint offset))centerOffset; /** * Modifies the NSLayoutConstraint constant */ - (MASConstraint * (^)(CGFloat offset))offset; /** * Modifies the NSLayoutConstraint constant based on a value type */ - (MASConstraint * (^)(NSValue *value))valueOffset; /** * Sets the NSLayoutConstraint multiplier property */ - (MASConstraint * (^)(CGFloat multiplier))multipliedBy; /** * Sets the NSLayoutConstraint multiplier to 1.0/dividedBy */ - (MASConstraint * (^)(CGFloat divider))dividedBy; /** * Sets the NSLayoutConstraint priority to a float or MASLayoutPriority */ - (MASConstraint * (^)(MASLayoutPriority priority))priority; /** * Sets the NSLayoutConstraint priority to MASLayoutPriorityLow */ - (MASConstraint * (^)(void))priorityLow; /** * Sets the NSLayoutConstraint priority to MASLayoutPriorityMedium */ - (MASConstraint * (^)(void))priorityMedium; /** * Sets the NSLayoutConstraint priority to MASLayoutPriorityHigh */ - (MASConstraint * (^)(void))priorityHigh; /** * Sets the constraint relation to NSLayoutRelationEqual * returns a block which accepts one of the following: * MASViewAttribute, UIView, NSValue, NSArray * see readme for more details. */ - (MASConstraint * (^)(id attr))equalTo; /** * Sets the constraint relation to NSLayoutRelationGreaterThanOrEqual * returns a block which accepts one of the following: * MASViewAttribute, UIView, NSValue, NSArray * see readme for more details. */ - (MASConstraint * (^)(id attr))greaterThanOrEqualTo; /** * Sets the constraint relation to NSLayoutRelationLessThanOrEqual * returns a block which accepts one of the following: * MASViewAttribute, UIView, NSValue, NSArray * see readme for more details. */ - (MASConstraint * (^)(id attr))lessThanOrEqualTo; /** * Optional semantic property which has no effect but improves the readability of constraint */ - (MASConstraint *)with; /** * Optional semantic property which has no effect but improves the readability of constraint */ - (MASConstraint *)and; /** * Creates a new MASCompositeConstraint with the called attribute and reciever */ - (MASConstraint *)left; - (MASConstraint *)top; - (MASConstraint *)right; - (MASConstraint *)bottom; - (MASConstraint *)leading; - (MASConstraint *)trailing; - (MASConstraint *)width; - (MASConstraint *)height; - (MASConstraint *)centerX; - (MASConstraint *)centerY; - (MASConstraint *)baseline; #if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (__TV_OS_VERSION_MIN_REQUIRED >= 9000) || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 101100) - (MASConstraint *)firstBaseline; - (MASConstraint *)lastBaseline; #endif #if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (__TV_OS_VERSION_MIN_REQUIRED >= 9000) - (MASConstraint *)leftMargin; - (MASConstraint *)rightMargin; - (MASConstraint *)topMargin; - (MASConstraint *)bottomMargin; - (MASConstraint *)leadingMargin; - (MASConstraint *)trailingMargin; - (MASConstraint *)centerXWithinMargins; - (MASConstraint *)centerYWithinMargins; #endif /** * Sets the constraint debug name */ - (MASConstraint * (^)(id key))key; // NSLayoutConstraint constant Setters // for use outside of mas_updateConstraints/mas_makeConstraints blocks /** * Modifies the NSLayoutConstraint constant, * only affects MASConstraints in which the first item's NSLayoutAttribute is one of the following * NSLayoutAttributeTop, NSLayoutAttributeLeft, NSLayoutAttributeBottom, NSLayoutAttributeRight */ - (void)setInsets:(MASEdgeInsets)insets; /** * Modifies the NSLayoutConstraint constant, * only affects MASConstraints in which the first item's NSLayoutAttribute is one of the following * NSLayoutAttributeTop, NSLayoutAttributeLeft, NSLayoutAttributeBottom, NSLayoutAttributeRight */ - (void)setInset:(CGFloat)inset; /** * Modifies the NSLayoutConstraint constant, * only affects MASConstraints in which the first item's NSLayoutAttribute is one of the following * NSLayoutAttributeWidth, NSLayoutAttributeHeight */ - (void)setSizeOffset:(CGSize)sizeOffset; /** * Modifies the NSLayoutConstraint constant, * only affects MASConstraints in which the first item's NSLayoutAttribute is one of the following * NSLayoutAttributeCenterX, NSLayoutAttributeCenterY */ - (void)setCenterOffset:(CGPoint)centerOffset; /** * Modifies the NSLayoutConstraint constant */ - (void)setOffset:(CGFloat)offset; // NSLayoutConstraint Installation support #if TARGET_OS_MAC && !(TARGET_OS_IPHONE || TARGET_OS_TV) /** * Whether or not to go through the animator proxy when modifying the constraint */ @property (nonatomic, copy, readonly) MASConstraint *animator; #endif /** * Activates an NSLayoutConstraint if it's supported by an OS. * Invokes install otherwise. */ - (void)activate; /** * Deactivates previously installed/activated NSLayoutConstraint. */ - (void)deactivate; /** * Creates a NSLayoutConstraint and adds it to the appropriate view. */ - (void)install; /** * Removes previously installed NSLayoutConstraint */ - (void)uninstall; @end /** * Convenience auto-boxing macros for MASConstraint methods. * * Defining MAS_SHORTHAND_GLOBALS will turn on auto-boxing for default syntax. * A potential drawback of this is that the unprefixed macros will appear in global scope. */ #define mas_equalTo(...) equalTo(MASBoxValue((__VA_ARGS__))) #define mas_greaterThanOrEqualTo(...) greaterThanOrEqualTo(MASBoxValue((__VA_ARGS__))) #define mas_lessThanOrEqualTo(...) lessThanOrEqualTo(MASBoxValue((__VA_ARGS__))) #define mas_offset(...) valueOffset(MASBoxValue((__VA_ARGS__))) #ifdef MAS_SHORTHAND_GLOBALS #define equalTo(...) mas_equalTo(__VA_ARGS__) #define greaterThanOrEqualTo(...) mas_greaterThanOrEqualTo(__VA_ARGS__) #define lessThanOrEqualTo(...) mas_lessThanOrEqualTo(__VA_ARGS__) #define offset(...) mas_offset(__VA_ARGS__) #endif @interface MASConstraint (AutoboxingSupport) /** * Aliases to corresponding relation methods (for shorthand macros) * Also needed to aid autocompletion */ - (MASConstraint * (^)(id attr))mas_equalTo; - (MASConstraint * (^)(id attr))mas_greaterThanOrEqualTo; - (MASConstraint * (^)(id attr))mas_lessThanOrEqualTo; /** * A dummy method to aid autocompletion */ - (MASConstraint * (^)(id offset))mas_offset; @end ================================================ FILE: KJBannerViewDemo/Masonry/MASConstraint.m ================================================ // // MASConstraint.m // Masonry // // Created by Nick Tymchenko on 1/20/14. // #import "MASConstraint.h" #import "MASConstraint+Private.h" #define MASMethodNotImplemented() \ @throw [NSException exceptionWithName:NSInternalInconsistencyException \ reason:[NSString stringWithFormat:@"You must override %@ in a subclass.", NSStringFromSelector(_cmd)] \ userInfo:nil] @implementation MASConstraint #pragma mark - Init - (id)init { NSAssert(![self isMemberOfClass:[MASConstraint class]], @"MASConstraint is an abstract class, you should not instantiate it directly."); return [super init]; } #pragma mark - NSLayoutRelation proxies - (MASConstraint * (^)(id))equalTo { return ^id(id attribute) { return self.equalToWithRelation(attribute, NSLayoutRelationEqual); }; } - (MASConstraint * (^)(id))mas_equalTo { return ^id(id attribute) { return self.equalToWithRelation(attribute, NSLayoutRelationEqual); }; } - (MASConstraint * (^)(id))greaterThanOrEqualTo { return ^id(id attribute) { return self.equalToWithRelation(attribute, NSLayoutRelationGreaterThanOrEqual); }; } - (MASConstraint * (^)(id))mas_greaterThanOrEqualTo { return ^id(id attribute) { return self.equalToWithRelation(attribute, NSLayoutRelationGreaterThanOrEqual); }; } - (MASConstraint * (^)(id))lessThanOrEqualTo { return ^id(id attribute) { return self.equalToWithRelation(attribute, NSLayoutRelationLessThanOrEqual); }; } - (MASConstraint * (^)(id))mas_lessThanOrEqualTo { return ^id(id attribute) { return self.equalToWithRelation(attribute, NSLayoutRelationLessThanOrEqual); }; } #pragma mark - MASLayoutPriority proxies - (MASConstraint * (^)(void))priorityLow { return ^id{ self.priority(MASLayoutPriorityDefaultLow); return self; }; } - (MASConstraint * (^)(void))priorityMedium { return ^id{ self.priority(MASLayoutPriorityDefaultMedium); return self; }; } - (MASConstraint * (^)(void))priorityHigh { return ^id{ self.priority(MASLayoutPriorityDefaultHigh); return self; }; } #pragma mark - NSLayoutConstraint constant proxies - (MASConstraint * (^)(MASEdgeInsets))insets { return ^id(MASEdgeInsets insets){ self.insets = insets; return self; }; } - (MASConstraint * (^)(CGFloat))inset { return ^id(CGFloat inset){ self.inset = inset; return self; }; } - (MASConstraint * (^)(CGSize))sizeOffset { return ^id(CGSize offset) { self.sizeOffset = offset; return self; }; } - (MASConstraint * (^)(CGPoint))centerOffset { return ^id(CGPoint offset) { self.centerOffset = offset; return self; }; } - (MASConstraint * (^)(CGFloat))offset { return ^id(CGFloat offset){ self.offset = offset; return self; }; } - (MASConstraint * (^)(NSValue *value))valueOffset { return ^id(NSValue *offset) { NSAssert([offset isKindOfClass:NSValue.class], @"expected an NSValue offset, got: %@", offset); [self setLayoutConstantWithValue:offset]; return self; }; } - (MASConstraint * (^)(id offset))mas_offset { // Will never be called due to macro return nil; } #pragma mark - NSLayoutConstraint constant setter - (void)setLayoutConstantWithValue:(NSValue *)value { if ([value isKindOfClass:NSNumber.class]) { self.offset = [(NSNumber *)value doubleValue]; } else if (strcmp(value.objCType, @encode(CGPoint)) == 0) { CGPoint point; [value getValue:&point]; self.centerOffset = point; } else if (strcmp(value.objCType, @encode(CGSize)) == 0) { CGSize size; [value getValue:&size]; self.sizeOffset = size; } else if (strcmp(value.objCType, @encode(MASEdgeInsets)) == 0) { MASEdgeInsets insets; [value getValue:&insets]; self.insets = insets; } else { NSAssert(NO, @"attempting to set layout constant with unsupported value: %@", value); } } #pragma mark - Semantic properties - (MASConstraint *)with { return self; } - (MASConstraint *)and { return self; } #pragma mark - Chaining - (MASConstraint *)addConstraintWithLayoutAttribute:(NSLayoutAttribute __unused)layoutAttribute { MASMethodNotImplemented(); } - (MASConstraint *)left { return [self addConstraintWithLayoutAttribute:NSLayoutAttributeLeft]; } - (MASConstraint *)top { return [self addConstraintWithLayoutAttribute:NSLayoutAttributeTop]; } - (MASConstraint *)right { return [self addConstraintWithLayoutAttribute:NSLayoutAttributeRight]; } - (MASConstraint *)bottom { return [self addConstraintWithLayoutAttribute:NSLayoutAttributeBottom]; } - (MASConstraint *)leading { return [self addConstraintWithLayoutAttribute:NSLayoutAttributeLeading]; } - (MASConstraint *)trailing { return [self addConstraintWithLayoutAttribute:NSLayoutAttributeTrailing]; } - (MASConstraint *)width { return [self addConstraintWithLayoutAttribute:NSLayoutAttributeWidth]; } - (MASConstraint *)height { return [self addConstraintWithLayoutAttribute:NSLayoutAttributeHeight]; } - (MASConstraint *)centerX { return [self addConstraintWithLayoutAttribute:NSLayoutAttributeCenterX]; } - (MASConstraint *)centerY { return [self addConstraintWithLayoutAttribute:NSLayoutAttributeCenterY]; } - (MASConstraint *)baseline { return [self addConstraintWithLayoutAttribute:NSLayoutAttributeBaseline]; } #if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (__TV_OS_VERSION_MIN_REQUIRED >= 9000) || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 101100) - (MASConstraint *)firstBaseline { return [self addConstraintWithLayoutAttribute:NSLayoutAttributeFirstBaseline]; } - (MASConstraint *)lastBaseline { return [self addConstraintWithLayoutAttribute:NSLayoutAttributeLastBaseline]; } #endif #if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (__TV_OS_VERSION_MIN_REQUIRED >= 9000) - (MASConstraint *)leftMargin { return [self addConstraintWithLayoutAttribute:NSLayoutAttributeLeftMargin]; } - (MASConstraint *)rightMargin { return [self addConstraintWithLayoutAttribute:NSLayoutAttributeRightMargin]; } - (MASConstraint *)topMargin { return [self addConstraintWithLayoutAttribute:NSLayoutAttributeTopMargin]; } - (MASConstraint *)bottomMargin { return [self addConstraintWithLayoutAttribute:NSLayoutAttributeBottomMargin]; } - (MASConstraint *)leadingMargin { return [self addConstraintWithLayoutAttribute:NSLayoutAttributeLeadingMargin]; } - (MASConstraint *)trailingMargin { return [self addConstraintWithLayoutAttribute:NSLayoutAttributeTrailingMargin]; } - (MASConstraint *)centerXWithinMargins { return [self addConstraintWithLayoutAttribute:NSLayoutAttributeCenterXWithinMargins]; } - (MASConstraint *)centerYWithinMargins { return [self addConstraintWithLayoutAttribute:NSLayoutAttributeCenterYWithinMargins]; } #endif #pragma mark - Abstract - (MASConstraint * (^)(CGFloat multiplier))multipliedBy { MASMethodNotImplemented(); } - (MASConstraint * (^)(CGFloat divider))dividedBy { MASMethodNotImplemented(); } - (MASConstraint * (^)(MASLayoutPriority priority))priority { MASMethodNotImplemented(); } - (MASConstraint * (^)(id, NSLayoutRelation))equalToWithRelation { MASMethodNotImplemented(); } - (MASConstraint * (^)(id key))key { MASMethodNotImplemented(); } - (void)setInsets:(MASEdgeInsets __unused)insets { MASMethodNotImplemented(); } - (void)setInset:(CGFloat __unused)inset { MASMethodNotImplemented(); } - (void)setSizeOffset:(CGSize __unused)sizeOffset { MASMethodNotImplemented(); } - (void)setCenterOffset:(CGPoint __unused)centerOffset { MASMethodNotImplemented(); } - (void)setOffset:(CGFloat __unused)offset { MASMethodNotImplemented(); } #if TARGET_OS_MAC && !(TARGET_OS_IPHONE || TARGET_OS_TV) - (MASConstraint *)animator { MASMethodNotImplemented(); } #endif - (void)activate { MASMethodNotImplemented(); } - (void)deactivate { MASMethodNotImplemented(); } - (void)install { MASMethodNotImplemented(); } - (void)uninstall { MASMethodNotImplemented(); } @end ================================================ FILE: KJBannerViewDemo/Masonry/MASConstraintMaker.h ================================================ // // MASConstraintMaker.h // Masonry // // Created by Jonas Budelmann on 20/07/13. // Copyright (c) 2013 cloudling. All rights reserved. // #import "MASConstraint.h" #import "MASUtilities.h" typedef NS_OPTIONS(NSInteger, MASAttribute) { MASAttributeLeft = 1 << NSLayoutAttributeLeft, MASAttributeRight = 1 << NSLayoutAttributeRight, MASAttributeTop = 1 << NSLayoutAttributeTop, MASAttributeBottom = 1 << NSLayoutAttributeBottom, MASAttributeLeading = 1 << NSLayoutAttributeLeading, MASAttributeTrailing = 1 << NSLayoutAttributeTrailing, MASAttributeWidth = 1 << NSLayoutAttributeWidth, MASAttributeHeight = 1 << NSLayoutAttributeHeight, MASAttributeCenterX = 1 << NSLayoutAttributeCenterX, MASAttributeCenterY = 1 << NSLayoutAttributeCenterY, MASAttributeBaseline = 1 << NSLayoutAttributeBaseline, #if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (__TV_OS_VERSION_MIN_REQUIRED >= 9000) || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 101100) MASAttributeFirstBaseline = 1 << NSLayoutAttributeFirstBaseline, MASAttributeLastBaseline = 1 << NSLayoutAttributeLastBaseline, #endif #if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (__TV_OS_VERSION_MIN_REQUIRED >= 9000) MASAttributeLeftMargin = 1 << NSLayoutAttributeLeftMargin, MASAttributeRightMargin = 1 << NSLayoutAttributeRightMargin, MASAttributeTopMargin = 1 << NSLayoutAttributeTopMargin, MASAttributeBottomMargin = 1 << NSLayoutAttributeBottomMargin, MASAttributeLeadingMargin = 1 << NSLayoutAttributeLeadingMargin, MASAttributeTrailingMargin = 1 << NSLayoutAttributeTrailingMargin, MASAttributeCenterXWithinMargins = 1 << NSLayoutAttributeCenterXWithinMargins, MASAttributeCenterYWithinMargins = 1 << NSLayoutAttributeCenterYWithinMargins, #endif }; /** * Provides factory methods for creating MASConstraints. * Constraints are collected until they are ready to be installed * */ @interface MASConstraintMaker : NSObject /** * The following properties return a new MASViewConstraint * with the first item set to the makers associated view and the appropriate MASViewAttribute */ @property (nonatomic, strong, readonly) MASConstraint *left; @property (nonatomic, strong, readonly) MASConstraint *top; @property (nonatomic, strong, readonly) MASConstraint *right; @property (nonatomic, strong, readonly) MASConstraint *bottom; @property (nonatomic, strong, readonly) MASConstraint *leading; @property (nonatomic, strong, readonly) MASConstraint *trailing; @property (nonatomic, strong, readonly) MASConstraint *width; @property (nonatomic, strong, readonly) MASConstraint *height; @property (nonatomic, strong, readonly) MASConstraint *centerX; @property (nonatomic, strong, readonly) MASConstraint *centerY; @property (nonatomic, strong, readonly) MASConstraint *baseline; #if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (__TV_OS_VERSION_MIN_REQUIRED >= 9000) || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 101100) @property (nonatomic, strong, readonly) MASConstraint *firstBaseline; @property (nonatomic, strong, readonly) MASConstraint *lastBaseline; #endif #if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (__TV_OS_VERSION_MIN_REQUIRED >= 9000) @property (nonatomic, strong, readonly) MASConstraint *leftMargin; @property (nonatomic, strong, readonly) MASConstraint *rightMargin; @property (nonatomic, strong, readonly) MASConstraint *topMargin; @property (nonatomic, strong, readonly) MASConstraint *bottomMargin; @property (nonatomic, strong, readonly) MASConstraint *leadingMargin; @property (nonatomic, strong, readonly) MASConstraint *trailingMargin; @property (nonatomic, strong, readonly) MASConstraint *centerXWithinMargins; @property (nonatomic, strong, readonly) MASConstraint *centerYWithinMargins; #endif /** * Returns a block which creates a new MASCompositeConstraint with the first item set * to the makers associated view and children corresponding to the set bits in the * MASAttribute parameter. Combine multiple attributes via binary-or. */ @property (nonatomic, strong, readonly) MASConstraint *(^attributes)(MASAttribute attrs); /** * Creates a MASCompositeConstraint with type MASCompositeConstraintTypeEdges * which generates the appropriate MASViewConstraint children (top, left, bottom, right) * with the first item set to the makers associated view */ @property (nonatomic, strong, readonly) MASConstraint *edges; /** * Creates a MASCompositeConstraint with type MASCompositeConstraintTypeSize * which generates the appropriate MASViewConstraint children (width, height) * with the first item set to the makers associated view */ @property (nonatomic, strong, readonly) MASConstraint *size; /** * Creates a MASCompositeConstraint with type MASCompositeConstraintTypeCenter * which generates the appropriate MASViewConstraint children (centerX, centerY) * with the first item set to the makers associated view */ @property (nonatomic, strong, readonly) MASConstraint *center; /** * Whether or not to check for an existing constraint instead of adding constraint */ @property (nonatomic, assign) BOOL updateExisting; /** * Whether or not to remove existing constraints prior to installing */ @property (nonatomic, assign) BOOL removeExisting; /** * initialises the maker with a default view * * @param view any MASConstraint are created with this view as the first item * * @return a new MASConstraintMaker */ - (id)initWithView:(MAS_VIEW *)view; /** * Calls install method on any MASConstraints which have been created by this maker * * @return an array of all the installed MASConstraints */ - (NSArray *)install; - (MASConstraint * (^)(dispatch_block_t))group; @end ================================================ FILE: KJBannerViewDemo/Masonry/MASConstraintMaker.m ================================================ // // MASConstraintMaker.m // Masonry // // Created by Jonas Budelmann on 20/07/13. // Copyright (c) 2013 cloudling. All rights reserved. // #import "MASConstraintMaker.h" #import "MASViewConstraint.h" #import "MASCompositeConstraint.h" #import "MASConstraint+Private.h" #import "MASViewAttribute.h" #import "View+MASAdditions.h" @interface MASConstraintMaker () @property (nonatomic, weak) MAS_VIEW *view; @property (nonatomic, strong) NSMutableArray *constraints; @end @implementation MASConstraintMaker - (id)initWithView:(MAS_VIEW *)view { self = [super init]; if (!self) return nil; self.view = view; self.constraints = NSMutableArray.new; return self; } - (NSArray *)install { if (self.removeExisting) { NSArray *installedConstraints = [MASViewConstraint installedConstraintsForView:self.view]; for (MASConstraint *constraint in installedConstraints) { [constraint uninstall]; } } NSArray *constraints = self.constraints.copy; for (MASConstraint *constraint in constraints) { constraint.updateExisting = self.updateExisting; [constraint install]; } [self.constraints removeAllObjects]; return constraints; } #pragma mark - MASConstraintDelegate - (void)constraint:(MASConstraint *)constraint shouldBeReplacedWithConstraint:(MASConstraint *)replacementConstraint { NSUInteger index = [self.constraints indexOfObject:constraint]; NSAssert(index != NSNotFound, @"Could not find constraint %@", constraint); [self.constraints replaceObjectAtIndex:index withObject:replacementConstraint]; } - (MASConstraint *)constraint:(MASConstraint *)constraint addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute { MASViewAttribute *viewAttribute = [[MASViewAttribute alloc] initWithView:self.view layoutAttribute:layoutAttribute]; MASViewConstraint *newConstraint = [[MASViewConstraint alloc] initWithFirstViewAttribute:viewAttribute]; if ([constraint isKindOfClass:MASViewConstraint.class]) { //replace with composite constraint NSArray *children = @[constraint, newConstraint]; MASCompositeConstraint *compositeConstraint = [[MASCompositeConstraint alloc] initWithChildren:children]; compositeConstraint.delegate = self; [self constraint:constraint shouldBeReplacedWithConstraint:compositeConstraint]; return compositeConstraint; } if (!constraint) { newConstraint.delegate = self; [self.constraints addObject:newConstraint]; } return newConstraint; } - (MASConstraint *)addConstraintWithAttributes:(MASAttribute)attrs { __unused MASAttribute anyAttribute = (MASAttributeLeft | MASAttributeRight | MASAttributeTop | MASAttributeBottom | MASAttributeLeading | MASAttributeTrailing | MASAttributeWidth | MASAttributeHeight | MASAttributeCenterX | MASAttributeCenterY | MASAttributeBaseline #if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (__TV_OS_VERSION_MIN_REQUIRED >= 9000) || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 101100) | MASAttributeFirstBaseline | MASAttributeLastBaseline #endif #if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (__TV_OS_VERSION_MIN_REQUIRED >= 9000) | MASAttributeLeftMargin | MASAttributeRightMargin | MASAttributeTopMargin | MASAttributeBottomMargin | MASAttributeLeadingMargin | MASAttributeTrailingMargin | MASAttributeCenterXWithinMargins | MASAttributeCenterYWithinMargins #endif ); NSAssert((attrs & anyAttribute) != 0, @"You didn't pass any attribute to make.attributes(...)"); NSMutableArray *attributes = [NSMutableArray array]; if (attrs & MASAttributeLeft) [attributes addObject:self.view.mas_left]; if (attrs & MASAttributeRight) [attributes addObject:self.view.mas_right]; if (attrs & MASAttributeTop) [attributes addObject:self.view.mas_top]; if (attrs & MASAttributeBottom) [attributes addObject:self.view.mas_bottom]; if (attrs & MASAttributeLeading) [attributes addObject:self.view.mas_leading]; if (attrs & MASAttributeTrailing) [attributes addObject:self.view.mas_trailing]; if (attrs & MASAttributeWidth) [attributes addObject:self.view.mas_width]; if (attrs & MASAttributeHeight) [attributes addObject:self.view.mas_height]; if (attrs & MASAttributeCenterX) [attributes addObject:self.view.mas_centerX]; if (attrs & MASAttributeCenterY) [attributes addObject:self.view.mas_centerY]; if (attrs & MASAttributeBaseline) [attributes addObject:self.view.mas_baseline]; #if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (__TV_OS_VERSION_MIN_REQUIRED >= 9000) || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 101100) if (attrs & MASAttributeFirstBaseline) [attributes addObject:self.view.mas_firstBaseline]; if (attrs & MASAttributeLastBaseline) [attributes addObject:self.view.mas_lastBaseline]; #endif #if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (__TV_OS_VERSION_MIN_REQUIRED >= 9000) if (attrs & MASAttributeLeftMargin) [attributes addObject:self.view.mas_leftMargin]; if (attrs & MASAttributeRightMargin) [attributes addObject:self.view.mas_rightMargin]; if (attrs & MASAttributeTopMargin) [attributes addObject:self.view.mas_topMargin]; if (attrs & MASAttributeBottomMargin) [attributes addObject:self.view.mas_bottomMargin]; if (attrs & MASAttributeLeadingMargin) [attributes addObject:self.view.mas_leadingMargin]; if (attrs & MASAttributeTrailingMargin) [attributes addObject:self.view.mas_trailingMargin]; if (attrs & MASAttributeCenterXWithinMargins) [attributes addObject:self.view.mas_centerXWithinMargins]; if (attrs & MASAttributeCenterYWithinMargins) [attributes addObject:self.view.mas_centerYWithinMargins]; #endif NSMutableArray *children = [NSMutableArray arrayWithCapacity:attributes.count]; for (MASViewAttribute *a in attributes) { [children addObject:[[MASViewConstraint alloc] initWithFirstViewAttribute:a]]; } MASCompositeConstraint *constraint = [[MASCompositeConstraint alloc] initWithChildren:children]; constraint.delegate = self; [self.constraints addObject:constraint]; return constraint; } #pragma mark - standard Attributes - (MASConstraint *)addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute { return [self constraint:nil addConstraintWithLayoutAttribute:layoutAttribute]; } - (MASConstraint *)left { return [self addConstraintWithLayoutAttribute:NSLayoutAttributeLeft]; } - (MASConstraint *)top { return [self addConstraintWithLayoutAttribute:NSLayoutAttributeTop]; } - (MASConstraint *)right { return [self addConstraintWithLayoutAttribute:NSLayoutAttributeRight]; } - (MASConstraint *)bottom { return [self addConstraintWithLayoutAttribute:NSLayoutAttributeBottom]; } - (MASConstraint *)leading { return [self addConstraintWithLayoutAttribute:NSLayoutAttributeLeading]; } - (MASConstraint *)trailing { return [self addConstraintWithLayoutAttribute:NSLayoutAttributeTrailing]; } - (MASConstraint *)width { return [self addConstraintWithLayoutAttribute:NSLayoutAttributeWidth]; } - (MASConstraint *)height { return [self addConstraintWithLayoutAttribute:NSLayoutAttributeHeight]; } - (MASConstraint *)centerX { return [self addConstraintWithLayoutAttribute:NSLayoutAttributeCenterX]; } - (MASConstraint *)centerY { return [self addConstraintWithLayoutAttribute:NSLayoutAttributeCenterY]; } - (MASConstraint *)baseline { return [self addConstraintWithLayoutAttribute:NSLayoutAttributeBaseline]; } - (MASConstraint *(^)(MASAttribute))attributes { return ^(MASAttribute attrs){ return [self addConstraintWithAttributes:attrs]; }; } #if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (__TV_OS_VERSION_MIN_REQUIRED >= 9000) || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 101100) - (MASConstraint *)firstBaseline { return [self addConstraintWithLayoutAttribute:NSLayoutAttributeFirstBaseline]; } - (MASConstraint *)lastBaseline { return [self addConstraintWithLayoutAttribute:NSLayoutAttributeLastBaseline]; } #endif #if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (__TV_OS_VERSION_MIN_REQUIRED >= 9000) - (MASConstraint *)leftMargin { return [self addConstraintWithLayoutAttribute:NSLayoutAttributeLeftMargin]; } - (MASConstraint *)rightMargin { return [self addConstraintWithLayoutAttribute:NSLayoutAttributeRightMargin]; } - (MASConstraint *)topMargin { return [self addConstraintWithLayoutAttribute:NSLayoutAttributeTopMargin]; } - (MASConstraint *)bottomMargin { return [self addConstraintWithLayoutAttribute:NSLayoutAttributeBottomMargin]; } - (MASConstraint *)leadingMargin { return [self addConstraintWithLayoutAttribute:NSLayoutAttributeLeadingMargin]; } - (MASConstraint *)trailingMargin { return [self addConstraintWithLayoutAttribute:NSLayoutAttributeTrailingMargin]; } - (MASConstraint *)centerXWithinMargins { return [self addConstraintWithLayoutAttribute:NSLayoutAttributeCenterXWithinMargins]; } - (MASConstraint *)centerYWithinMargins { return [self addConstraintWithLayoutAttribute:NSLayoutAttributeCenterYWithinMargins]; } #endif #pragma mark - composite Attributes - (MASConstraint *)edges { return [self addConstraintWithAttributes:MASAttributeTop | MASAttributeLeft | MASAttributeRight | MASAttributeBottom]; } - (MASConstraint *)size { return [self addConstraintWithAttributes:MASAttributeWidth | MASAttributeHeight]; } - (MASConstraint *)center { return [self addConstraintWithAttributes:MASAttributeCenterX | MASAttributeCenterY]; } #pragma mark - grouping - (MASConstraint *(^)(dispatch_block_t group))group { return ^id(dispatch_block_t group) { NSInteger previousCount = self.constraints.count; group(); NSArray *children = [self.constraints subarrayWithRange:NSMakeRange(previousCount, self.constraints.count - previousCount)]; MASCompositeConstraint *constraint = [[MASCompositeConstraint alloc] initWithChildren:children]; constraint.delegate = self; return constraint; }; } @end ================================================ FILE: KJBannerViewDemo/Masonry/MASLayoutConstraint.h ================================================ // // MASLayoutConstraint.h // Masonry // // Created by Jonas Budelmann on 3/08/13. // Copyright (c) 2013 Jonas Budelmann. All rights reserved. // #import "MASUtilities.h" /** * When you are debugging or printing the constraints attached to a view this subclass * makes it easier to identify which constraints have been created via Masonry */ @interface MASLayoutConstraint : NSLayoutConstraint /** * a key to associate with this constraint */ @property (nonatomic, strong) id mas_key; @end ================================================ FILE: KJBannerViewDemo/Masonry/MASLayoutConstraint.m ================================================ // // MASLayoutConstraint.m // Masonry // // Created by Jonas Budelmann on 3/08/13. // Copyright (c) 2013 Jonas Budelmann. All rights reserved. // #import "MASLayoutConstraint.h" @implementation MASLayoutConstraint @end ================================================ FILE: KJBannerViewDemo/Masonry/MASUtilities.h ================================================ // // MASUtilities.h // Masonry // // Created by Jonas Budelmann on 19/08/13. // Copyright (c) 2013 Jonas Budelmann. All rights reserved. // #import #if TARGET_OS_IPHONE || TARGET_OS_TV #import #define MAS_VIEW UIView #define MAS_VIEW_CONTROLLER UIViewController #define MASEdgeInsets UIEdgeInsets typedef UILayoutPriority MASLayoutPriority; static const MASLayoutPriority MASLayoutPriorityRequired = UILayoutPriorityRequired; static const MASLayoutPriority MASLayoutPriorityDefaultHigh = UILayoutPriorityDefaultHigh; static const MASLayoutPriority MASLayoutPriorityDefaultMedium = 500; static const MASLayoutPriority MASLayoutPriorityDefaultLow = UILayoutPriorityDefaultLow; static const MASLayoutPriority MASLayoutPriorityFittingSizeLevel = UILayoutPriorityFittingSizeLevel; #elif TARGET_OS_MAC #import #define MAS_VIEW NSView #define MASEdgeInsets NSEdgeInsets typedef NSLayoutPriority MASLayoutPriority; static const MASLayoutPriority MASLayoutPriorityRequired = NSLayoutPriorityRequired; static const MASLayoutPriority MASLayoutPriorityDefaultHigh = NSLayoutPriorityDefaultHigh; static const MASLayoutPriority MASLayoutPriorityDragThatCanResizeWindow = NSLayoutPriorityDragThatCanResizeWindow; static const MASLayoutPriority MASLayoutPriorityDefaultMedium = 501; static const MASLayoutPriority MASLayoutPriorityWindowSizeStayPut = NSLayoutPriorityWindowSizeStayPut; static const MASLayoutPriority MASLayoutPriorityDragThatCannotResizeWindow = NSLayoutPriorityDragThatCannotResizeWindow; static const MASLayoutPriority MASLayoutPriorityDefaultLow = NSLayoutPriorityDefaultLow; static const MASLayoutPriority MASLayoutPriorityFittingSizeCompression = NSLayoutPriorityFittingSizeCompression; #endif /** * Allows you to attach keys to objects matching the variable names passed. * * view1.mas_key = @"view1", view2.mas_key = @"view2"; * * is equivalent to: * * MASAttachKeys(view1, view2); */ #define MASAttachKeys(...) \ { \ NSDictionary *keyPairs = NSDictionaryOfVariableBindings(__VA_ARGS__); \ for (id key in keyPairs.allKeys) { \ id obj = keyPairs[key]; \ NSAssert([obj respondsToSelector:@selector(setMas_key:)], \ @"Cannot attach mas_key to %@", obj); \ [obj setMas_key:key]; \ } \ } /** * Used to create object hashes * Based on http://www.mikeash.com/pyblog/friday-qa-2010-06-18-implementing-equality-and-hashing.html */ #define MAS_NSUINT_BIT (CHAR_BIT * sizeof(NSUInteger)) #define MAS_NSUINTROTATE(val, howmuch) ((((NSUInteger)val) << howmuch) | (((NSUInteger)val) >> (MAS_NSUINT_BIT - howmuch))) /** * Given a scalar or struct value, wraps it in NSValue * Based on EXPObjectify: https://github.com/specta/expecta */ static inline id _MASBoxValue(const char *type, ...) { va_list v; va_start(v, type); id obj = nil; if (strcmp(type, @encode(id)) == 0) { id actual = va_arg(v, id); obj = actual; } else if (strcmp(type, @encode(CGPoint)) == 0) { CGPoint actual = (CGPoint)va_arg(v, CGPoint); obj = [NSValue value:&actual withObjCType:type]; } else if (strcmp(type, @encode(CGSize)) == 0) { CGSize actual = (CGSize)va_arg(v, CGSize); obj = [NSValue value:&actual withObjCType:type]; } else if (strcmp(type, @encode(MASEdgeInsets)) == 0) { MASEdgeInsets actual = (MASEdgeInsets)va_arg(v, MASEdgeInsets); obj = [NSValue value:&actual withObjCType:type]; } else if (strcmp(type, @encode(double)) == 0) { double actual = (double)va_arg(v, double); obj = [NSNumber numberWithDouble:actual]; } else if (strcmp(type, @encode(float)) == 0) { float actual = (float)va_arg(v, double); obj = [NSNumber numberWithFloat:actual]; } else if (strcmp(type, @encode(int)) == 0) { int actual = (int)va_arg(v, int); obj = [NSNumber numberWithInt:actual]; } else if (strcmp(type, @encode(long)) == 0) { long actual = (long)va_arg(v, long); obj = [NSNumber numberWithLong:actual]; } else if (strcmp(type, @encode(long long)) == 0) { long long actual = (long long)va_arg(v, long long); obj = [NSNumber numberWithLongLong:actual]; } else if (strcmp(type, @encode(short)) == 0) { short actual = (short)va_arg(v, int); obj = [NSNumber numberWithShort:actual]; } else if (strcmp(type, @encode(char)) == 0) { char actual = (char)va_arg(v, int); obj = [NSNumber numberWithChar:actual]; } else if (strcmp(type, @encode(bool)) == 0) { bool actual = (bool)va_arg(v, int); obj = [NSNumber numberWithBool:actual]; } else if (strcmp(type, @encode(unsigned char)) == 0) { unsigned char actual = (unsigned char)va_arg(v, unsigned int); obj = [NSNumber numberWithUnsignedChar:actual]; } else if (strcmp(type, @encode(unsigned int)) == 0) { unsigned int actual = (unsigned int)va_arg(v, unsigned int); obj = [NSNumber numberWithUnsignedInt:actual]; } else if (strcmp(type, @encode(unsigned long)) == 0) { unsigned long actual = (unsigned long)va_arg(v, unsigned long); obj = [NSNumber numberWithUnsignedLong:actual]; } else if (strcmp(type, @encode(unsigned long long)) == 0) { unsigned long long actual = (unsigned long long)va_arg(v, unsigned long long); obj = [NSNumber numberWithUnsignedLongLong:actual]; } else if (strcmp(type, @encode(unsigned short)) == 0) { unsigned short actual = (unsigned short)va_arg(v, unsigned int); obj = [NSNumber numberWithUnsignedShort:actual]; } va_end(v); return obj; } #define MASBoxValue(value) _MASBoxValue(@encode(__typeof__((value))), (value)) ================================================ FILE: KJBannerViewDemo/Masonry/MASViewAttribute.h ================================================ // // MASViewAttribute.h // Masonry // // Created by Jonas Budelmann on 21/07/13. // Copyright (c) 2013 cloudling. All rights reserved. // #import "MASUtilities.h" /** * An immutable tuple which stores the view and the related NSLayoutAttribute. * Describes part of either the left or right hand side of a constraint equation */ @interface MASViewAttribute : NSObject /** * The view which the reciever relates to. Can be nil if item is not a view. */ @property (nonatomic, weak, readonly) MAS_VIEW *view; /** * The item which the reciever relates to. */ @property (nonatomic, weak, readonly) id item; /** * The attribute which the reciever relates to */ @property (nonatomic, assign, readonly) NSLayoutAttribute layoutAttribute; /** * Convenience initializer. */ - (id)initWithView:(MAS_VIEW *)view layoutAttribute:(NSLayoutAttribute)layoutAttribute; /** * The designated initializer. */ - (id)initWithView:(MAS_VIEW *)view item:(id)item layoutAttribute:(NSLayoutAttribute)layoutAttribute; /** * Determine whether the layoutAttribute is a size attribute * * @return YES if layoutAttribute is equal to NSLayoutAttributeWidth or NSLayoutAttributeHeight */ - (BOOL)isSizeAttribute; @end ================================================ FILE: KJBannerViewDemo/Masonry/MASViewAttribute.m ================================================ // // MASViewAttribute.m // Masonry // // Created by Jonas Budelmann on 21/07/13. // Copyright (c) 2013 cloudling. All rights reserved. // #import "MASViewAttribute.h" @implementation MASViewAttribute - (id)initWithView:(MAS_VIEW *)view layoutAttribute:(NSLayoutAttribute)layoutAttribute { self = [self initWithView:view item:view layoutAttribute:layoutAttribute]; return self; } - (id)initWithView:(MAS_VIEW *)view item:(id)item layoutAttribute:(NSLayoutAttribute)layoutAttribute { self = [super init]; if (!self) return nil; _view = view; _item = item; _layoutAttribute = layoutAttribute; return self; } - (BOOL)isSizeAttribute { return self.layoutAttribute == NSLayoutAttributeWidth || self.layoutAttribute == NSLayoutAttributeHeight; } - (BOOL)isEqual:(MASViewAttribute *)viewAttribute { if ([viewAttribute isKindOfClass:self.class]) { return self.view == viewAttribute.view && self.layoutAttribute == viewAttribute.layoutAttribute; } return [super isEqual:viewAttribute]; } - (NSUInteger)hash { return MAS_NSUINTROTATE([self.view hash], MAS_NSUINT_BIT / 2) ^ self.layoutAttribute; } @end ================================================ FILE: KJBannerViewDemo/Masonry/MASViewConstraint.h ================================================ // // MASViewConstraint.h // Masonry // // Created by Jonas Budelmann on 20/07/13. // Copyright (c) 2013 cloudling. All rights reserved. // #import "MASViewAttribute.h" #import "MASConstraint.h" #import "MASLayoutConstraint.h" #import "MASUtilities.h" /** * A single constraint. * Contains the attributes neccessary for creating a NSLayoutConstraint and adding it to the appropriate view */ @interface MASViewConstraint : MASConstraint /** * First item/view and first attribute of the NSLayoutConstraint */ @property (nonatomic, strong, readonly) MASViewAttribute *firstViewAttribute; /** * Second item/view and second attribute of the NSLayoutConstraint */ @property (nonatomic, strong, readonly) MASViewAttribute *secondViewAttribute; /** * initialises the MASViewConstraint with the first part of the equation * * @param firstViewAttribute view.mas_left, view.mas_width etc. * * @return a new view constraint */ - (id)initWithFirstViewAttribute:(MASViewAttribute *)firstViewAttribute; /** * Returns all MASViewConstraints installed with this view as a first item. * * @param view A view to retrieve constraints for. * * @return An array of MASViewConstraints. */ + (NSArray *)installedConstraintsForView:(MAS_VIEW *)view; @end ================================================ FILE: KJBannerViewDemo/Masonry/MASViewConstraint.m ================================================ // // MASViewConstraint.m // Masonry // // Created by Jonas Budelmann on 20/07/13. // Copyright (c) 2013 cloudling. All rights reserved. // #import "MASViewConstraint.h" #import "MASConstraint+Private.h" #import "MASCompositeConstraint.h" #import "MASLayoutConstraint.h" #import "View+MASAdditions.h" #import @interface MAS_VIEW (MASConstraints) @property (nonatomic, readonly) NSMutableSet *mas_installedConstraints; @end @implementation MAS_VIEW (MASConstraints) static char kInstalledConstraintsKey; - (NSMutableSet *)mas_installedConstraints { NSMutableSet *constraints = objc_getAssociatedObject(self, &kInstalledConstraintsKey); if (!constraints) { constraints = [NSMutableSet set]; objc_setAssociatedObject(self, &kInstalledConstraintsKey, constraints, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } return constraints; } @end @interface MASViewConstraint () @property (nonatomic, strong, readwrite) MASViewAttribute *secondViewAttribute; @property (nonatomic, weak) MAS_VIEW *installedView; @property (nonatomic, weak) MASLayoutConstraint *layoutConstraint; @property (nonatomic, assign) NSLayoutRelation layoutRelation; @property (nonatomic, assign) MASLayoutPriority layoutPriority; @property (nonatomic, assign) CGFloat layoutMultiplier; @property (nonatomic, assign) CGFloat layoutConstant; @property (nonatomic, assign) BOOL hasLayoutRelation; @property (nonatomic, strong) id mas_key; @property (nonatomic, assign) BOOL useAnimator; @end @implementation MASViewConstraint - (id)initWithFirstViewAttribute:(MASViewAttribute *)firstViewAttribute { self = [super init]; if (!self) return nil; _firstViewAttribute = firstViewAttribute; self.layoutPriority = MASLayoutPriorityRequired; self.layoutMultiplier = 1; return self; } #pragma mark - NSCoping - (id)copyWithZone:(NSZone __unused *)zone { MASViewConstraint *constraint = [[MASViewConstraint alloc] initWithFirstViewAttribute:self.firstViewAttribute]; constraint.layoutConstant = self.layoutConstant; constraint.layoutRelation = self.layoutRelation; constraint.layoutPriority = self.layoutPriority; constraint.layoutMultiplier = self.layoutMultiplier; constraint.delegate = self.delegate; return constraint; } #pragma mark - Public + (NSArray *)installedConstraintsForView:(MAS_VIEW *)view { return [view.mas_installedConstraints allObjects]; } #pragma mark - Private - (void)setLayoutConstant:(CGFloat)layoutConstant { _layoutConstant = layoutConstant; #if TARGET_OS_MAC && !(TARGET_OS_IPHONE || TARGET_OS_TV) if (self.useAnimator) { [self.layoutConstraint.animator setConstant:layoutConstant]; } else { self.layoutConstraint.constant = layoutConstant; } #else self.layoutConstraint.constant = layoutConstant; #endif } - (void)setLayoutRelation:(NSLayoutRelation)layoutRelation { _layoutRelation = layoutRelation; self.hasLayoutRelation = YES; } - (BOOL)supportsActiveProperty { return [self.layoutConstraint respondsToSelector:@selector(isActive)]; } - (BOOL)isActive { BOOL active = YES; if ([self supportsActiveProperty]) { active = [self.layoutConstraint isActive]; } return active; } - (BOOL)hasBeenInstalled { return (self.layoutConstraint != nil) && [self isActive]; } - (void)setSecondViewAttribute:(id)secondViewAttribute { if ([secondViewAttribute isKindOfClass:NSValue.class]) { [self setLayoutConstantWithValue:secondViewAttribute]; } else if ([secondViewAttribute isKindOfClass:MAS_VIEW.class]) { _secondViewAttribute = [[MASViewAttribute alloc] initWithView:secondViewAttribute layoutAttribute:self.firstViewAttribute.layoutAttribute]; } else if ([secondViewAttribute isKindOfClass:MASViewAttribute.class]) { _secondViewAttribute = secondViewAttribute; } else { NSAssert(NO, @"attempting to add unsupported attribute: %@", secondViewAttribute); } } #pragma mark - NSLayoutConstraint multiplier proxies - (MASConstraint * (^)(CGFloat))multipliedBy { return ^id(CGFloat multiplier) { NSAssert(!self.hasBeenInstalled, @"Cannot modify constraint multiplier after it has been installed"); self.layoutMultiplier = multiplier; return self; }; } - (MASConstraint * (^)(CGFloat))dividedBy { return ^id(CGFloat divider) { NSAssert(!self.hasBeenInstalled, @"Cannot modify constraint multiplier after it has been installed"); self.layoutMultiplier = 1.0/divider; return self; }; } #pragma mark - MASLayoutPriority proxy - (MASConstraint * (^)(MASLayoutPriority))priority { return ^id(MASLayoutPriority priority) { NSAssert(!self.hasBeenInstalled, @"Cannot modify constraint priority after it has been installed"); self.layoutPriority = priority; return self; }; } #pragma mark - NSLayoutRelation proxy - (MASConstraint * (^)(id, NSLayoutRelation))equalToWithRelation { return ^id(id attribute, NSLayoutRelation relation) { if ([attribute isKindOfClass:NSArray.class]) { NSAssert(!self.hasLayoutRelation, @"Redefinition of constraint relation"); NSMutableArray *children = NSMutableArray.new; for (id attr in attribute) { MASViewConstraint *viewConstraint = [self copy]; viewConstraint.layoutRelation = relation; viewConstraint.secondViewAttribute = attr; [children addObject:viewConstraint]; } MASCompositeConstraint *compositeConstraint = [[MASCompositeConstraint alloc] initWithChildren:children]; compositeConstraint.delegate = self.delegate; [self.delegate constraint:self shouldBeReplacedWithConstraint:compositeConstraint]; return compositeConstraint; } else { NSAssert(!self.hasLayoutRelation || self.layoutRelation == relation && [attribute isKindOfClass:NSValue.class], @"Redefinition of constraint relation"); self.layoutRelation = relation; self.secondViewAttribute = attribute; return self; } }; } #pragma mark - Semantic properties - (MASConstraint *)with { return self; } - (MASConstraint *)and { return self; } #pragma mark - attribute chaining - (MASConstraint *)addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute { NSAssert(!self.hasLayoutRelation, @"Attributes should be chained before defining the constraint relation"); return [self.delegate constraint:self addConstraintWithLayoutAttribute:layoutAttribute]; } #pragma mark - Animator proxy #if TARGET_OS_MAC && !(TARGET_OS_IPHONE || TARGET_OS_TV) - (MASConstraint *)animator { self.useAnimator = YES; return self; } #endif #pragma mark - debug helpers - (MASConstraint * (^)(id))key { return ^id(id key) { self.mas_key = key; return self; }; } #pragma mark - NSLayoutConstraint constant setters - (void)setInsets:(MASEdgeInsets)insets { NSLayoutAttribute layoutAttribute = self.firstViewAttribute.layoutAttribute; switch (layoutAttribute) { case NSLayoutAttributeLeft: case NSLayoutAttributeLeading: self.layoutConstant = insets.left; break; case NSLayoutAttributeTop: self.layoutConstant = insets.top; break; case NSLayoutAttributeBottom: self.layoutConstant = -insets.bottom; break; case NSLayoutAttributeRight: case NSLayoutAttributeTrailing: self.layoutConstant = -insets.right; break; default: break; } } - (void)setInset:(CGFloat)inset { [self setInsets:(MASEdgeInsets){.top = inset, .left = inset, .bottom = inset, .right = inset}]; } - (void)setOffset:(CGFloat)offset { self.layoutConstant = offset; } - (void)setSizeOffset:(CGSize)sizeOffset { NSLayoutAttribute layoutAttribute = self.firstViewAttribute.layoutAttribute; switch (layoutAttribute) { case NSLayoutAttributeWidth: self.layoutConstant = sizeOffset.width; break; case NSLayoutAttributeHeight: self.layoutConstant = sizeOffset.height; break; default: break; } } - (void)setCenterOffset:(CGPoint)centerOffset { NSLayoutAttribute layoutAttribute = self.firstViewAttribute.layoutAttribute; switch (layoutAttribute) { case NSLayoutAttributeCenterX: self.layoutConstant = centerOffset.x; break; case NSLayoutAttributeCenterY: self.layoutConstant = centerOffset.y; break; default: break; } } #pragma mark - MASConstraint - (void)activate { [self install]; } - (void)deactivate { [self uninstall]; } - (void)install { if (self.hasBeenInstalled) { return; } if ([self supportsActiveProperty] && self.layoutConstraint) { self.layoutConstraint.active = YES; [self.firstViewAttribute.view.mas_installedConstraints addObject:self]; return; } MAS_VIEW *firstLayoutItem = self.firstViewAttribute.item; NSLayoutAttribute firstLayoutAttribute = self.firstViewAttribute.layoutAttribute; MAS_VIEW *secondLayoutItem = self.secondViewAttribute.item; NSLayoutAttribute secondLayoutAttribute = self.secondViewAttribute.layoutAttribute; // alignment attributes must have a secondViewAttribute // therefore we assume that is refering to superview // eg make.left.equalTo(@10) if (!self.firstViewAttribute.isSizeAttribute && !self.secondViewAttribute) { secondLayoutItem = self.firstViewAttribute.view.superview; secondLayoutAttribute = firstLayoutAttribute; } MASLayoutConstraint *layoutConstraint = [MASLayoutConstraint constraintWithItem:firstLayoutItem attribute:firstLayoutAttribute relatedBy:self.layoutRelation toItem:secondLayoutItem attribute:secondLayoutAttribute multiplier:self.layoutMultiplier constant:self.layoutConstant]; layoutConstraint.priority = self.layoutPriority; layoutConstraint.mas_key = self.mas_key; if (self.secondViewAttribute.view) { MAS_VIEW *closestCommonSuperview = [self.firstViewAttribute.view mas_closestCommonSuperview:self.secondViewAttribute.view]; NSAssert(closestCommonSuperview, @"couldn't find a common superview for %@ and %@", self.firstViewAttribute.view, self.secondViewAttribute.view); self.installedView = closestCommonSuperview; } else if (self.firstViewAttribute.isSizeAttribute) { self.installedView = self.firstViewAttribute.view; } else { self.installedView = self.firstViewAttribute.view.superview; } MASLayoutConstraint *existingConstraint = nil; if (self.updateExisting) { existingConstraint = [self layoutConstraintSimilarTo:layoutConstraint]; } if (existingConstraint) { // just update the constant existingConstraint.constant = layoutConstraint.constant; self.layoutConstraint = existingConstraint; } else { [self.installedView addConstraint:layoutConstraint]; self.layoutConstraint = layoutConstraint; [firstLayoutItem.mas_installedConstraints addObject:self]; } } - (MASLayoutConstraint *)layoutConstraintSimilarTo:(MASLayoutConstraint *)layoutConstraint { // check if any constraints are the same apart from the only mutable property constant // go through constraints in reverse as we do not want to match auto-resizing or interface builder constraints // and they are likely to be added first. for (NSLayoutConstraint *existingConstraint in self.installedView.constraints.reverseObjectEnumerator) { if (![existingConstraint isKindOfClass:MASLayoutConstraint.class]) continue; if (existingConstraint.firstItem != layoutConstraint.firstItem) continue; if (existingConstraint.secondItem != layoutConstraint.secondItem) continue; if (existingConstraint.firstAttribute != layoutConstraint.firstAttribute) continue; if (existingConstraint.secondAttribute != layoutConstraint.secondAttribute) continue; if (existingConstraint.relation != layoutConstraint.relation) continue; if (existingConstraint.multiplier != layoutConstraint.multiplier) continue; if (existingConstraint.priority != layoutConstraint.priority) continue; return (id)existingConstraint; } return nil; } - (void)uninstall { if ([self supportsActiveProperty]) { self.layoutConstraint.active = NO; [self.firstViewAttribute.view.mas_installedConstraints removeObject:self]; return; } [self.installedView removeConstraint:self.layoutConstraint]; self.layoutConstraint = nil; self.installedView = nil; [self.firstViewAttribute.view.mas_installedConstraints removeObject:self]; } @end ================================================ FILE: KJBannerViewDemo/Masonry/Masonry.h ================================================ // // Masonry.h // Masonry // // Created by Jonas Budelmann on 20/07/13. // Copyright (c) 2013 cloudling. All rights reserved. // #import //! Project version number for Masonry. FOUNDATION_EXPORT double MasonryVersionNumber; //! Project version string for Masonry. FOUNDATION_EXPORT const unsigned char MasonryVersionString[]; #import "MASUtilities.h" #import "View+MASAdditions.h" #import "View+MASShorthandAdditions.h" #import "ViewController+MASAdditions.h" #import "NSArray+MASAdditions.h" #import "NSArray+MASShorthandAdditions.h" #import "MASConstraint.h" #import "MASCompositeConstraint.h" #import "MASViewAttribute.h" #import "MASViewConstraint.h" #import "MASConstraintMaker.h" #import "MASLayoutConstraint.h" #import "NSLayoutConstraint+MASDebugAdditions.h" ================================================ FILE: KJBannerViewDemo/Masonry/NSArray+MASAdditions.h ================================================ // // NSArray+MASAdditions.h // // // Created by Daniel Hammond on 11/26/13. // // #import "MASUtilities.h" #import "MASConstraintMaker.h" #import "MASViewAttribute.h" typedef NS_ENUM(NSUInteger, MASAxisType) { MASAxisTypeHorizontal, MASAxisTypeVertical }; @interface NSArray (MASAdditions) /** * Creates a MASConstraintMaker with each view in the callee. * Any constraints defined are added to the view or the appropriate superview once the block has finished executing on each view * * @param block scope within which you can build up the constraints which you wish to apply to each view. * * @return Array of created MASConstraints */ - (NSArray *)mas_makeConstraints:(void (NS_NOESCAPE ^)(MASConstraintMaker *make))block; /** * Creates a MASConstraintMaker with each view in the callee. * Any constraints defined are added to each view or the appropriate superview once the block has finished executing on each view. * If an existing constraint exists then it will be updated instead. * * @param block scope within which you can build up the constraints which you wish to apply to each view. * * @return Array of created/updated MASConstraints */ - (NSArray *)mas_updateConstraints:(void (NS_NOESCAPE ^)(MASConstraintMaker *make))block; /** * Creates a MASConstraintMaker with each view in the callee. * Any constraints defined are added to each view or the appropriate superview once the block has finished executing on each view. * All constraints previously installed for the views will be removed. * * @param block scope within which you can build up the constraints which you wish to apply to each view. * * @return Array of created/updated MASConstraints */ - (NSArray *)mas_remakeConstraints:(void (NS_NOESCAPE ^)(MASConstraintMaker *make))block; /** * distribute with fixed spacing * * @param axisType which axis to distribute items along * @param fixedSpacing the spacing between each item * @param leadSpacing the spacing before the first item and the container * @param tailSpacing the spacing after the last item and the container */ - (void)mas_distributeViewsAlongAxis:(MASAxisType)axisType withFixedSpacing:(CGFloat)fixedSpacing leadSpacing:(CGFloat)leadSpacing tailSpacing:(CGFloat)tailSpacing; /** * distribute with fixed item size * * @param axisType which axis to distribute items along * @param fixedItemLength the fixed length of each item * @param leadSpacing the spacing before the first item and the container * @param tailSpacing the spacing after the last item and the container */ - (void)mas_distributeViewsAlongAxis:(MASAxisType)axisType withFixedItemLength:(CGFloat)fixedItemLength leadSpacing:(CGFloat)leadSpacing tailSpacing:(CGFloat)tailSpacing; @end ================================================ FILE: KJBannerViewDemo/Masonry/NSArray+MASAdditions.m ================================================ // // NSArray+MASAdditions.m // // // Created by Daniel Hammond on 11/26/13. // // #import "NSArray+MASAdditions.h" #import "View+MASAdditions.h" @implementation NSArray (MASAdditions) - (NSArray *)mas_makeConstraints:(void(^)(MASConstraintMaker *make))block { NSMutableArray *constraints = [NSMutableArray array]; for (MAS_VIEW *view in self) { NSAssert([view isKindOfClass:[MAS_VIEW class]], @"All objects in the array must be views"); [constraints addObjectsFromArray:[view mas_makeConstraints:block]]; } return constraints; } - (NSArray *)mas_updateConstraints:(void(^)(MASConstraintMaker *make))block { NSMutableArray *constraints = [NSMutableArray array]; for (MAS_VIEW *view in self) { NSAssert([view isKindOfClass:[MAS_VIEW class]], @"All objects in the array must be views"); [constraints addObjectsFromArray:[view mas_updateConstraints:block]]; } return constraints; } - (NSArray *)mas_remakeConstraints:(void(^)(MASConstraintMaker *make))block { NSMutableArray *constraints = [NSMutableArray array]; for (MAS_VIEW *view in self) { NSAssert([view isKindOfClass:[MAS_VIEW class]], @"All objects in the array must be views"); [constraints addObjectsFromArray:[view mas_remakeConstraints:block]]; } return constraints; } - (void)mas_distributeViewsAlongAxis:(MASAxisType)axisType withFixedSpacing:(CGFloat)fixedSpacing leadSpacing:(CGFloat)leadSpacing tailSpacing:(CGFloat)tailSpacing { if (self.count < 2) { NSAssert(self.count>1,@"views to distribute need to bigger than one"); return; } MAS_VIEW *tempSuperView = [self mas_commonSuperviewOfViews]; if (axisType == MASAxisTypeHorizontal) { MAS_VIEW *prev; for (int i = 0; i < self.count; i++) { MAS_VIEW *v = self[i]; [v mas_makeConstraints:^(MASConstraintMaker *make) { if (prev) { make.width.equalTo(prev); make.left.equalTo(prev.mas_right).offset(fixedSpacing); if (i == self.count - 1) {//last one make.right.equalTo(tempSuperView).offset(-tailSpacing); } } else {//first one make.left.equalTo(tempSuperView).offset(leadSpacing); } }]; prev = v; } } else { MAS_VIEW *prev; for (int i = 0; i < self.count; i++) { MAS_VIEW *v = self[i]; [v mas_makeConstraints:^(MASConstraintMaker *make) { if (prev) { make.height.equalTo(prev); make.top.equalTo(prev.mas_bottom).offset(fixedSpacing); if (i == self.count - 1) {//last one make.bottom.equalTo(tempSuperView).offset(-tailSpacing); } } else {//first one make.top.equalTo(tempSuperView).offset(leadSpacing); } }]; prev = v; } } } - (void)mas_distributeViewsAlongAxis:(MASAxisType)axisType withFixedItemLength:(CGFloat)fixedItemLength leadSpacing:(CGFloat)leadSpacing tailSpacing:(CGFloat)tailSpacing { if (self.count < 2) { NSAssert(self.count>1,@"views to distribute need to bigger than one"); return; } MAS_VIEW *tempSuperView = [self mas_commonSuperviewOfViews]; if (axisType == MASAxisTypeHorizontal) { MAS_VIEW *prev; for (int i = 0; i < self.count; i++) { MAS_VIEW *v = self[i]; [v mas_makeConstraints:^(MASConstraintMaker *make) { make.width.equalTo(@(fixedItemLength)); if (prev) { if (i == self.count - 1) {//last one make.right.equalTo(tempSuperView).offset(-tailSpacing); } else { CGFloat offset = (1-(i/((CGFloat)self.count-1)))*(fixedItemLength+leadSpacing)-i*tailSpacing/(((CGFloat)self.count-1)); make.right.equalTo(tempSuperView).multipliedBy(i/((CGFloat)self.count-1)).with.offset(offset); } } else {//first one make.left.equalTo(tempSuperView).offset(leadSpacing); } }]; prev = v; } } else { MAS_VIEW *prev; for (int i = 0; i < self.count; i++) { MAS_VIEW *v = self[i]; [v mas_makeConstraints:^(MASConstraintMaker *make) { make.height.equalTo(@(fixedItemLength)); if (prev) { if (i == self.count - 1) {//last one make.bottom.equalTo(tempSuperView).offset(-tailSpacing); } else { CGFloat offset = (1-(i/((CGFloat)self.count-1)))*(fixedItemLength+leadSpacing)-i*tailSpacing/(((CGFloat)self.count-1)); make.bottom.equalTo(tempSuperView).multipliedBy(i/((CGFloat)self.count-1)).with.offset(offset); } } else {//first one make.top.equalTo(tempSuperView).offset(leadSpacing); } }]; prev = v; } } } - (MAS_VIEW *)mas_commonSuperviewOfViews { MAS_VIEW *commonSuperview = nil; MAS_VIEW *previousView = nil; for (id object in self) { if ([object isKindOfClass:[MAS_VIEW class]]) { MAS_VIEW *view = (MAS_VIEW *)object; if (previousView) { commonSuperview = [view mas_closestCommonSuperview:commonSuperview]; } else { commonSuperview = view; } previousView = view; } } NSAssert(commonSuperview, @"Can't constrain views that do not share a common superview. Make sure that all the views in this array have been added into the same view hierarchy."); return commonSuperview; } @end ================================================ FILE: KJBannerViewDemo/Masonry/NSArray+MASShorthandAdditions.h ================================================ // // NSArray+MASShorthandAdditions.h // Masonry // // Created by Jonas Budelmann on 22/07/13. // Copyright (c) 2013 Jonas Budelmann. All rights reserved. // #import "NSArray+MASAdditions.h" #ifdef MAS_SHORTHAND /** * Shorthand array additions without the 'mas_' prefixes, * only enabled if MAS_SHORTHAND is defined */ @interface NSArray (MASShorthandAdditions) - (NSArray *)makeConstraints:(void(^)(MASConstraintMaker *make))block; - (NSArray *)updateConstraints:(void(^)(MASConstraintMaker *make))block; - (NSArray *)remakeConstraints:(void(^)(MASConstraintMaker *make))block; @end @implementation NSArray (MASShorthandAdditions) - (NSArray *)makeConstraints:(void(^)(MASConstraintMaker *))block { return [self mas_makeConstraints:block]; } - (NSArray *)updateConstraints:(void(^)(MASConstraintMaker *))block { return [self mas_updateConstraints:block]; } - (NSArray *)remakeConstraints:(void(^)(MASConstraintMaker *))block { return [self mas_remakeConstraints:block]; } @end #endif ================================================ FILE: KJBannerViewDemo/Masonry/NSLayoutConstraint+MASDebugAdditions.h ================================================ // // NSLayoutConstraint+MASDebugAdditions.h // Masonry // // Created by Jonas Budelmann on 3/08/13. // Copyright (c) 2013 Jonas Budelmann. All rights reserved. // #import "MASUtilities.h" /** * makes debug and log output of NSLayoutConstraints more readable */ @interface NSLayoutConstraint (MASDebugAdditions) @end ================================================ FILE: KJBannerViewDemo/Masonry/NSLayoutConstraint+MASDebugAdditions.m ================================================ // // NSLayoutConstraint+MASDebugAdditions.m // Masonry // // Created by Jonas Budelmann on 3/08/13. // Copyright (c) 2013 Jonas Budelmann. All rights reserved. // #import "NSLayoutConstraint+MASDebugAdditions.h" #import "MASConstraint.h" #import "MASLayoutConstraint.h" @implementation NSLayoutConstraint (MASDebugAdditions) #pragma mark - description maps + (NSDictionary *)layoutRelationDescriptionsByValue { static dispatch_once_t once; static NSDictionary *descriptionMap; dispatch_once(&once, ^{ descriptionMap = @{ @(NSLayoutRelationEqual) : @"==", @(NSLayoutRelationGreaterThanOrEqual) : @">=", @(NSLayoutRelationLessThanOrEqual) : @"<=", }; }); return descriptionMap; } + (NSDictionary *)layoutAttributeDescriptionsByValue { static dispatch_once_t once; static NSDictionary *descriptionMap; dispatch_once(&once, ^{ descriptionMap = @{ @(NSLayoutAttributeTop) : @"top", @(NSLayoutAttributeLeft) : @"left", @(NSLayoutAttributeBottom) : @"bottom", @(NSLayoutAttributeRight) : @"right", @(NSLayoutAttributeLeading) : @"leading", @(NSLayoutAttributeTrailing) : @"trailing", @(NSLayoutAttributeWidth) : @"width", @(NSLayoutAttributeHeight) : @"height", @(NSLayoutAttributeCenterX) : @"centerX", @(NSLayoutAttributeCenterY) : @"centerY", @(NSLayoutAttributeBaseline) : @"baseline", #if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (__TV_OS_VERSION_MIN_REQUIRED >= 9000) || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 101100) @(NSLayoutAttributeFirstBaseline) : @"firstBaseline", @(NSLayoutAttributeLastBaseline) : @"lastBaseline", #endif #if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (__TV_OS_VERSION_MIN_REQUIRED >= 9000) @(NSLayoutAttributeLeftMargin) : @"leftMargin", @(NSLayoutAttributeRightMargin) : @"rightMargin", @(NSLayoutAttributeTopMargin) : @"topMargin", @(NSLayoutAttributeBottomMargin) : @"bottomMargin", @(NSLayoutAttributeLeadingMargin) : @"leadingMargin", @(NSLayoutAttributeTrailingMargin) : @"trailingMargin", @(NSLayoutAttributeCenterXWithinMargins) : @"centerXWithinMargins", @(NSLayoutAttributeCenterYWithinMargins) : @"centerYWithinMargins", #endif }; }); return descriptionMap; } + (NSDictionary *)layoutPriorityDescriptionsByValue { static dispatch_once_t once; static NSDictionary *descriptionMap; dispatch_once(&once, ^{ #if TARGET_OS_IPHONE || TARGET_OS_TV descriptionMap = @{ @(MASLayoutPriorityDefaultHigh) : @"high", @(MASLayoutPriorityDefaultLow) : @"low", @(MASLayoutPriorityDefaultMedium) : @"medium", @(MASLayoutPriorityRequired) : @"required", @(MASLayoutPriorityFittingSizeLevel) : @"fitting size", }; #elif TARGET_OS_MAC descriptionMap = @{ @(MASLayoutPriorityDefaultHigh) : @"high", @(MASLayoutPriorityDragThatCanResizeWindow) : @"drag can resize window", @(MASLayoutPriorityDefaultMedium) : @"medium", @(MASLayoutPriorityWindowSizeStayPut) : @"window size stay put", @(MASLayoutPriorityDragThatCannotResizeWindow) : @"drag cannot resize window", @(MASLayoutPriorityDefaultLow) : @"low", @(MASLayoutPriorityFittingSizeCompression) : @"fitting size", @(MASLayoutPriorityRequired) : @"required", }; #endif }); return descriptionMap; } #pragma mark - description override + (NSString *)descriptionForObject:(id)obj { if ([obj respondsToSelector:@selector(mas_key)] && [obj mas_key]) { return [NSString stringWithFormat:@"%@:%@", [obj class], [obj mas_key]]; } return [NSString stringWithFormat:@"%@:%p", [obj class], obj]; } - (NSString *)description { NSMutableString *description = [[NSMutableString alloc] initWithString:@"<"]; [description appendString:[self.class descriptionForObject:self]]; [description appendFormat:@" %@", [self.class descriptionForObject:self.firstItem]]; if (self.firstAttribute != NSLayoutAttributeNotAnAttribute) { [description appendFormat:@".%@", self.class.layoutAttributeDescriptionsByValue[@(self.firstAttribute)]]; } [description appendFormat:@" %@", self.class.layoutRelationDescriptionsByValue[@(self.relation)]]; if (self.secondItem) { [description appendFormat:@" %@", [self.class descriptionForObject:self.secondItem]]; } if (self.secondAttribute != NSLayoutAttributeNotAnAttribute) { [description appendFormat:@".%@", self.class.layoutAttributeDescriptionsByValue[@(self.secondAttribute)]]; } if (self.multiplier != 1) { [description appendFormat:@" * %g", self.multiplier]; } if (self.secondAttribute == NSLayoutAttributeNotAnAttribute) { [description appendFormat:@" %g", self.constant]; } else { if (self.constant) { [description appendFormat:@" %@ %g", (self.constant < 0 ? @"-" : @"+"), ABS(self.constant)]; } } if (self.priority != MASLayoutPriorityRequired) { [description appendFormat:@" ^%@", self.class.layoutPriorityDescriptionsByValue[@(self.priority)] ?: [NSNumber numberWithDouble:self.priority]]; } [description appendString:@">"]; return description; } @end ================================================ FILE: KJBannerViewDemo/Masonry/View+MASAdditions.h ================================================ // // UIView+MASAdditions.h // Masonry // // Created by Jonas Budelmann on 20/07/13. // Copyright (c) 2013 cloudling. All rights reserved. // #import "MASUtilities.h" #import "MASConstraintMaker.h" #import "MASViewAttribute.h" /** * Provides constraint maker block * and convience methods for creating MASViewAttribute which are view + NSLayoutAttribute pairs */ @interface MAS_VIEW (MASAdditions) /** * following properties return a new MASViewAttribute with current view and appropriate NSLayoutAttribute */ @property (nonatomic, strong, readonly) MASViewAttribute *mas_left; @property (nonatomic, strong, readonly) MASViewAttribute *mas_top; @property (nonatomic, strong, readonly) MASViewAttribute *mas_right; @property (nonatomic, strong, readonly) MASViewAttribute *mas_bottom; @property (nonatomic, strong, readonly) MASViewAttribute *mas_leading; @property (nonatomic, strong, readonly) MASViewAttribute *mas_trailing; @property (nonatomic, strong, readonly) MASViewAttribute *mas_width; @property (nonatomic, strong, readonly) MASViewAttribute *mas_height; @property (nonatomic, strong, readonly) MASViewAttribute *mas_centerX; @property (nonatomic, strong, readonly) MASViewAttribute *mas_centerY; @property (nonatomic, strong, readonly) MASViewAttribute *mas_baseline; @property (nonatomic, strong, readonly) MASViewAttribute *(^mas_attribute)(NSLayoutAttribute attr); #if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (__TV_OS_VERSION_MIN_REQUIRED >= 9000) || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 101100) @property (nonatomic, strong, readonly) MASViewAttribute *mas_firstBaseline; @property (nonatomic, strong, readonly) MASViewAttribute *mas_lastBaseline; #endif #if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (__TV_OS_VERSION_MIN_REQUIRED >= 9000) @property (nonatomic, strong, readonly) MASViewAttribute *mas_leftMargin; @property (nonatomic, strong, readonly) MASViewAttribute *mas_rightMargin; @property (nonatomic, strong, readonly) MASViewAttribute *mas_topMargin; @property (nonatomic, strong, readonly) MASViewAttribute *mas_bottomMargin; @property (nonatomic, strong, readonly) MASViewAttribute *mas_leadingMargin; @property (nonatomic, strong, readonly) MASViewAttribute *mas_trailingMargin; @property (nonatomic, strong, readonly) MASViewAttribute *mas_centerXWithinMargins; @property (nonatomic, strong, readonly) MASViewAttribute *mas_centerYWithinMargins; #endif #if (__IPHONE_OS_VERSION_MAX_ALLOWED >= 110000) || (__TV_OS_VERSION_MAX_ALLOWED >= 110000) @property (nonatomic, strong, readonly) MASViewAttribute *mas_safeAreaLayoutGuide API_AVAILABLE(ios(11.0),tvos(11.0)); @property (nonatomic, strong, readonly) MASViewAttribute *mas_safeAreaLayoutGuideTop API_AVAILABLE(ios(11.0),tvos(11.0)); @property (nonatomic, strong, readonly) MASViewAttribute *mas_safeAreaLayoutGuideBottom API_AVAILABLE(ios(11.0),tvos(11.0)); @property (nonatomic, strong, readonly) MASViewAttribute *mas_safeAreaLayoutGuideLeft API_AVAILABLE(ios(11.0),tvos(11.0)); @property (nonatomic, strong, readonly) MASViewAttribute *mas_safeAreaLayoutGuideRight API_AVAILABLE(ios(11.0),tvos(11.0)); #endif /** * a key to associate with this view */ @property (nonatomic, strong) id mas_key; /** * Finds the closest common superview between this view and another view * * @param view other view * * @return returns nil if common superview could not be found */ - (instancetype)mas_closestCommonSuperview:(MAS_VIEW *)view; /** * Creates a MASConstraintMaker with the callee view. * Any constraints defined are added to the view or the appropriate superview once the block has finished executing * * @param block scope within which you can build up the constraints which you wish to apply to the view. * * @return Array of created MASConstraints */ - (NSArray *)mas_makeConstraints:(void(NS_NOESCAPE ^)(MASConstraintMaker *make))block; /** * Creates a MASConstraintMaker with the callee view. * Any constraints defined are added to the view or the appropriate superview once the block has finished executing. * If an existing constraint exists then it will be updated instead. * * @param block scope within which you can build up the constraints which you wish to apply to the view. * * @return Array of created/updated MASConstraints */ - (NSArray *)mas_updateConstraints:(void(NS_NOESCAPE ^)(MASConstraintMaker *make))block; /** * Creates a MASConstraintMaker with the callee view. * Any constraints defined are added to the view or the appropriate superview once the block has finished executing. * All constraints previously installed for the view will be removed. * * @param block scope within which you can build up the constraints which you wish to apply to the view. * * @return Array of created/updated MASConstraints */ - (NSArray *)mas_remakeConstraints:(void(NS_NOESCAPE ^)(MASConstraintMaker *make))block; @end ================================================ FILE: KJBannerViewDemo/Masonry/View+MASAdditions.m ================================================ // // UIView+MASAdditions.m // Masonry // // Created by Jonas Budelmann on 20/07/13. // Copyright (c) 2013 cloudling. All rights reserved. // #import "View+MASAdditions.h" #import @implementation MAS_VIEW (MASAdditions) - (NSArray *)mas_makeConstraints:(void(^)(MASConstraintMaker *))block { self.translatesAutoresizingMaskIntoConstraints = NO; MASConstraintMaker *constraintMaker = [[MASConstraintMaker alloc] initWithView:self]; block(constraintMaker); return [constraintMaker install]; } - (NSArray *)mas_updateConstraints:(void(^)(MASConstraintMaker *))block { self.translatesAutoresizingMaskIntoConstraints = NO; MASConstraintMaker *constraintMaker = [[MASConstraintMaker alloc] initWithView:self]; constraintMaker.updateExisting = YES; block(constraintMaker); return [constraintMaker install]; } - (NSArray *)mas_remakeConstraints:(void(^)(MASConstraintMaker *make))block { self.translatesAutoresizingMaskIntoConstraints = NO; MASConstraintMaker *constraintMaker = [[MASConstraintMaker alloc] initWithView:self]; constraintMaker.removeExisting = YES; block(constraintMaker); return [constraintMaker install]; } #pragma mark - NSLayoutAttribute properties - (MASViewAttribute *)mas_left { return [[MASViewAttribute alloc] initWithView:self layoutAttribute:NSLayoutAttributeLeft]; } - (MASViewAttribute *)mas_top { return [[MASViewAttribute alloc] initWithView:self layoutAttribute:NSLayoutAttributeTop]; } - (MASViewAttribute *)mas_right { return [[MASViewAttribute alloc] initWithView:self layoutAttribute:NSLayoutAttributeRight]; } - (MASViewAttribute *)mas_bottom { return [[MASViewAttribute alloc] initWithView:self layoutAttribute:NSLayoutAttributeBottom]; } - (MASViewAttribute *)mas_leading { return [[MASViewAttribute alloc] initWithView:self layoutAttribute:NSLayoutAttributeLeading]; } - (MASViewAttribute *)mas_trailing { return [[MASViewAttribute alloc] initWithView:self layoutAttribute:NSLayoutAttributeTrailing]; } - (MASViewAttribute *)mas_width { return [[MASViewAttribute alloc] initWithView:self layoutAttribute:NSLayoutAttributeWidth]; } - (MASViewAttribute *)mas_height { return [[MASViewAttribute alloc] initWithView:self layoutAttribute:NSLayoutAttributeHeight]; } - (MASViewAttribute *)mas_centerX { return [[MASViewAttribute alloc] initWithView:self layoutAttribute:NSLayoutAttributeCenterX]; } - (MASViewAttribute *)mas_centerY { return [[MASViewAttribute alloc] initWithView:self layoutAttribute:NSLayoutAttributeCenterY]; } - (MASViewAttribute *)mas_baseline { return [[MASViewAttribute alloc] initWithView:self layoutAttribute:NSLayoutAttributeBaseline]; } - (MASViewAttribute *(^)(NSLayoutAttribute))mas_attribute { return ^(NSLayoutAttribute attr) { return [[MASViewAttribute alloc] initWithView:self layoutAttribute:attr]; }; } #if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (__TV_OS_VERSION_MIN_REQUIRED >= 9000) || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 101100) - (MASViewAttribute *)mas_firstBaseline { return [[MASViewAttribute alloc] initWithView:self layoutAttribute:NSLayoutAttributeFirstBaseline]; } - (MASViewAttribute *)mas_lastBaseline { return [[MASViewAttribute alloc] initWithView:self layoutAttribute:NSLayoutAttributeLastBaseline]; } #endif #if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (__TV_OS_VERSION_MIN_REQUIRED >= 9000) - (MASViewAttribute *)mas_leftMargin { return [[MASViewAttribute alloc] initWithView:self layoutAttribute:NSLayoutAttributeLeftMargin]; } - (MASViewAttribute *)mas_rightMargin { return [[MASViewAttribute alloc] initWithView:self layoutAttribute:NSLayoutAttributeRightMargin]; } - (MASViewAttribute *)mas_topMargin { return [[MASViewAttribute alloc] initWithView:self layoutAttribute:NSLayoutAttributeTopMargin]; } - (MASViewAttribute *)mas_bottomMargin { return [[MASViewAttribute alloc] initWithView:self layoutAttribute:NSLayoutAttributeBottomMargin]; } - (MASViewAttribute *)mas_leadingMargin { return [[MASViewAttribute alloc] initWithView:self layoutAttribute:NSLayoutAttributeLeadingMargin]; } - (MASViewAttribute *)mas_trailingMargin { return [[MASViewAttribute alloc] initWithView:self layoutAttribute:NSLayoutAttributeTrailingMargin]; } - (MASViewAttribute *)mas_centerXWithinMargins { return [[MASViewAttribute alloc] initWithView:self layoutAttribute:NSLayoutAttributeCenterXWithinMargins]; } - (MASViewAttribute *)mas_centerYWithinMargins { return [[MASViewAttribute alloc] initWithView:self layoutAttribute:NSLayoutAttributeCenterYWithinMargins]; } #endif #if (__IPHONE_OS_VERSION_MAX_ALLOWED >= 110000) || (__TV_OS_VERSION_MAX_ALLOWED >= 110000) - (MASViewAttribute *)mas_safeAreaLayoutGuide { return [[MASViewAttribute alloc] initWithView:self item:self.safeAreaLayoutGuide layoutAttribute:NSLayoutAttributeBottom]; } - (MASViewAttribute *)mas_safeAreaLayoutGuideTop { return [[MASViewAttribute alloc] initWithView:self item:self.safeAreaLayoutGuide layoutAttribute:NSLayoutAttributeTop]; } - (MASViewAttribute *)mas_safeAreaLayoutGuideBottom { return [[MASViewAttribute alloc] initWithView:self item:self.safeAreaLayoutGuide layoutAttribute:NSLayoutAttributeBottom]; } - (MASViewAttribute *)mas_safeAreaLayoutGuideLeft { return [[MASViewAttribute alloc] initWithView:self item:self.safeAreaLayoutGuide layoutAttribute:NSLayoutAttributeLeft]; } - (MASViewAttribute *)mas_safeAreaLayoutGuideRight { return [[MASViewAttribute alloc] initWithView:self item:self.safeAreaLayoutGuide layoutAttribute:NSLayoutAttributeRight]; } #endif #pragma mark - associated properties - (id)mas_key { return objc_getAssociatedObject(self, @selector(mas_key)); } - (void)setMas_key:(id)key { objc_setAssociatedObject(self, @selector(mas_key), key, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } #pragma mark - heirachy - (instancetype)mas_closestCommonSuperview:(MAS_VIEW *)view { MAS_VIEW *closestCommonSuperview = nil; MAS_VIEW *secondViewSuperview = view; while (!closestCommonSuperview && secondViewSuperview) { MAS_VIEW *firstViewSuperview = self; while (!closestCommonSuperview && firstViewSuperview) { if (secondViewSuperview == firstViewSuperview) { closestCommonSuperview = secondViewSuperview; } firstViewSuperview = firstViewSuperview.superview; } secondViewSuperview = secondViewSuperview.superview; } return closestCommonSuperview; } @end ================================================ FILE: KJBannerViewDemo/Masonry/View+MASShorthandAdditions.h ================================================ // // UIView+MASShorthandAdditions.h // Masonry // // Created by Jonas Budelmann on 22/07/13. // Copyright (c) 2013 Jonas Budelmann. All rights reserved. // #import "View+MASAdditions.h" #ifdef MAS_SHORTHAND /** * Shorthand view additions without the 'mas_' prefixes, * only enabled if MAS_SHORTHAND is defined */ @interface MAS_VIEW (MASShorthandAdditions) @property (nonatomic, strong, readonly) MASViewAttribute *left; @property (nonatomic, strong, readonly) MASViewAttribute *top; @property (nonatomic, strong, readonly) MASViewAttribute *right; @property (nonatomic, strong, readonly) MASViewAttribute *bottom; @property (nonatomic, strong, readonly) MASViewAttribute *leading; @property (nonatomic, strong, readonly) MASViewAttribute *trailing; @property (nonatomic, strong, readonly) MASViewAttribute *width; @property (nonatomic, strong, readonly) MASViewAttribute *height; @property (nonatomic, strong, readonly) MASViewAttribute *centerX; @property (nonatomic, strong, readonly) MASViewAttribute *centerY; @property (nonatomic, strong, readonly) MASViewAttribute *baseline; @property (nonatomic, strong, readonly) MASViewAttribute *(^attribute)(NSLayoutAttribute attr); #if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (__TV_OS_VERSION_MIN_REQUIRED >= 9000) || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 101100) @property (nonatomic, strong, readonly) MASViewAttribute *firstBaseline; @property (nonatomic, strong, readonly) MASViewAttribute *lastBaseline; #endif #if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (__TV_OS_VERSION_MIN_REQUIRED >= 9000) @property (nonatomic, strong, readonly) MASViewAttribute *leftMargin; @property (nonatomic, strong, readonly) MASViewAttribute *rightMargin; @property (nonatomic, strong, readonly) MASViewAttribute *topMargin; @property (nonatomic, strong, readonly) MASViewAttribute *bottomMargin; @property (nonatomic, strong, readonly) MASViewAttribute *leadingMargin; @property (nonatomic, strong, readonly) MASViewAttribute *trailingMargin; @property (nonatomic, strong, readonly) MASViewAttribute *centerXWithinMargins; @property (nonatomic, strong, readonly) MASViewAttribute *centerYWithinMargins; #endif #if (__IPHONE_OS_VERSION_MAX_ALLOWED >= 110000) || (__TV_OS_VERSION_MAX_ALLOWED >= 110000) @property (nonatomic, strong, readonly) MASViewAttribute *safeAreaLayoutGuideTop API_AVAILABLE(ios(11.0),tvos(11.0)); @property (nonatomic, strong, readonly) MASViewAttribute *safeAreaLayoutGuideBottom API_AVAILABLE(ios(11.0),tvos(11.0)); @property (nonatomic, strong, readonly) MASViewAttribute *safeAreaLayoutGuideLeft API_AVAILABLE(ios(11.0),tvos(11.0)); @property (nonatomic, strong, readonly) MASViewAttribute *safeAreaLayoutGuideRight API_AVAILABLE(ios(11.0),tvos(11.0)); #endif - (NSArray *)makeConstraints:(void(^)(MASConstraintMaker *make))block; - (NSArray *)updateConstraints:(void(^)(MASConstraintMaker *make))block; - (NSArray *)remakeConstraints:(void(^)(MASConstraintMaker *make))block; @end #define MAS_ATTR_FORWARD(attr) \ - (MASViewAttribute *)attr { \ return [self mas_##attr]; \ } @implementation MAS_VIEW (MASShorthandAdditions) MAS_ATTR_FORWARD(top); MAS_ATTR_FORWARD(left); MAS_ATTR_FORWARD(bottom); MAS_ATTR_FORWARD(right); MAS_ATTR_FORWARD(leading); MAS_ATTR_FORWARD(trailing); MAS_ATTR_FORWARD(width); MAS_ATTR_FORWARD(height); MAS_ATTR_FORWARD(centerX); MAS_ATTR_FORWARD(centerY); MAS_ATTR_FORWARD(baseline); #if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (__TV_OS_VERSION_MIN_REQUIRED >= 9000) || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 101100) MAS_ATTR_FORWARD(firstBaseline); MAS_ATTR_FORWARD(lastBaseline); #endif #if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (__TV_OS_VERSION_MIN_REQUIRED >= 9000) MAS_ATTR_FORWARD(leftMargin); MAS_ATTR_FORWARD(rightMargin); MAS_ATTR_FORWARD(topMargin); MAS_ATTR_FORWARD(bottomMargin); MAS_ATTR_FORWARD(leadingMargin); MAS_ATTR_FORWARD(trailingMargin); MAS_ATTR_FORWARD(centerXWithinMargins); MAS_ATTR_FORWARD(centerYWithinMargins); #endif #if (__IPHONE_OS_VERSION_MAX_ALLOWED >= 110000) || (__TV_OS_VERSION_MAX_ALLOWED >= 110000) MAS_ATTR_FORWARD(safeAreaLayoutGuideTop); MAS_ATTR_FORWARD(safeAreaLayoutGuideBottom); MAS_ATTR_FORWARD(safeAreaLayoutGuideLeft); MAS_ATTR_FORWARD(safeAreaLayoutGuideRight); #endif - (MASViewAttribute *(^)(NSLayoutAttribute))attribute { return [self mas_attribute]; } - (NSArray *)makeConstraints:(void(NS_NOESCAPE ^)(MASConstraintMaker *))block { return [self mas_makeConstraints:block]; } - (NSArray *)updateConstraints:(void(NS_NOESCAPE ^)(MASConstraintMaker *))block { return [self mas_updateConstraints:block]; } - (NSArray *)remakeConstraints:(void(NS_NOESCAPE ^)(MASConstraintMaker *))block { return [self mas_remakeConstraints:block]; } @end #endif ================================================ FILE: KJBannerViewDemo/Masonry/ViewController+MASAdditions.h ================================================ // // UIViewController+MASAdditions.h // Masonry // // Created by Craig Siemens on 2015-06-23. // // #import "MASUtilities.h" #import "MASConstraintMaker.h" #import "MASViewAttribute.h" #ifdef MAS_VIEW_CONTROLLER @interface MAS_VIEW_CONTROLLER (MASAdditions) /** * following properties return a new MASViewAttribute with appropriate UILayoutGuide and NSLayoutAttribute */ @property (nonatomic, strong, readonly) MASViewAttribute *mas_topLayoutGuide; @property (nonatomic, strong, readonly) MASViewAttribute *mas_bottomLayoutGuide; @property (nonatomic, strong, readonly) MASViewAttribute *mas_topLayoutGuideTop; @property (nonatomic, strong, readonly) MASViewAttribute *mas_topLayoutGuideBottom; @property (nonatomic, strong, readonly) MASViewAttribute *mas_bottomLayoutGuideTop; @property (nonatomic, strong, readonly) MASViewAttribute *mas_bottomLayoutGuideBottom; @end #endif ================================================ FILE: KJBannerViewDemo/Masonry/ViewController+MASAdditions.m ================================================ // // UIViewController+MASAdditions.m // Masonry // // Created by Craig Siemens on 2015-06-23. // // #import "ViewController+MASAdditions.h" #ifdef MAS_VIEW_CONTROLLER @implementation MAS_VIEW_CONTROLLER (MASAdditions) - (MASViewAttribute *)mas_topLayoutGuide { return [[MASViewAttribute alloc] initWithView:self.view item:self.topLayoutGuide layoutAttribute:NSLayoutAttributeBottom]; } - (MASViewAttribute *)mas_topLayoutGuideTop { return [[MASViewAttribute alloc] initWithView:self.view item:self.topLayoutGuide layoutAttribute:NSLayoutAttributeTop]; } - (MASViewAttribute *)mas_topLayoutGuideBottom { return [[MASViewAttribute alloc] initWithView:self.view item:self.topLayoutGuide layoutAttribute:NSLayoutAttributeBottom]; } - (MASViewAttribute *)mas_bottomLayoutGuide { return [[MASViewAttribute alloc] initWithView:self.view item:self.bottomLayoutGuide layoutAttribute:NSLayoutAttributeTop]; } - (MASViewAttribute *)mas_bottomLayoutGuideTop { return [[MASViewAttribute alloc] initWithView:self.view item:self.bottomLayoutGuide layoutAttribute:NSLayoutAttributeTop]; } - (MASViewAttribute *)mas_bottomLayoutGuideBottom { return [[MASViewAttribute alloc] initWithView:self.view item:self.bottomLayoutGuide layoutAttribute:NSLayoutAttributeBottom]; } @end #endif ================================================ FILE: KJBannerViewDemo/Modules/Controller/DownloadViewController.h ================================================ // // DownloadViewController.h // KJBannerViewDemo // // Created by 杨科军 on 2020/1/15. // Copyright © 2020 杨科军. All rights reserved. // https://github.com/yangKJ/KJBannerViewDemo #import NS_ASSUME_NONNULL_BEGIN @interface DownloadViewController : UIViewController @end NS_ASSUME_NONNULL_END ================================================ FILE: KJBannerViewDemo/Modules/Controller/DownloadViewController.m ================================================ // // DownloadViewController.m // KJBannerViewDemo // // Created by 杨科军 on 2020/1/15. // Copyright © 2020 杨科军. All rights reserved. // https://github.com/yangKJ/KJBannerViewDemo #import "DownloadViewController.h" #import "KJWebImageDownloader.h" #import "KJBannerHeader.h" #import "Masonry.h" @interface DownloadViewController () @property (nonatomic,strong) UILabel *downloadLabel; @property (nonatomic,strong) UILabel *totalLabel; @property (nonatomic,strong) UILabel *speedLabel; @property (nonatomic,strong) UILabel *progressLabel; @property (nonatomic,strong) UIButton *againButton; @property (nonatomic,strong) UIButton *webButton; @end @implementation DownloadViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view from its nib. [self setupUI]; [self buttonAction]; } - (void)setupUI{ self.view.backgroundColor = UIColor.whiteColor; [self.view addSubview:self.downloadLabel]; [self.view addSubview:self.totalLabel]; [self.view addSubview:self.speedLabel]; [self.view addSubview:self.progressLabel]; [self.view addSubview:self.againButton]; [self.view addSubview:self.webButton]; [self.downloadLabel mas_makeConstraints:^(MASConstraintMaker *make) { make.top.equalTo(self.view.mas_safeAreaLayoutGuideTop).offset(60); make.left.equalTo(self.view).offset(20); }]; [self.totalLabel mas_makeConstraints:^(MASConstraintMaker *make) { make.top.equalTo(self.downloadLabel.mas_bottom).offset(20); make.left.equalTo(self.view).offset(20); }]; [self.speedLabel mas_makeConstraints:^(MASConstraintMaker *make) { make.top.equalTo(self.totalLabel.mas_bottom).offset(20); make.left.equalTo(self.view).offset(20); }]; [self.progressLabel mas_makeConstraints:^(MASConstraintMaker *make) { make.top.equalTo(self.speedLabel.mas_bottom).offset(20); make.left.equalTo(self.view).offset(20); }]; [self.againButton mas_makeConstraints:^(MASConstraintMaker *make) { make.width.equalTo(@150); make.height.equalTo(@40); make.center.equalTo(self.view); }]; [self.webButton mas_makeConstraints:^(MASConstraintMaker *make) { make.left.right.equalTo(self.view).inset(20); make.bottom.equalTo(self.view.mas_safeAreaLayoutGuideBottom).inset(40); make.height.equalTo(@180); }]; } #pragma mark - action - (void)buttonAction{ dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_async(queue, ^{ __weak __typeof(self) weakself = self; NSString * url = @"https://mp4.vjshi.com/2018-03-30/1f36dd9819eeef0bc508414494d34ad9.mp4"; [KJWebImageDownloader kj_downloadDataWithURL:url progress:^(KJBannerDownloadProgress * pro) { [weakself displayProgress:pro]; }]; }); } - (void)displayProgress:(KJBannerDownloadProgress *)pro{ dispatch_async(dispatch_get_main_queue(), ^{ self.downloadLabel.text = [NSString stringWithFormat:@"已下载:%.2fkb",pro.downloadBytes/1024.]; self.totalLabel.text = [NSString stringWithFormat:@"总大小:%.2fkb",pro.totalBytes/1024.]; self.speedLabel.text = [NSString stringWithFormat:@"下载速度:%.2fkb/s",pro.speed]; self.progressLabel.text = [NSString stringWithFormat:@"下载进度:%.5f",pro.progress]; }); } #pragma mark - lazy - (UILabel *)downloadLabel{ if (!_downloadLabel) { _downloadLabel = [[UILabel alloc] init]; _downloadLabel.textColor = UIColor.blueColor; _downloadLabel.font = [UIFont systemFontOfSize:14]; } return _downloadLabel; } - (UILabel *)totalLabel{ if (!_totalLabel) { _totalLabel = [[UILabel alloc] init]; _totalLabel.textColor = UIColor.blueColor; _totalLabel.font = [UIFont systemFontOfSize:14]; } return _totalLabel; } - (UILabel *)speedLabel{ if (!_speedLabel) { _speedLabel = [[UILabel alloc] init]; _speedLabel.textColor = UIColor.blueColor; _speedLabel.font = [UIFont systemFontOfSize:14]; } return _speedLabel; } - (UILabel *)progressLabel{ if (!_progressLabel) { _progressLabel = [[UILabel alloc] init]; _progressLabel.textColor = UIColor.blueColor; _progressLabel.font = [UIFont systemFontOfSize:14]; } return _progressLabel; } - (UIButton *)againButton{ if (!_againButton) { _againButton = [UIButton buttonWithType:(UIButtonTypeCustom)]; _againButton.backgroundColor = UIColor.yellowColor; [_againButton setTitle:@"重新获取" forState:(UIControlStateNormal)]; [_againButton setTitleColor:UIColor.blueColor forState:(UIControlStateNormal)]; [_againButton addTarget:self action:@selector(buttonAction) forControlEvents:(UIControlEventTouchUpInside)]; } return _againButton; } - (UIButton *)webButton{ if (!_webButton) { _webButton = [UIButton buttonWithType:(UIButtonTypeCustom)]; _webButton.backgroundColor = [UIColor.greenColor colorWithAlphaComponent:0.3]; NSURL * URL = [NSURL URLWithString:@"http://photos.tuchong.com/285606/f/4374153.jpg"]; [_webButton kj_setImageWithURL:URL provider:nil]; } return _webButton; } @end ================================================ FILE: KJBannerViewDemo/Modules/Controller/ViewController.h ================================================ // // ViewController.h // KJBannerViewDemo // // Created by 杨科军 on 2018/12/22. // Copyright © 2018 杨科军. All rights reserved. // https://github.com/yangKJ/KJBannerViewDemo #import @interface ViewController : UIViewController @end ================================================ FILE: KJBannerViewDemo/Modules/Controller/ViewController.m ================================================ // // ViewController.m // KJBannerViewDemo // // Created by 杨科军 on 2018/12/22. // Copyright © 2018 杨科军. All rights reserved. // https://github.com/yangKJ/KJBannerViewDemo #import "ViewController.h" #import "KJBannerHeader.h" #import "KJCollectionViewCell.h" #import "DownloadViewController.h" #import "KJViewModel.h" #import "Masonry.h" @interface ViewController () @property (weak, nonatomic) IBOutlet KJBannerView *banner; @property (weak, nonatomic) IBOutlet KJBannerView *banner3; @property (weak, nonatomic) IBOutlet UIView *backView; @property (weak, nonatomic) IBOutlet UIButton *button; @property (weak, nonatomic) IBOutlet UISwitch *Switch; @property (weak, nonatomic) IBOutlet UILabel *label; @property (nonatomic,strong) KJBannerView *banner2; @property (nonatomic,strong) NSArray *datas; @property (nonatomic,strong) NSArray *banner3Datas; @property (nonatomic,strong) KJViewModel *viewModel; @end @implementation ViewController - (void)viewDidAppear:(BOOL)animated{ [super viewDidAppear:animated]; [self.banner kj_repauseTimer]; [self.banner2 kj_repauseTimer]; } - (void)viewDidDisappear:(BOOL)animated{ [super viewDidDisappear:animated]; [self.banner kj_pauseTimer]; [self.banner2 kj_pauseTimer]; } - (void)viewDidLoad { [super viewDidLoad]; [self setupUI]; [self setText]; [self setXib]; [self setMasonry]; [self _bingViewModel]; } - (void)setupUI{ BOOL isPhoneX = ({ BOOL isPhoneX = NO; if (@available(iOS 13.0, *)) { isPhoneX = [UIApplication sharedApplication].windows.firstObject.safeAreaInsets.bottom > 0.0; } else if (@available(iOS 11.0, *)) { isPhoneX = [[UIApplication sharedApplication] delegate].window.safeAreaInsets.bottom > 0.0; } (isPhoneX); }); [self.button addTarget:self action:@selector(clearAction) forControlEvents:(UIControlEventTouchUpInside)]; [self.Switch addTarget:self action:@selector(qiehuanAction:) forControlEvents:(UIControlEventValueChanged)]; UIButton *button = [UIButton buttonWithType:(UIButtonTypeCustom)]; button.frame = CGRectMake(10, self.view.frame.size.height-100-(isPhoneX ? 34.0f : 0.0f), self.view.frame.size.width-20, 100); NSDictionary *attributes = @{ NSUnderlineStyleAttributeName:@(NSUnderlineStyleSingle), NSForegroundColorAttributeName:UIColor.redColor }; NSMutableAttributedString *attrStr = [[NSMutableAttributedString alloc] initWithString:@"大家觉得好用还请点个星,遇见什么问题也可issues,持续更新ing.." attributes:attributes]; [button setAttributedTitle:attrStr forState:(UIControlStateNormal)]; button.titleLabel.numberOfLines = 0; button.titleLabel.textAlignment = 1; [button addTarget:self action:@selector(kj_button) forControlEvents:(UIControlEventTouchUpInside)]; [self.view addSubview:button]; self.Switch.on = NO; } - (void)setText{ self.banner3.dataSource = self; self.banner3.showPageControl = NO; self.banner3.rollType = KJBannerViewRollDirectionTypeBottomToTop; [self.banner3 registerClass:[KJCollectionViewCell class] forCellWithReuseIdentifier:@"banner3"]; self.banner3Datas = @[ @"测试文本滚动", @"觉得好用请给我点个星", @"有什么问题也可以联系我", @"邮箱: ykj310@126.com" ]; [self.banner3 reloadData]; } - (void)setXib{ self.banner.delegate = self; self.banner.dataSource = self; self.banner.pageControl.pageType = PageControlStyleRectangle; self.banner.pageControl.selectColor = UIColor.greenColor; self.banner.pageControl.dotwidth = 20; self.banner.pageControl.dotheight = 2; self.banner.pageControl.displayType = KJPageControlDisplayTypeLeft; self.banner.pageControl.backgroundColor = [UIColor.blackColor colorWithAlphaComponent:0.5]; self.banner.rollType = KJBannerViewRollDirectionTypeBottomToTop; [self.banner registerClass:[KJBannerViewCell class] forCellWithReuseIdentifier:@"KJBannerViewCell"]; self.datas = @[ @"IMG_Guitar_52", @"IMG_0139", @"http://photos.tuchong.com/285606/f/4374153.jpg", @"https://up.54fcnr.com/pic_source/f7/1f/5d/f71f5d3cbf13f1f0f7da798aa8ddb4f9.gif", ]; [self.banner reloadData]; } - (void)setMasonry{ [self.backView addSubview:self.banner2]; [self.banner2 mas_makeConstraints:^(MASConstraintMaker *make) { make.top.left.right.bottom.equalTo(self.backView); }]; [self.banner2 kj_useMasonry]; } #pragma mark - setDatas - (void)_bingViewModel{ self.viewModel = [[KJViewModel alloc] init]; [self.viewModel refresh:^(NSArray * _Nonnull datas) { [self.banner2 reloadData]; } haveDatas:NO]; self.label.text = [NSString stringWithFormat:@"缓存大小:%.02f MB", [KJViewModel cacheSize]]; } #pragma mark - action - (void)kj_button{ NSURL * url = [NSURL URLWithString:@"https://github.com/yangKJ/KJBannerViewDemo"]; [[UIApplication sharedApplication] openURL:url options:@{} completionHandler:nil]; } - (void)qiehuanAction:(UISwitch *)sender{ [self.viewModel refresh:^(NSArray * _Nonnull datas) { [self.banner2 reloadData]; } haveDatas:sender.on]; } - (void)clearAction{ [KJViewModel clearCache]; self.label.text = [NSString stringWithFormat:@"缓存大小:%.02f MB", [KJViewModel cacheSize]]; } - (IBAction)pauseRoll:(UIButton *)sender { [self.banner kj_pauseTimer]; [self.banner2 kj_pauseTimer]; [self.banner3 kj_pauseTimer]; } - (IBAction)repauseRoll:(UIButton *)sender { [self.banner kj_repauseTimer]; [self.banner2 kj_repauseTimer]; [self.banner3 kj_repauseTimer]; } #pragma mark - KJBannerViewDelegate - (void)kj_bannerView:(KJBannerView *)banner didSelectItemAtIndex:(NSInteger)index{ DownloadViewController *vc = [DownloadViewController new]; [self.navigationController pushViewController:vc animated:YES]; } - (void)kj_bannerView:(KJBannerView *)banner loopScrolledItemAtIndex:(NSInteger)index{ } #pragma mark - KJBannerViewDataSource - (NSInteger)kj_numberOfItemsInBannerView:(KJBannerView *)banner { if (banner == self.banner) { return self.datas.count; } else if (banner == self.banner2) { return self.viewModel.datas.count; } return self.banner3Datas.count; } - (__kindof KJBannerViewCell *)kj_bannerView:(KJBannerView *)banner cellForItemAtIndex:(NSInteger)index { if (banner == self.banner) { KJBannerViewCell *cell = [banner dequeueReusableCellWithReuseIdentifier:@"KJBannerViewCell" forIndex:index]; cell.bannerContentMode = UIViewContentModeScaleAspectFill; cell.bannerCornerRadius = UIRectCornerTopRight | UIRectCornerBottomLeft | UIRectCornerBottomRight; cell.bannerNoPureBack = YES; cell.bannerRadius = 50; [cell setupImageURLString:self.datas[index] mineLoadImage:YES]; return cell; } else if (banner == self.banner2) { KJBannerViewCell *cell = [banner dequeueReusableCellWithReuseIdentifier:@"banner2" forIndex:index]; cell.bannerRadius = 20; cell.bannerNoPureBack = YES; cell.bannerRadiusColor = self.backView.backgroundColor; KJBannerModel *model = self.viewModel.datas[index]; [cell setupImageURLString:model.customImageUrl mineLoadImage:YES]; return cell; } KJCollectionViewCell *cell = [banner dequeueReusableCellWithReuseIdentifier:@"banner3" forIndex:index]; cell.title = self.banner3Datas[index]; return cell; } #pragma mark - lazy - (KJBannerView *)banner2{ if (!_banner2) { _banner2 = [[KJBannerView alloc] init]; _banner2.delegate = self; _banner2.dataSource = self; _banner2.autoTime = 3; _banner2.isZoom = YES; _banner2.itemSpace = -10; CGSize size = CGSizeApplyAffineTransform(self.backView.frame.size, CGAffineTransformMakeScale(.7, .7)); _banner2.itemWidth = size.width; _banner2.pageControl.pageType = PageControlStyleSizeDot; _banner2.pageControl.displayType = KJPageControlDisplayTypeRight; _banner2.pageControl.backgroundColor = [UIColor.blackColor colorWithAlphaComponent:0.5]; [_banner2 registerClass:[KJBannerViewCell class] forCellWithReuseIdentifier:@"banner2"]; } return _banner2; } @end ================================================ FILE: KJBannerViewDemo/Modules/Model/KJBannerModel.h ================================================ // // KJBannerModel.h // KJBannerViewDemo // // Created by 杨科军 on 2019/1/12. // Copyright © 2019 杨科军. All rights reserved. // https://github.com/yangKJ/KJBannerViewDemo #import #import NS_ASSUME_NONNULL_BEGIN @interface KJBannerModel : NSObject @property (nonatomic,strong) NSString *customImageUrl; @property (nonatomic,strong) NSString *customTitle; @property (nonatomic,strong) UIImage *customImage; //获取当前设备可用内存 + (double)availableMemory; //获取当前任务所占用内存 + (double)usedMemory; @end NS_ASSUME_NONNULL_END ================================================ FILE: KJBannerViewDemo/Modules/Model/KJBannerModel.m ================================================ // // KJBannerModel.m // KJBannerViewDemo // // Created by 杨科军 on 2019/1/12. // Copyright © 2019 杨科军. All rights reserved. // https://github.com/yangKJ/KJBannerViewDemo #import "KJBannerModel.h" #import #import @implementation KJBannerModel //获取当前设备可用内存 + (double)availableMemory{ vm_statistics_data_t vmStats; mach_msg_type_number_t infoCount = HOST_VM_INFO_COUNT; kern_return_t kernReturn = host_statistics(mach_host_self(), HOST_VM_INFO, (host_info_t)&vmStats, &infoCount); if (kernReturn != KERN_SUCCESS) { return NSNotFound; } return ((vm_page_size * vmStats.free_count)/1024.0)/1024.0; } //获取当前任务所占用内存 + (double)usedMemory{ task_basic_info_data_t taskInfo; mach_msg_type_number_t infoCount = TASK_BASIC_INFO_COUNT; kern_return_t kernReturn = task_info(mach_task_self(), TASK_BASIC_INFO, (task_info_t)&taskInfo, &infoCount); if (kernReturn != KERN_SUCCESS) { return NSNotFound; } return taskInfo.resident_size/1024.0/1024.0; } @end ================================================ FILE: KJBannerViewDemo/Modules/View/KJCollectionViewCell.h ================================================ // // KJCollectionViewCell.h // KJBannerViewDemo // // Created by 杨科军 on 2019/1/13. // Copyright © 2019 杨科军. All rights reserved. // https://github.com/yangKJ/KJBannerViewDemo #import "KJBannerViewCell.h" #import "KJBannerModel.h" NS_ASSUME_NONNULL_BEGIN @interface KJCollectionViewCell : KJBannerViewCell @property (nonatomic, strong) NSString *title; @end NS_ASSUME_NONNULL_END ================================================ FILE: KJBannerViewDemo/Modules/View/KJCollectionViewCell.m ================================================ // // KJCollectionViewCell.m // KJBannerViewDemo // // Created by 杨科军 on 2019/1/13. // Copyright © 2019 杨科军. All rights reserved. // https://github.com/yangKJ/KJBannerViewDemo #import "KJCollectionViewCell.h" @interface KJCollectionViewCell () @property (strong, nonatomic) UILabel *label; @end @implementation KJCollectionViewCell - (instancetype)initWithFrame:(CGRect)frame{ if (self = [super initWithFrame:frame]) { [self.contentView addSubview:self.label]; } return self; } - (void)setTitle:(NSString *)title{ self.label.text = title; } - (UILabel *)label{ if (!_label) { _label = [[UILabel alloc]initWithFrame:self.bounds]; _label.textColor = UIColor.blackColor; _label.font = [UIFont boldSystemFontOfSize:16]; } return _label; } @end ================================================ FILE: KJBannerViewDemo/Modules/ViewModel/KJViewModel.h ================================================ // // KJViewModel.h // KJBannerViewDemo // // Created by 77。 on 2020/9/18. // Copyright © 2020 杨科军. All rights reserved. // https://github.com/yangKJ/KJBannerViewDemo #import #import "KJBannerModel.h" NS_ASSUME_NONNULL_BEGIN @interface KJViewModel : NSObject @property (nonatomic, strong) NSArray *datas; /// 模拟网络请求 /// @param refresh 请求回调 /// @param have 是否有数据 - (void)refresh:(void(^)(NSArray* datas))refresh haveDatas:(BOOL)have; /// 缓存大小 + (CGFloat)cacheSize; /// 清除缓存 + (void)clearCache; @end NS_ASSUME_NONNULL_END ================================================ FILE: KJBannerViewDemo/Modules/ViewModel/KJViewModel.m ================================================ // // KJViewModel.m // KJBannerViewDemo // // Created by 77。 on 2020/9/18. // Copyright © 2020 杨科军. All rights reserved. // https://github.com/yangKJ/KJBannerViewDemo #import "KJViewModel.h" #import "KJImageCache.h" #import "KJBannerViewTimer.h" #define gif @"https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/"\ "7a00f7f6c0c744a893f304a7d3b629b5~tplv-k3u1fbpfcp-watermark.image?" #define tu1 @"https://img.jwfzl.com.cn/storage/20210422/60810d41b6303.jpeg" #define tu2 @"http://photos.tuchong.com/285606/f/4374153.jpg" #define tu3 @"https://tfile.melinked.com/2021/01/5c071de1-b7e9-4bf4-a1f7-a2f35eff9ed6.jpg" @implementation KJViewModel /// 模拟网络请求 /// @param refresh 请求回调 /// @param have 是否有数据 - (void)refresh:(void(^)(NSArray* datas))refresh haveDatas:(BOOL)have{ if (have == NO) { // KJBannerModel *model = [[KJBannerModel alloc]init]; // model.customImageUrl = gif; // model.customTitle = @"单图"; // self.datas = @[model]; self.datas = @[]; refresh ? refresh(self.datas) : nil; return; } NSMutableArray *array = [NSMutableArray array]; dispatch_group_t dispatchGroup = dispatch_group_create(); dispatch_group_enter(dispatchGroup); dispatch_group_async(dispatchGroup, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_global_queue(0, 0), ^{ NSArray *images = @[tu3,tu2,tu1]; for (int i = 0; i < images.count; i++) { KJBannerModel *model = [[KJBannerModel alloc]init]; model.customImageUrl = images[i]; model.customTitle = [NSString stringWithFormat:@"A线程图片名称:%d",i]; [array addObject:model]; } dispatch_group_leave(dispatchGroup); }); }); dispatch_group_enter(dispatchGroup); dispatch_group_async(dispatchGroup, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_global_queue(0, 0), ^{ NSArray *images = @[@"",@"xsi"]; for (int i = 0; i < images.count; i++) { KJBannerModel *model = [[KJBannerModel alloc]init]; model.customImageUrl = images[i]; model.customTitle = @"B线程图片地址"; [array addObject:model]; } dispatch_group_leave(dispatchGroup); }); }); dispatch_group_notify(dispatchGroup, dispatch_get_main_queue(), ^(){ self.datas = array.mutableCopy; refresh ? refresh(array) : nil; }); } /// 缓存大小 + (CGFloat)cacheSize{ return [KJImageCache kj_getLocalityImageCacheSize] / 1024 / 1024.0; } /// 清除缓存 + (void)clearCache{ [KJImageCache kj_clearLocalityImageAndCache]; } @end ================================================ FILE: KJBannerViewDemo/Supporting Files/Assets.xcassets/AppIcon.appiconset/Contents.json ================================================ { "images" : [ { "size" : "20x20", "idiom" : "iphone", "filename" : "WechatIMG44iPhoneNotification_20pt@2x.png", "scale" : "2x" }, { "size" : "20x20", "idiom" : "iphone", "filename" : "WechatIMG44iPhoneNotification_20pt@3x.png", "scale" : "3x" }, { "size" : "29x29", "idiom" : "iphone", "filename" : "WechatIMG44iPhoneSpootlight5_29pt@2x.png", "scale" : "2x" }, { "size" : "29x29", "idiom" : "iphone", "filename" : "WechatIMG44iPhoneSpootlight5_29pt@3x.png", "scale" : "3x" }, { "size" : "40x40", "idiom" : "iphone", "filename" : "WechatIMG44iPhoneSpootlight7_40pt@2x.png", "scale" : "2x" }, { "size" : "40x40", "idiom" : "iphone", "filename" : "WechatIMG44iPhoneSpootlight7_40pt@3x.png", "scale" : "3x" }, { "size" : "60x60", "idiom" : "iphone", "filename" : "WechatIMG44iPhoneApp_60pt@2x.png", "scale" : "2x" }, { "size" : "60x60", "idiom" : "iphone", "filename" : "WechatIMG44iPhoneApp_60pt@3x.png", "scale" : "3x" }, { "size" : "20x20", "idiom" : "ipad", "filename" : "WechatIMG44iPadNotifications_20pt.png", "scale" : "1x" }, { "size" : "20x20", "idiom" : "ipad", "filename" : "WechatIMG44iPadNotifications_20pt@2x.png", "scale" : "2x" }, { "size" : "29x29", "idiom" : "ipad", "filename" : "WechatIMG44iPadSpootlight5_29pt.png", "scale" : "1x" }, { "size" : "29x29", "idiom" : "ipad", "filename" : "WechatIMG44iPadSpootlight5_29pt@2x.png", "scale" : "2x" }, { "size" : "40x40", "idiom" : "ipad", "filename" : "WechatIMG44iPadSpootlight7_40pt.png", "scale" : "1x" }, { "size" : "40x40", "idiom" : "ipad", "filename" : "WechatIMG44iPadSpootlight7_40pt@2x.png", "scale" : "2x" }, { "size" : "76x76", "idiom" : "ipad", "filename" : "WechatIMG44iPadApp_76pt.png", "scale" : "1x" }, { "size" : "76x76", "idiom" : "ipad", "filename" : "WechatIMG44iPadApp_76pt@2x.png", "scale" : "2x" }, { "size" : "83.5x83.5", "idiom" : "ipad", "filename" : "WechatIMG44iPadProApp_83.5pt@2x.png", "scale" : "2x" }, { "size" : "1024x1024", "idiom" : "ios-marketing", "filename" : "WechatIMG44store_1024pt.png", "scale" : "1x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: KJBannerViewDemo/Supporting Files/Assets.xcassets/Contents.json ================================================ { "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: KJBannerViewDemo/Supporting Files/Assets.xcassets/IMG_4931.imageset/Contents.json ================================================ { "images" : [ { "idiom" : "universal", "filename" : "IMG_4931.jpg", "scale" : "1x" }, { "idiom" : "universal", "scale" : "2x" }, { "idiom" : "universal", "scale" : "3x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: KJBannerViewDemo/Supporting Files/Assets.xcassets/IMG_4931.imageset/Contents.json~Add 2.0.3 ================================================ { "images" : [ { "idiom" : "universal", "filename" : "IMG_4931.jpg", "scale" : "1x" }, { "idiom" : "universal", "scale" : "2x" }, { "idiom" : "universal", "scale" : "3x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: KJBannerViewDemo/Supporting Files/Assets.xcassets/IMG_4931.imageset/Contents.json~HEAD ================================================ { "images" : [ { "idiom" : "universal", "filename" : "IMG_4931.jpg", "scale" : "1x" }, { "idiom" : "universal", "scale" : "2x" }, { "idiom" : "universal", "scale" : "3x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: KJBannerViewDemo/Supporting Files/Assets.xcassets/IMG_Guitar_52.imageset/Contents.json ================================================ { "images" : [ { "idiom" : "universal", "filename" : "jita.png", "scale" : "1x" }, { "idiom" : "universal", "scale" : "2x" }, { "idiom" : "universal", "scale" : "3x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: KJBannerViewDemo/Supporting Files/Assets.xcassets/ax.imageset/Contents.json ================================================ { "images" : [ { "filename" : "0161da5541af81000001a64bc753a4.jpg@1280w_1l_2o_100sh.jpg", "idiom" : "universal", "scale" : "1x" }, { "idiom" : "universal", "scale" : "2x" }, { "idiom" : "universal", "scale" : "3x" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: KJBannerViewDemo/Supporting Files/Assets.xcassets/z_ds.imageset/Contents.json ================================================ { "images" : [ { "filename" : "z_ds.png", "idiom" : "universal", "scale" : "1x" }, { "idiom" : "universal", "scale" : "2x" }, { "idiom" : "universal", "scale" : "3x" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: KJBannerViewDemo/Supporting Files/Base.lproj/LaunchScreen.storyboard ================================================ ================================================ FILE: KJBannerViewDemo/Supporting Files/Base.lproj/Main.storyboard ================================================ ================================================ FILE: KJBannerViewDemo/Supporting Files/Info.plist ================================================ CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleDisplayName BANNER CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName $(PRODUCT_NAME) CFBundlePackageType APPL CFBundleShortVersionString 1.0 CFBundleVersion 1 LSRequiresIPhoneOS NSAppTransportSecurity NSAllowsArbitraryLoads UILaunchStoryboardName LaunchScreen UIMainStoryboardFile Main UIRequiredDeviceCapabilities armv7 UISupportedInterfaceOrientations UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UISupportedInterfaceOrientations~ipad UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIUserInterfaceStyle Light ================================================ FILE: KJBannerViewDemo/Supporting Files/main.m ================================================ // // main.m // KJBannerViewDemo // // Created by 杨科军 on 2018/12/22. // Copyright © 2018 杨科军. All rights reserved. // https://github.com/yangKJ/KJBannerViewDemo #import #import "AppDelegate.h" int main(int argc, char * argv[]) { @autoreleasepool { return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); } } ================================================ FILE: KJBannerViewDemo.xcodeproj/project.pbxproj ================================================ // !$*UTF8*$! { archiveVersion = 1; classes = { }; objectVersion = 50; objects = { /* Begin PBXBuildFile section */ 684059DD21EA08D00092F630 /* KJBannerModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 684059DC21EA08D00092F630 /* KJBannerModel.m */; }; 684059E121EB59DA0092F630 /* KJCollectionViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 684059DF21EB59DA0092F630 /* KJCollectionViewCell.m */; }; 686568CB26FB5EDC0001C691 /* DownloadViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 686568C726FB5EDC0001C691 /* DownloadViewController.m */; }; 686568CC26FB5EDC0001C691 /* KJViewModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 686568C926FB5EDC0001C691 /* KJViewModel.m */; }; 6868EC2126FB5D54001F2400 /* NSLayoutConstraint+MASDebugAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 6868EC0926FB5D54001F2400 /* NSLayoutConstraint+MASDebugAdditions.m */; }; 6868EC2226FB5D54001F2400 /* ViewController+MASAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 6868EC1226FB5D54001F2400 /* ViewController+MASAdditions.m */; }; 6868EC2326FB5D54001F2400 /* MASCompositeConstraint.m in Sources */ = {isa = PBXBuildFile; fileRef = 6868EC1626FB5D54001F2400 /* MASCompositeConstraint.m */; }; 6868EC2426FB5D54001F2400 /* MASConstraintMaker.m in Sources */ = {isa = PBXBuildFile; fileRef = 6868EC1726FB5D54001F2400 /* MASConstraintMaker.m */; }; 6868EC2526FB5D54001F2400 /* MASLayoutConstraint.m in Sources */ = {isa = PBXBuildFile; fileRef = 6868EC1826FB5D54001F2400 /* MASLayoutConstraint.m */; }; 6868EC2626FB5D54001F2400 /* NSArray+MASAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 6868EC1926FB5D54001F2400 /* NSArray+MASAdditions.m */; }; 6868EC2726FB5D54001F2400 /* View+MASAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 6868EC1A26FB5D54001F2400 /* View+MASAdditions.m */; }; 6868EC2826FB5D54001F2400 /* MASConstraint.m in Sources */ = {isa = PBXBuildFile; fileRef = 6868EC1D26FB5D54001F2400 /* MASConstraint.m */; }; 6868EC2926FB5D54001F2400 /* MASViewConstraint.m in Sources */ = {isa = PBXBuildFile; fileRef = 6868EC1F26FB5D54001F2400 /* MASViewConstraint.m */; }; 6868EC2A26FB5D54001F2400 /* MASViewAttribute.m in Sources */ = {isa = PBXBuildFile; fileRef = 6868EC2026FB5D54001F2400 /* MASViewAttribute.m */; }; 68BD69F82580712000D4B3AF /* IMG_0139.GIF in Resources */ = {isa = PBXBuildFile; fileRef = 68BD69F72580712000D4B3AF /* IMG_0139.GIF */; }; 68E26B5321CE924700F91182 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 68E26B5221CE924700F91182 /* AppDelegate.m */; }; 68E26B5621CE924700F91182 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 68E26B5521CE924700F91182 /* ViewController.m */; }; 68E26B5921CE924700F91182 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 68E26B5721CE924700F91182 /* Main.storyboard */; }; 68E26B5B21CE924800F91182 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 68E26B5A21CE924800F91182 /* Assets.xcassets */; }; 68E26B5E21CE924800F91182 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 68E26B5C21CE924800F91182 /* LaunchScreen.storyboard */; }; 68E26B6121CE924800F91182 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 68E26B6021CE924800F91182 /* main.m */; }; 68FFE575273672AF008E323F /* KJBannerView.podspec in Resources */ = {isa = PBXBuildFile; fileRef = 68FFE573273672AE008E323F /* KJBannerView.podspec */; }; 68FFE576273672AF008E323F /* CHANGELOG.md in Resources */ = {isa = PBXBuildFile; fileRef = 68FFE574273672AE008E323F /* CHANGELOG.md */; }; 68FFE59927367754008E323F /* KJBannerView.m in Sources */ = {isa = PBXBuildFile; fileRef = 68FFE57E27367754008E323F /* KJBannerView.m */; }; 68FFE59C27367754008E323F /* KJBannerViewTimer.m in Sources */ = {isa = PBXBuildFile; fileRef = 68FFE58227367754008E323F /* KJBannerViewTimer.m */; }; 68FFE59D27367754008E323F /* KJBannerViewFlowLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = 68FFE58327367754008E323F /* KJBannerViewFlowLayout.m */; }; 68FFE59E27367754008E323F /* KJBannerViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 68FFE58627367754008E323F /* KJBannerViewCell.m */; }; 68FFE59F27367754008E323F /* KJBannerView.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 68FFE58827367754008E323F /* KJBannerView.bundle */; }; 68FFE5A027367754008E323F /* KJWebImageDownloader.m in Sources */ = {isa = PBXBuildFile; fileRef = 68FFE58B27367754008E323F /* KJWebImageDownloader.m */; }; 68FFE5A127367754008E323F /* UIView+KJWebImage.m in Sources */ = {isa = PBXBuildFile; fileRef = 68FFE59127367754008E323F /* UIView+KJWebImage.m */; }; 68FFE5A227367754008E323F /* KJImageCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 68FFE59227367754008E323F /* KJImageCache.m */; }; 68FFE5A327367754008E323F /* KJNetworkManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 68FFE59327367754008E323F /* KJNetworkManager.m */; }; 68FFE5A427367754008E323F /* KJAutoPurgingCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 68FFE59427367754008E323F /* KJAutoPurgingCache.m */; }; 68FFE5A527367754008E323F /* KJPageControl.m in Sources */ = {isa = PBXBuildFile; fileRef = 68FFE59727367754008E323F /* KJPageControl.m */; }; 68FFE5AC2736918F008E323F /* README.md in Resources */ = {isa = PBXBuildFile; fileRef = 68FFE5AB2736918F008E323F /* README.md */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ 684059DB21EA08D00092F630 /* KJBannerModel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KJBannerModel.h; sourceTree = ""; }; 684059DC21EA08D00092F630 /* KJBannerModel.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KJBannerModel.m; sourceTree = ""; }; 684059DE21EB59DA0092F630 /* KJCollectionViewCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KJCollectionViewCell.h; sourceTree = ""; }; 684059DF21EB59DA0092F630 /* KJCollectionViewCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KJCollectionViewCell.m; sourceTree = ""; }; 686568C726FB5EDC0001C691 /* DownloadViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DownloadViewController.m; sourceTree = ""; }; 686568C826FB5EDC0001C691 /* KJViewModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KJViewModel.h; sourceTree = ""; }; 686568C926FB5EDC0001C691 /* KJViewModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KJViewModel.m; sourceTree = ""; }; 686568CA26FB5EDC0001C691 /* DownloadViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DownloadViewController.h; sourceTree = ""; }; 6868EC0826FB5D54001F2400 /* MASCompositeConstraint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MASCompositeConstraint.h; sourceTree = ""; }; 6868EC0926FB5D54001F2400 /* NSLayoutConstraint+MASDebugAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSLayoutConstraint+MASDebugAdditions.m"; sourceTree = ""; }; 6868EC0A26FB5D54001F2400 /* MASConstraint+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MASConstraint+Private.h"; sourceTree = ""; }; 6868EC0B26FB5D54001F2400 /* MASLayoutConstraint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MASLayoutConstraint.h; sourceTree = ""; }; 6868EC0C26FB5D54001F2400 /* NSArray+MASShorthandAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSArray+MASShorthandAdditions.h"; sourceTree = ""; }; 6868EC0D26FB5D54001F2400 /* MASConstraintMaker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MASConstraintMaker.h; sourceTree = ""; }; 6868EC0E26FB5D54001F2400 /* View+MASAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "View+MASAdditions.h"; sourceTree = ""; }; 6868EC0F26FB5D54001F2400 /* NSArray+MASAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSArray+MASAdditions.h"; sourceTree = ""; }; 6868EC1026FB5D54001F2400 /* MASUtilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MASUtilities.h; sourceTree = ""; }; 6868EC1126FB5D54001F2400 /* MASViewAttribute.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MASViewAttribute.h; sourceTree = ""; }; 6868EC1226FB5D54001F2400 /* ViewController+MASAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "ViewController+MASAdditions.m"; sourceTree = ""; }; 6868EC1326FB5D54001F2400 /* MASViewConstraint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MASViewConstraint.h; sourceTree = ""; }; 6868EC1426FB5D54001F2400 /* MASConstraint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MASConstraint.h; sourceTree = ""; }; 6868EC1526FB5D54001F2400 /* NSLayoutConstraint+MASDebugAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSLayoutConstraint+MASDebugAdditions.h"; sourceTree = ""; }; 6868EC1626FB5D54001F2400 /* MASCompositeConstraint.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MASCompositeConstraint.m; sourceTree = ""; }; 6868EC1726FB5D54001F2400 /* MASConstraintMaker.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MASConstraintMaker.m; sourceTree = ""; }; 6868EC1826FB5D54001F2400 /* MASLayoutConstraint.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MASLayoutConstraint.m; sourceTree = ""; }; 6868EC1926FB5D54001F2400 /* NSArray+MASAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSArray+MASAdditions.m"; sourceTree = ""; }; 6868EC1A26FB5D54001F2400 /* View+MASAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "View+MASAdditions.m"; sourceTree = ""; }; 6868EC1B26FB5D54001F2400 /* View+MASShorthandAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "View+MASShorthandAdditions.h"; sourceTree = ""; }; 6868EC1C26FB5D54001F2400 /* Masonry.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Masonry.h; sourceTree = ""; }; 6868EC1D26FB5D54001F2400 /* MASConstraint.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MASConstraint.m; sourceTree = ""; }; 6868EC1E26FB5D54001F2400 /* ViewController+MASAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ViewController+MASAdditions.h"; sourceTree = ""; }; 6868EC1F26FB5D54001F2400 /* MASViewConstraint.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MASViewConstraint.m; sourceTree = ""; }; 6868EC2026FB5D54001F2400 /* MASViewAttribute.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MASViewAttribute.m; sourceTree = ""; }; 68BD69F72580712000D4B3AF /* IMG_0139.GIF */ = {isa = PBXFileReference; lastKnownFileType = image.gif; path = IMG_0139.GIF; sourceTree = ""; }; 68E26B4E21CE924700F91182 /* KJBannerViewDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = KJBannerViewDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 68E26B5121CE924700F91182 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 68E26B5221CE924700F91182 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 68E26B5421CE924700F91182 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 68E26B5521CE924700F91182 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 68E26B5821CE924700F91182 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 68E26B5A21CE924800F91182 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 68E26B5D21CE924800F91182 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 68E26B5F21CE924800F91182 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 68E26B6021CE924800F91182 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 68FFE573273672AE008E323F /* KJBannerView.podspec */ = {isa = PBXFileReference; lastKnownFileType = text; path = KJBannerView.podspec; sourceTree = SOURCE_ROOT; }; 68FFE574273672AE008E323F /* CHANGELOG.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = CHANGELOG.md; sourceTree = SOURCE_ROOT; }; 68FFE57C27367754008E323F /* KJBannerViewTimer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KJBannerViewTimer.h; sourceTree = ""; }; 68FFE57D27367754008E323F /* KJBannerViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KJBannerViewCell.h; sourceTree = ""; }; 68FFE57E27367754008E323F /* KJBannerView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KJBannerView.m; sourceTree = ""; }; 68FFE58027367754008E323F /* KJBannerViewFlowLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KJBannerViewFlowLayout.h; sourceTree = ""; }; 68FFE58227367754008E323F /* KJBannerViewTimer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KJBannerViewTimer.m; sourceTree = ""; }; 68FFE58327367754008E323F /* KJBannerViewFlowLayout.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KJBannerViewFlowLayout.m; sourceTree = ""; }; 68FFE58527367754008E323F /* KJBannerView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KJBannerView.h; sourceTree = ""; }; 68FFE58627367754008E323F /* KJBannerViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KJBannerViewCell.m; sourceTree = ""; }; 68FFE58827367754008E323F /* KJBannerView.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = KJBannerView.bundle; sourceTree = ""; }; 68FFE58A27367754008E323F /* UIView+KJWebImage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIView+KJWebImage.h"; sourceTree = ""; }; 68FFE58B27367754008E323F /* KJWebImageDownloader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KJWebImageDownloader.m; sourceTree = ""; }; 68FFE58C27367754008E323F /* KJImageCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KJImageCache.h; sourceTree = ""; }; 68FFE58D27367754008E323F /* KJAutoPurgingCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KJAutoPurgingCache.h; sourceTree = ""; }; 68FFE58E27367754008E323F /* KJNetworkManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KJNetworkManager.h; sourceTree = ""; }; 68FFE58F27367754008E323F /* KJWebImageDownloader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KJWebImageDownloader.h; sourceTree = ""; }; 68FFE59127367754008E323F /* UIView+KJWebImage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIView+KJWebImage.m"; sourceTree = ""; }; 68FFE59227367754008E323F /* KJImageCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KJImageCache.m; sourceTree = ""; }; 68FFE59327367754008E323F /* KJNetworkManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KJNetworkManager.m; sourceTree = ""; }; 68FFE59427367754008E323F /* KJAutoPurgingCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KJAutoPurgingCache.m; sourceTree = ""; }; 68FFE59627367754008E323F /* KJPageControl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KJPageControl.h; sourceTree = ""; }; 68FFE59727367754008E323F /* KJPageControl.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KJPageControl.m; sourceTree = ""; }; 68FFE59827367754008E323F /* KJBannerHeader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KJBannerHeader.h; sourceTree = ""; }; 68FFE5AA273686ED008E323F /* KJWebImageHeader.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KJWebImageHeader.h; sourceTree = ""; }; 68FFE5AB2736918F008E323F /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = SOURCE_ROOT; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ 68E26B4B21CE924700F91182 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ 06EFFDA7C3924E99F02B4C94 /* Frameworks */ = { isa = PBXGroup; children = ( ); name = Frameworks; sourceTree = ""; }; 6868EC0726FB5D54001F2400 /* Masonry */ = { isa = PBXGroup; children = ( 6868EC0826FB5D54001F2400 /* MASCompositeConstraint.h */, 6868EC1626FB5D54001F2400 /* MASCompositeConstraint.m */, 6868EC1426FB5D54001F2400 /* MASConstraint.h */, 6868EC1D26FB5D54001F2400 /* MASConstraint.m */, 6868EC0A26FB5D54001F2400 /* MASConstraint+Private.h */, 6868EC0D26FB5D54001F2400 /* MASConstraintMaker.h */, 6868EC1726FB5D54001F2400 /* MASConstraintMaker.m */, 6868EC0B26FB5D54001F2400 /* MASLayoutConstraint.h */, 6868EC1826FB5D54001F2400 /* MASLayoutConstraint.m */, 6868EC1C26FB5D54001F2400 /* Masonry.h */, 6868EC1026FB5D54001F2400 /* MASUtilities.h */, 6868EC1126FB5D54001F2400 /* MASViewAttribute.h */, 6868EC2026FB5D54001F2400 /* MASViewAttribute.m */, 6868EC1326FB5D54001F2400 /* MASViewConstraint.h */, 6868EC1F26FB5D54001F2400 /* MASViewConstraint.m */, 6868EC0F26FB5D54001F2400 /* NSArray+MASAdditions.h */, 6868EC1926FB5D54001F2400 /* NSArray+MASAdditions.m */, 6868EC0C26FB5D54001F2400 /* NSArray+MASShorthandAdditions.h */, 6868EC1526FB5D54001F2400 /* NSLayoutConstraint+MASDebugAdditions.h */, 6868EC0926FB5D54001F2400 /* NSLayoutConstraint+MASDebugAdditions.m */, 6868EC0E26FB5D54001F2400 /* View+MASAdditions.h */, 6868EC1A26FB5D54001F2400 /* View+MASAdditions.m */, 6868EC1B26FB5D54001F2400 /* View+MASShorthandAdditions.h */, 6868EC1E26FB5D54001F2400 /* ViewController+MASAdditions.h */, 6868EC1226FB5D54001F2400 /* ViewController+MASAdditions.m */, ); path = Masonry; sourceTree = ""; }; 68E26B4521CE924700F91182 = { isa = PBXGroup; children = ( 68E26B5021CE924700F91182 /* KJBannerViewDemo */, 68E26B4F21CE924700F91182 /* Products */, 06EFFDA7C3924E99F02B4C94 /* Frameworks */, ); sourceTree = ""; }; 68E26B4F21CE924700F91182 /* Products */ = { isa = PBXGroup; children = ( 68E26B4E21CE924700F91182 /* KJBannerViewDemo.app */, ); name = Products; sourceTree = ""; }; 68E26B5021CE924700F91182 /* KJBannerViewDemo */ = { isa = PBXGroup; children = ( 68E26B5121CE924700F91182 /* AppDelegate.h */, 68E26B5221CE924700F91182 /* AppDelegate.m */, 6868EC0726FB5D54001F2400 /* Masonry */, 68FFE56E27367023008E323F /* Modules */, 68FFE5702736719E008E323F /* Resources */, 68FFE57A27367754008E323F /* Sources */, 68FFE5722736725D008E323F /* Supporting Files */, ); path = KJBannerViewDemo; sourceTree = ""; }; 68FFE56E27367023008E323F /* Modules */ = { isa = PBXGroup; children = ( 68FFE5A6273678C1008E323F /* Controller */, 68FFE5A7273678F8008E323F /* Model */, 68FFE5A8273678FF008E323F /* View */, 68FFE5A927367908008E323F /* ViewModel */, ); path = Modules; sourceTree = ""; }; 68FFE5702736719E008E323F /* Resources */ = { isa = PBXGroup; children = ( 68BD69F72580712000D4B3AF /* IMG_0139.GIF */, ); path = Resources; sourceTree = ""; }; 68FFE5722736725D008E323F /* Supporting Files */ = { isa = PBXGroup; children = ( 68E26B5A21CE924800F91182 /* Assets.xcassets */, 68FFE574273672AE008E323F /* CHANGELOG.md */, 68E26B5F21CE924800F91182 /* Info.plist */, 68FFE573273672AE008E323F /* KJBannerView.podspec */, 68E26B5C21CE924800F91182 /* LaunchScreen.storyboard */, 68E26B6021CE924800F91182 /* main.m */, 68E26B5721CE924700F91182 /* Main.storyboard */, 68FFE5AB2736918F008E323F /* README.md */, ); path = "Supporting Files"; sourceTree = ""; }; 68FFE57A27367754008E323F /* Sources */ = { isa = PBXGroup; children = ( 68FFE59827367754008E323F /* KJBannerHeader.h */, 68FFE57B27367754008E323F /* KJBannerView */, 68FFE59527367754008E323F /* PageControl */, 68FFE58927367754008E323F /* WebImage */, ); path = Sources; sourceTree = SOURCE_ROOT; }; 68FFE57B27367754008E323F /* KJBannerView */ = { isa = PBXGroup; children = ( 68FFE58827367754008E323F /* KJBannerView.bundle */, 68FFE58527367754008E323F /* KJBannerView.h */, 68FFE57E27367754008E323F /* KJBannerView.m */, 68FFE57D27367754008E323F /* KJBannerViewCell.h */, 68FFE58627367754008E323F /* KJBannerViewCell.m */, 68FFE58027367754008E323F /* KJBannerViewFlowLayout.h */, 68FFE58327367754008E323F /* KJBannerViewFlowLayout.m */, 68FFE57C27367754008E323F /* KJBannerViewTimer.h */, 68FFE58227367754008E323F /* KJBannerViewTimer.m */, ); path = KJBannerView; sourceTree = ""; }; 68FFE58927367754008E323F /* WebImage */ = { isa = PBXGroup; children = ( 68FFE58D27367754008E323F /* KJAutoPurgingCache.h */, 68FFE59427367754008E323F /* KJAutoPurgingCache.m */, 68FFE5AA273686ED008E323F /* KJWebImageHeader.h */, 68FFE58C27367754008E323F /* KJImageCache.h */, 68FFE59227367754008E323F /* KJImageCache.m */, 68FFE58E27367754008E323F /* KJNetworkManager.h */, 68FFE59327367754008E323F /* KJNetworkManager.m */, 68FFE58F27367754008E323F /* KJWebImageDownloader.h */, 68FFE58B27367754008E323F /* KJWebImageDownloader.m */, 68FFE58A27367754008E323F /* UIView+KJWebImage.h */, 68FFE59127367754008E323F /* UIView+KJWebImage.m */, ); path = WebImage; sourceTree = ""; }; 68FFE59527367754008E323F /* PageControl */ = { isa = PBXGroup; children = ( 68FFE59627367754008E323F /* KJPageControl.h */, 68FFE59727367754008E323F /* KJPageControl.m */, ); path = PageControl; sourceTree = ""; }; 68FFE5A6273678C1008E323F /* Controller */ = { isa = PBXGroup; children = ( 686568CA26FB5EDC0001C691 /* DownloadViewController.h */, 686568C726FB5EDC0001C691 /* DownloadViewController.m */, 68E26B5421CE924700F91182 /* ViewController.h */, 68E26B5521CE924700F91182 /* ViewController.m */, ); path = Controller; sourceTree = ""; }; 68FFE5A7273678F8008E323F /* Model */ = { isa = PBXGroup; children = ( 684059DB21EA08D00092F630 /* KJBannerModel.h */, 684059DC21EA08D00092F630 /* KJBannerModel.m */, ); path = Model; sourceTree = ""; }; 68FFE5A8273678FF008E323F /* View */ = { isa = PBXGroup; children = ( 684059DE21EB59DA0092F630 /* KJCollectionViewCell.h */, 684059DF21EB59DA0092F630 /* KJCollectionViewCell.m */, ); path = View; sourceTree = ""; }; 68FFE5A927367908008E323F /* ViewModel */ = { isa = PBXGroup; children = ( 686568C826FB5EDC0001C691 /* KJViewModel.h */, 686568C926FB5EDC0001C691 /* KJViewModel.m */, ); path = ViewModel; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ 68E26B4D21CE924700F91182 /* KJBannerViewDemo */ = { isa = PBXNativeTarget; buildConfigurationList = 68E26B6421CE924800F91182 /* Build configuration list for PBXNativeTarget "KJBannerViewDemo" */; buildPhases = ( 68E26B4A21CE924700F91182 /* Sources */, 68E26B4B21CE924700F91182 /* Frameworks */, 68E26B4C21CE924700F91182 /* Resources */, ); buildRules = ( ); dependencies = ( ); name = KJBannerViewDemo; productName = KJBannerViewDemo; productReference = 68E26B4E21CE924700F91182 /* KJBannerViewDemo.app */; productType = "com.apple.product-type.application"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ 68E26B4621CE924700F91182 /* Project object */ = { isa = PBXProject; attributes = { LastUpgradeCheck = 1220; ORGANIZATIONNAME = "杨科军"; TargetAttributes = { 68E26B4D21CE924700F91182 = { CreatedOnToolsVersion = 10.1; }; }; }; buildConfigurationList = 68E26B4921CE924700F91182 /* Build configuration list for PBXProject "KJBannerViewDemo" */; compatibilityVersion = "Xcode 9.3"; developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, Base, ); mainGroup = 68E26B4521CE924700F91182; productRefGroup = 68E26B4F21CE924700F91182 /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( 68E26B4D21CE924700F91182 /* KJBannerViewDemo */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ 68E26B4C21CE924700F91182 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( 68FFE575273672AF008E323F /* KJBannerView.podspec in Resources */, 68E26B5E21CE924800F91182 /* LaunchScreen.storyboard in Resources */, 68E26B5B21CE924800F91182 /* Assets.xcassets in Resources */, 68E26B5921CE924700F91182 /* Main.storyboard in Resources */, 68FFE576273672AF008E323F /* CHANGELOG.md in Resources */, 68FFE59F27367754008E323F /* KJBannerView.bundle in Resources */, 68FFE5AC2736918F008E323F /* README.md in Resources */, 68BD69F82580712000D4B3AF /* IMG_0139.GIF in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ 68E26B4A21CE924700F91182 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 6868EC2426FB5D54001F2400 /* MASConstraintMaker.m in Sources */, 686568CB26FB5EDC0001C691 /* DownloadViewController.m in Sources */, 68E26B5621CE924700F91182 /* ViewController.m in Sources */, 68E26B6121CE924800F91182 /* main.m in Sources */, 686568CC26FB5EDC0001C691 /* KJViewModel.m in Sources */, 6868EC2326FB5D54001F2400 /* MASCompositeConstraint.m in Sources */, 6868EC2826FB5D54001F2400 /* MASConstraint.m in Sources */, 6868EC2726FB5D54001F2400 /* View+MASAdditions.m in Sources */, 68E26B5321CE924700F91182 /* AppDelegate.m in Sources */, 68FFE5A227367754008E323F /* KJImageCache.m in Sources */, 68FFE5A127367754008E323F /* UIView+KJWebImage.m in Sources */, 68FFE59927367754008E323F /* KJBannerView.m in Sources */, 6868EC2526FB5D54001F2400 /* MASLayoutConstraint.m in Sources */, 6868EC2926FB5D54001F2400 /* MASViewConstraint.m in Sources */, 68FFE5A027367754008E323F /* KJWebImageDownloader.m in Sources */, 684059DD21EA08D00092F630 /* KJBannerModel.m in Sources */, 68FFE5A327367754008E323F /* KJNetworkManager.m in Sources */, 6868EC2626FB5D54001F2400 /* NSArray+MASAdditions.m in Sources */, 68FFE5A427367754008E323F /* KJAutoPurgingCache.m in Sources */, 68FFE59E27367754008E323F /* KJBannerViewCell.m in Sources */, 68FFE5A527367754008E323F /* KJPageControl.m in Sources */, 6868EC2126FB5D54001F2400 /* NSLayoutConstraint+MASDebugAdditions.m in Sources */, 68FFE59D27367754008E323F /* KJBannerViewFlowLayout.m in Sources */, 6868EC2226FB5D54001F2400 /* ViewController+MASAdditions.m in Sources */, 684059E121EB59DA0092F630 /* KJCollectionViewCell.m in Sources */, 68FFE59C27367754008E323F /* KJBannerViewTimer.m in Sources */, 6868EC2A26FB5D54001F2400 /* MASViewAttribute.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXVariantGroup section */ 68E26B5721CE924700F91182 /* Main.storyboard */ = { isa = PBXVariantGroup; children = ( 68E26B5821CE924700F91182 /* Base */, ); name = Main.storyboard; sourceTree = ""; }; 68E26B5C21CE924800F91182 /* LaunchScreen.storyboard */ = { isa = PBXVariantGroup; children = ( 68E26B5D21CE924800F91182 /* Base */, ); name = LaunchScreen.storyboard; sourceTree = ""; }; /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ 68E26B6221CE924800F91182 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_IDENTITY = "iPhone Developer"; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 12.1; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; }; name = Debug; }; 68E26B6321CE924800F91182 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_IDENTITY = "iPhone Developer"; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 12.1; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; SDKROOT = iphoneos; VALIDATE_PRODUCT = YES; }; name = Release; }; 68E26B6521CE924800F91182 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = 66T6NB6T4J; INFOPLIST_FILE = "KJBannerViewDemo/Supporting Files/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); PRODUCT_BUNDLE_IDENTIFIER = com.yangkejun.KJBannerViewDemo; PRODUCT_NAME = "$(TARGET_NAME)"; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; }; 68E26B6621CE924800F91182 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = 66T6NB6T4J; INFOPLIST_FILE = "KJBannerViewDemo/Supporting Files/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); PRODUCT_BUNDLE_IDENTIFIER = com.yangkejun.KJBannerViewDemo; PRODUCT_NAME = "$(TARGET_NAME)"; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ 68E26B4921CE924700F91182 /* Build configuration list for PBXProject "KJBannerViewDemo" */ = { isa = XCConfigurationList; buildConfigurations = ( 68E26B6221CE924800F91182 /* Debug */, 68E26B6321CE924800F91182 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 68E26B6421CE924800F91182 /* Build configuration list for PBXNativeTarget "KJBannerViewDemo" */ = { isa = XCConfigurationList; buildConfigurations = ( 68E26B6521CE924800F91182 /* Debug */, 68E26B6621CE924800F91182 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; rootObject = 68E26B4621CE924700F91182 /* Project object */; } ================================================ FILE: KJBannerViewDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata ================================================ ================================================ FILE: KJBannerViewDemo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist ================================================ IDEDidComputeMac32BitWarning ================================================ FILE: KJBannerViewDemo.xcodeproj/xcshareddata/xcschemes/KJBannerViewDemo.xcscheme ================================================ ================================================ FILE: LICENSE ================================================ Copyright (c) 2018 yangkejun Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ # KJBannerView

### 功能介绍 KJBannerView 是一款轮播Banner,支持网络GIF和网络图片和本地图片混合轮播 - [x] 缩放无限自动循环滚动 √ - [x] 预加载渲染处理,缓存区缓存图片资源处理 √ - [x] 支持四种方向滚动,从左往右、从右往左、从上往下、从下往上 √ - [x] 自定义继承,定制不同样式,详情使用请见Demo √ - [x] 支持网络动态图和网络图片和本地图片混合轮播 √ - [x] 支持在Storyboard和Xib中创建并配置其属性 √ - [x] 提供多种分页控件PageControl显示 √ - [x] 自带缓存加载,内部封装网图下载缓存工具 √ - [x] 清理指定时间段以前的图片资源数据 √ ---- ### KJBannerView类介绍 | Class | 功能区 | | :--- | :--- | | KJBannerView | 轮播图主控件 | | KJPageView | 自定义分页控件 | | KJBannerViewCell | 基类,自定义需继承该Cell | | KJBannerViewFlowLayout | Cell缩放管理 | | KJBannerViewTimer | 计时器 | ### Downloader类介绍 | Class | 功能区 | | :--- | :--- | | KJAutoPurgingImageCache | 定时清理缓存工具 | | KJImageCache | 缓存工具 | | KJNetworkManager | 网络请求工具 | | KJWebImageDownloader | 网图下载工具 | | UIView+KJWebImage | 显示网络图片(支持UIImageView、UIButton、UIView) | #### 支持Xib快捷设置属性 ![Xib](https://upload-images.jianshu.io/upload_images/1933747-0c4b715868e47746.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/666) ### 效果图 ![轮播图](https://upload-images.jianshu.io/upload_images/1933747-2e51515ae91af6d4.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/666) - 下载测试效果图,图片采用信号量方式获取 ![IMG_0145.PNG](https://upload-images.jianshu.io/upload_images/1933747-ea228edad91a2dcd.PNG?imageMogr2/auto-orient/strip%7CimageView2/2/w/666) ### CocoaPods Install ``` pod 'KJBannerView' # 轮播图 pod 'KJBannerView/Downloader' # 网络加载板块 ``` ### 关于作者 - 🎷 **邮箱地址:[ykj310@126.com](ykj310@126.com) 🎷** - 🎸 **GitHub地址:[yangKJ](https://github.com/yangKJ) 🎸** - 🎺 **掘金地址:[茶底世界之下](https://juejin.cn/user/1987535102554472/posts) 🎺** - 🚴🏻 **简书地址:[77___](https://www.jianshu.com/u/c84c00476ab6) 🚴🏻** ---- > **觉得有帮助的老哥们,请帮忙点个星 ⭐..** **救救孩子吧,谢谢各位老板。** 🥺 ----- ================================================ FILE: Sources/KJBannerHeader.h ================================================ // // KJBannerHeader.h // KJBannerViewDemo // // Created by 杨科军 on 2018/12/23. // Copyright © 2018 杨科军. All rights reserved. // https://github.com/yangKJ/KJBannerViewDemo #ifndef KJBannerHeader_h #define KJBannerHeader_h #import "KJBannerView.h" // 轮播Banner #import "KJPageControl.h" // 分页控件 #import "KJBannerViewTimer.h" // GCD计时器 // 显示网络图片(目前支持设置UIImageView,UIButton,UIView三种) #if __has_include() #import #elif __has_include("KJWebImageHeader.h") #import "KJWebImageHeader.h" #else #endif #endif /* KJBannerHeader_h */ ================================================ FILE: Sources/KJBannerView/KJBannerView.h ================================================ // // KJBannerView.h // KJBannerView // // Created by 杨科军 on 2018/2/27. // Copyright © 2018年 杨科军. All rights reserved. // https://github.com/yangKJ/KJBannerViewDemo // 轮播图 #import #import "KJPageControl.h" #import "KJBannerViewCell.h" NS_ASSUME_NONNULL_BEGIN /// 滚动方法 typedef NS_ENUM(NSInteger, KJBannerViewRollDirectionType) { KJBannerViewRollDirectionTypeRightToLeft, /// 默认,从右往左 KJBannerViewRollDirectionTypeLeftToRight, /// 从左往右 KJBannerViewRollDirectionTypeBottomToTop, /// 从下往上 KJBannerViewRollDirectionTypeTopToBottom, /// 从上往下 }; @class KJBannerViewFlowLayout; @protocol KJBannerViewDelegate,KJBannerViewDataSource; IB_DESIGNABLE @interface KJBannerView : UIView /// 代理方法 @property (nonatomic,weak) id delegate; @property (nonatomic,weak) id dataSource; //************************ API ************************ /// 是否缩放,默认no @property (nonatomic,assign) IBInspectable BOOL isZoom; /// 是否无线循环,默认yes @property (nonatomic,assign) IBInspectable BOOL infiniteLoop; /// 是否自动滑动,默认yes @property (nonatomic,assign) IBInspectable BOOL autoScroll; /// 是否显示分页控件,默认yes @property (nonatomic,assign) IBInspectable BOOL showPageControl; /// 自动滚动间隔时间,默认2s @property (nonatomic,assign) IBInspectable CGFloat autoTime; /// Cell宽度,默认控件宽度 @property (nonatomic,assign) IBInspectable CGFloat itemWidth; /// Cell间距,默认0px @property (nonatomic,assign) IBInspectable CGFloat itemSpace; /// 占位图,用于没有数据时刻占位显示 @property (nonatomic,strong) UIImage *placeholderImage; /// 滚动方向,默认从右到左 @property (nonatomic,assign) KJBannerViewRollDirectionType rollType; /// 当前位置 @property (nonatomic,assign,readonly) NSInteger currentIndex; /// 分页控制器 @property (nonatomic,strong,readonly) KJPageControl *pageControl; /// 布局信息 @property (nonatomic,strong,readonly) KJBannerViewFlowLayout *layout; /// 缓存已加载的图片资源 @property (nonatomic,strong,readonly) NSMutableDictionary *cacheImages; /// 注册一个类,用于创建 `UICollectionViewCell` /// @param clazz 类 /// @param identifier 标识符 - (void)registerClass:(Class)clazz forCellWithReuseIdentifier:(NSString *)identifier; /// 注册一个类,用于创建`UICollectionViewCell` /// @param nib UINib /// @param identifier 标识符 - (void)registerNib:(UINib *)nib forCellWithReuseIdentifier:(NSString *)identifier; /// 创建 `UICollectionViewCell` /// @param identifier 标识符 /// @param index 下标 - (__kindof KJBannerViewCell *)dequeueReusableCellWithReuseIdentifier:(NSString *)identifier forIndex:(NSInteger)index; /// 刷新 - (void)reloadData; /// 滚动到指定位置 /// @param index 指定位置 /// @param animated 是否执行动画 - (void)scrollToItemAtIndex:(NSInteger)index animated:(BOOL)animated; /// 暂停计时器滚动处理,备注:在viewDidDisappear当中实现 - (void)kj_pauseTimer; /// 继续计时器滚动,备注:在viewDidAppear当中实现 - (void)kj_repauseTimer; /// 使用Masonry自动布局,请在设置布局之后调用该方法 - (void)kj_useMasonry; @end /// 委托协议代理 @protocol KJBannerViewDataSource /// 数据源 /// @param banner 轮播图 /// @return 返回轮播图数据源 - (NSInteger)kj_numberOfItemsInBannerView:(KJBannerView *)banner; /// 定制专有样式 /// @param banner 轮播图 /// @param index 索引 /// @return 返回定制样式Cell - (__kindof KJBannerViewCell *)kj_bannerView:(KJBannerView *)banner cellForItemAtIndex:(NSInteger)index; @end @protocol KJBannerViewDelegate @optional /// 点击图片响应 /// @param banner 轮播图 /// @param index 索引 - (void)kj_bannerView:(KJBannerView *)banner didSelectItemAtIndex:(NSInteger)index; /// 轮播滚动时刻响应 /// @param banner 轮播图 /// @param index 索引 - (void)kj_bannerView:(KJBannerView *)banner loopScrolledItemAtIndex:(NSInteger)index; /// 滚动调用 /// @param banner 轮播图 - (void)kj_bannerViewDidScroll:(KJBannerView *)banner; @end NS_ASSUME_NONNULL_END ================================================ FILE: Sources/KJBannerView/KJBannerView.m ================================================ // // KJBannerView.m // KJBannerView // // Created by 杨科军 on 2018/2/27. // Copyright © 2018年 杨科军. All rights reserved. // https://github.com/yangKJ/KJBannerViewDemo #import "KJBannerView.h" #import "KJBannerViewFlowLayout.h" #import "KJBannerViewTimer.h" #define kPageHeight (20) @interface KJBannerView() @property (nonatomic,strong) UICollectionView *collectionView; @property (nonatomic,strong) KJBannerViewFlowLayout *layout; @property (nonatomic,strong) KJPageControl *pageControl; @property (nonatomic,strong) CALayer *topLayer; @property (nonatomic,assign) CGFloat height; @property (nonatomic,assign) CGFloat lastX,lastY; @property (nonatomic,strong) KJBannerViewTimer *timer; @property (nonatomic,assign) NSInteger shamItemCount;// 虚假个数,无穷大看着像无限轮播 @property (nonatomic,assign) NSInteger numberOfItems;// 真实个数 @end @implementation KJBannerView - (void)dealloc{ [self.timer kj_invalidateTimer]; } - (instancetype)initWithFrame:(CGRect)frame{ if (self = [super initWithFrame:frame]) { [self setupInit]; [self setupUI]; } return self; } - (instancetype)initWithCoder:(NSCoder *)aDecoder{ if (self = [super initWithCoder:aDecoder]) { [self setupInit]; [self setupUI]; } return self; } - (void)willMoveToSuperview:(UIView *)newSuperview{ [super willMoveToSuperview:newSuperview]; [self.timer kj_pauseTimer]; } - (void)setupInit{ _itemWidth = self.bounds.size.width; _height = self.bounds.size.height; _itemSpace = 0; _autoTime = 2; _infiniteLoop = YES; _autoScroll = YES; _showPageControl = YES; _rollType = KJBannerViewRollDirectionTypeRightToLeft; _placeholderImage = [UIImage imageNamed:@"KJBannerView.bundle/KJBannerPlaceholderImage.png"]; _cacheImages = [NSMutableDictionary dictionary]; } - (void)setupUI{ [self addSubview:self.collectionView]; [self addSubview:self.pageControl]; [self.layer addSublayer:self.topLayer]; } #pragma mark - public method /// 注册一个类,用于创建 `UICollectionViewCell` /// @param clazz 类 /// @param identifier 标识符 - (void)registerClass:(Class)clazz forCellWithReuseIdentifier:(NSString *)identifier{ [self.collectionView registerClass:clazz forCellWithReuseIdentifier:identifier]; } /// 注册一个类,用于创建 `UICollectionViewCell` /// @param nib UINib /// @param identifier 标识符 - (void)registerNib:(UINib *)nib forCellWithReuseIdentifier:(NSString *)identifier{ [self.collectionView registerNib:nib forCellWithReuseIdentifier:identifier]; } /// 创建 `UICollectionViewCell` /// @param identifier 标识符 /// @param index 下标 - (__kindof KJBannerViewCell *)dequeueReusableCellWithReuseIdentifier:(NSString *)identifier forIndex:(NSInteger)index{ NSIndexPath * indexPath = [NSIndexPath indexPathForItem:index inSection:0]; id cell = [self.collectionView dequeueReusableCellWithReuseIdentifier:identifier forIndexPath:indexPath]; if (![cell isKindOfClass:[KJBannerViewCell class]]) { NSAssert(YES, @"Cell class must be subclass of KJBannerViewCell"); } return (__kindof KJBannerViewCell *)cell; } /// 刷新 - (void)reloadData{ if ([self.dataSource respondsToSelector:@selector(kj_numberOfItemsInBannerView:)]) { self.numberOfItems = [self.dataSource kj_numberOfItemsInBannerView:self]; } } /// 滚动到指定位置 /// @param index 指定位置 /// @param animated 是否执行动画 - (void)scrollToItemAtIndex:(NSInteger)index animated:(BOOL)animated{ [self kj_scrollToIndex:index autoScroll:NO animated:animated]; } /// 暂停计时器滚动处理 - (void)kj_pauseTimer{ [self.timer kj_pauseTimer]; } /// 继续计时器滚动 - (void)kj_repauseTimer{ [self.timer kj_startTimer]; } /// 使用Masonry自动布局,请在设置布局之后调用该方法 - (void)kj_useMasonry{ [self layoutIfNeeded]; if (self.bounds.size.height <= 0) return; self.height = self.bounds.size.height; self.collectionView.frame = self.bounds; self.layout.itemSize = CGSizeMake(_itemWidth, self.height); self.pageControl.frame = CGRectMake(0, self.height-kPageHeight, self.bounds.size.width, kPageHeight); [self.topLayer setBounds:self.bounds]; [self.topLayer setPosition:CGPointMake(self.bounds.size.width*.5, self.bounds.size.height*.5)]; } #pragma mark - setter/getter - (void)setIsZoom:(BOOL)isZoom{ _isZoom = isZoom; self.layout.isZoom = isZoom; } - (void)setAutoScroll:(BOOL)autoScroll{ _autoScroll = autoScroll; if (autoScroll) { if (self.timer.pausing) { [self.timer kj_startTimer]; } } else { [self.timer kj_invalidateTimer]; } } - (void)setShowPageControl:(BOOL)showPageControl{ _showPageControl = showPageControl; self.pageControl.hidden = !showPageControl; } - (void)setAutoTime:(CGFloat)autoTime{ if (_autoTime != autoTime) { _autoTime = autoTime; [self.timer kj_invalidateTimer]; _timer = nil; if (self.timer && self.autoScroll) { [self.timer kj_startTimer]; } } } - (void)setItemWidth:(CGFloat)itemWidth{ _itemWidth = itemWidth; self.layout.itemSize = CGSizeMake(itemWidth, self.height); } - (void)setItemSpace:(CGFloat)itemSpace{ _itemSpace = itemSpace; self.layout.minimumLineSpacing = itemSpace; } - (void)setRollType:(KJBannerViewRollDirectionType)rollType{ _rollType = rollType; if (rollType == KJBannerViewRollDirectionTypeRightToLeft || rollType == KJBannerViewRollDirectionTypeLeftToRight) { self.layout.scrollDirection = UICollectionViewScrollDirectionHorizontal; } else { self.layout.scrollDirection = UICollectionViewScrollDirectionVertical; } } - (void)setNumberOfItems:(NSInteger)numberOfItems{ _numberOfItems = numberOfItems; if (numberOfItems == 0) { self.shamItemCount = 0; self.topLayer.hidden = NO; self.pageControl.hidden = YES; self.collectionView.hidden = YES; [self.timer kj_pauseTimer]; return; } self.topLayer.hidden = YES; self.collectionView.hidden = NO; if (numberOfItems == 1) { self.shamItemCount = 9; self.pageControl.hidden = YES; self.collectionView.scrollEnabled = NO; [self.timer kj_pauseTimer]; } else { if (CGRectEqualToRect(self.pageControl.frame, CGRectZero)) { [self kj_useMasonry]; } self.shamItemCount = self.infiniteLoop ? 10000 * numberOfItems : numberOfItems; self.pageControl.hidden = !self.showPageControl; self.pageControl.totalPages = numberOfItems; self.collectionView.scrollEnabled = YES; [self setAutoScroll:self.autoScroll]; } [self.collectionView reloadData]; NSInteger index = self.infiniteLoop ? (int)(self.shamItemCount / 2) : 0; [self kj_scrollToIndex:index autoScroll:self.autoScroll animated:YES]; } /// 当前位置 - (NSInteger)currentIndex{ NSInteger index = 0; if (_layout.scrollDirection == UICollectionViewScrollDirectionHorizontal) { float wT = (_itemWidth + _itemSpace); index = (self.collectionView.contentOffset.x + wT * 0.5) / wT; } else { float wT = self.height; index = (self.collectionView.contentOffset.y + wT * 0.5) / wT; } return MAX(0, index); } #pragma mark - private method /// 自动滚动 - (void)kj_automaticScroll{ if(_numberOfItems == 0) return; NSInteger index = [self currentIndex]; switch (_rollType) { case KJBannerViewRollDirectionTypeRightToLeft: case KJBannerViewRollDirectionTypeBottomToTop: if (index == self.shamItemCount - 10) index = 10; index++; break; case KJBannerViewRollDirectionTypeLeftToRight: case KJBannerViewRollDirectionTypeTopToBottom: if (index == 10) index = self.shamItemCount - 10; index--; break; default:break; } [self kj_scrollToIndex:index autoScroll:YES animated:YES]; } /// 滚动到指定位置 - (void)kj_scrollToIndex:(NSInteger)index autoScroll:(BOOL)autoScroll animated:(BOOL)animated{ if (_numberOfItems == 0) { return; } NSInteger idx = index % _numberOfItems; if (autoScroll) { self.pageControl.currentIndex = idx; } else if (self.pageControl.hidden == NO){ [self.pageControl scrollToIndex:idx]; } UICollectionViewScrollPosition position = UICollectionViewScrollPositionCenteredVertically; if (_layout.scrollDirection == UICollectionViewScrollDirectionHorizontal) { position = UICollectionViewScrollPositionCenteredHorizontally; } [self.collectionView scrollToItemAtIndexPath:[NSIndexPath indexPathForRow:index inSection:0] atScrollPosition:position animated:animated]; if ([self.delegate respondsToSelector:@selector(kj_bannerView:loopScrolledItemAtIndex:)]) { [self.delegate kj_bannerView:self loopScrolledItemAtIndex:idx]; } } #pragma mark - UIScrollViewDelegate - (void)scrollViewDidScroll:(UIScrollView *)scrollView{ if (_numberOfItems == 0) return; if ([self.delegate respondsToSelector:@selector(kj_bannerViewDidScroll:)]) { [self.delegate kj_bannerViewDidScroll:self]; } } - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView{ _lastX = scrollView.contentOffset.x; _lastY = scrollView.contentOffset.y; if (self.autoScroll) { [self.timer kj_pauseTimer]; } } - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate{ if (self.autoScroll) { dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(self.autoTime * NSEC_PER_SEC)); dispatch_after(time, dispatch_get_main_queue(), ^{ [self.timer kj_startTimer]; }); } } - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView{ [self scrollViewDidEndScrollingAnimation:self.collectionView]; } - (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView{ self.collectionView.userInteractionEnabled = YES; } /// 手离开屏幕的时候 - (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset{ self.collectionView.userInteractionEnabled = NO; NSInteger idx = 0,dragIndex = 0; if (_layout.scrollDirection == UICollectionViewScrollDirectionHorizontal) { CGFloat move = scrollView.contentOffset.x - self.lastX; NSInteger page = (int)(move / (self.itemWidth * .5)); if (velocity.x > 0 || page > 0) { dragIndex = 1; } else if (velocity.x < 0 || page < 0) { dragIndex = -1; } idx = (_lastX + (self.itemWidth + self.itemSpace) * 0.5) / (self.itemSpace + self.itemWidth); } else { CGFloat move = scrollView.contentOffset.y - self.lastY; NSInteger page = move / (self.height*.5); if (velocity.y > 0 || page > 0) { dragIndex = 1; } else if (velocity.y < 0 || page < 0) { dragIndex = -1; } idx = (_lastY + (self.height) * 0.5) / (self.height); } [self kj_scrollToIndex:idx + dragIndex autoScroll:NO animated:YES]; } /// 松开手指滑动开始减速的时候设置滑动动画 - (void)scrollViewWillBeginDecelerating:(UIScrollView *)scrollView{ // NSInteger idx = 0; // if (_layout.scrollDirection == UICollectionViewScrollDirectionHorizontal) { // idx = (_lastX + (self.itemWidth + self.itemSpace) * 0.5) / (self.itemSpace + self.itemWidth); // } else { // idx = (_lastY + (self.height) * 0.5) / (self.height); // } // [self kj_scrollToIndex:idx automaticScroll:NO]; } #pragma mark - UICollectionViewDataSource - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{ return self.shamItemCount; } - (__kindof UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{ if (_numberOfItems == 0) { return [collectionView dequeueReusableCellWithReuseIdentifier:@"banner_identifier" forIndexPath:indexPath]; } NSInteger itemIndex = indexPath.item % _numberOfItems; KJBannerViewCell *cell = [self.dataSource kj_bannerView:self cellForItemAtIndex:itemIndex]; [cell setValue:self forKey:@"bannerView"]; return cell; } #pragma mark - UICollectionViewDelegate - (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath{ [collectionView deselectItemAtIndexPath:indexPath animated:YES]; if ([self.delegate respondsToSelector:@selector(kj_bannerView:didSelectItemAtIndex:)]) { NSInteger idx = [self currentIndex] % _numberOfItems; [self.delegate kj_bannerView:self didSelectItemAtIndex:idx]; } } #pragma mark - lazy - (CALayer *)topLayer{ if (!_topLayer) { _topLayer = [[CALayer alloc] init]; [_topLayer setBounds:self.bounds]; [_topLayer setPosition:CGPointMake(self.bounds.size.width*.5, self.bounds.size.height*.5)]; [_topLayer setContents:(id)self.placeholderImage.CGImage]; _topLayer.zPosition = 1; } return _topLayer; } - (KJBannerViewFlowLayout *)layout{ if (!_layout) { _layout = [[KJBannerViewFlowLayout alloc] init]; _layout.minimumLineSpacing = 0; _layout.itemSize = CGSizeMake(_itemWidth, self.frame.size.height); _layout.scrollDirection = UICollectionViewScrollDirectionHorizontal; } return _layout; } - (UICollectionView *)collectionView{ if (!_collectionView) { _collectionView = [[UICollectionView alloc] initWithFrame:self.bounds collectionViewLayout:self.layout]; _collectionView.delegate = self; _collectionView.dataSource = self; _collectionView.scrollsToTop = NO; _collectionView.showsVerticalScrollIndicator = NO; _collectionView.showsHorizontalScrollIndicator = NO; _collectionView.backgroundColor = self.backgroundColor; [_collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:@"banner_identifier"]; } return _collectionView; } - (KJPageControl *)pageControl{ if (!_pageControl) { _pageControl = [[KJPageControl alloc] init]; _pageControl.hidden = YES; } return _pageControl; } - (KJBannerViewTimer *)timer{ if (!_timer) { __weak __typeof(self) weakself = self; _timer = [[KJBannerViewTimer alloc] initWithInterval:self.autoTime repeats:YES task:^{ [weakself kj_automaticScroll]; }]; } return _timer; } @end ================================================ FILE: Sources/KJBannerView/KJBannerViewCell.h ================================================ // // KJBannerViewCell.h // KJBannerView // // Created by 杨科军 on 2018/2/27. // Copyright © 2018年 杨科军. All rights reserved. // https://github.com/yangKJ/KJBannerViewDemo #import @interface KJBannerViewCell : UICollectionViewCell /// 如果背景不是纯色并且需要切圆角,请设置为yes @property (nonatomic,assign) BOOL bannerNoPureBack; /// 是否裁剪,默认NO @property (nonatomic,assign) BOOL bannerScale; /// 切圆角,默认为0px @property (nonatomic,assign) CGFloat bannerRadius; /// 轮播图片的ContentMode,默认为 UIViewContentModeScaleToFill @property (nonatomic,assign) UIViewContentMode bannerContentMode; /// 定制特定方位圆角,默认四个位置 @property (nonatomic,assign) UIRectCorner bannerCornerRadius; /// 圆角背景颜色,默认KJBannerView背景色 @property (nonatomic,strong) UIColor *bannerRadiusColor; /// 图片显示控件 @property (nonatomic, strong, readonly) UIImageView *bannerImageView; /// 🎷 是否使用本库提供的图片加载,支持动态GIF网图混合使用 /// 经过预渲染和暂存在缓存区处理,性能方面更优 /// 前提条件,必须引入网络加载模块 pod 'KJBannerView/Downloader' /// @param imageURLString 图片链接地址,支持动态GIF和网图、本地图等等 /// @param mineLoadImage 是否使用本库提供的图片加载 - (void)setupImageURLString:(NSString *)imageURLString mineLoadImage:(BOOL)mineLoadImage; - (void)setupImageURLString:(NSString *)imageURLString placeholderImage:(UIImage *)placeholderImage mineLoadImage:(BOOL)mineLoadImage; @end ================================================ FILE: Sources/KJBannerView/KJBannerViewCell.m ================================================ // // KJBannerViewCell.m // KJBannerView // // Created by 杨科军 on 2018/2/27. // Copyright © 2018年 杨科军. All rights reserved. // https://github.com/yangKJ/KJBannerViewDemo #import "KJBannerViewCell.h" #import "KJBannerView.h" #if __has_include("KJWebImageHeader.h") #import "KJWebImageHeader.h" #endif @interface KJBannerViewCell (){ char _divisor; } @property (nonatomic,strong) KJBannerView *bannerView; @property (nonatomic,strong) UIImageView *bannerImageView; @property (nonatomic,strong) UIImage *placeholderImage; @property (nonatomic,strong) NSString *imageURLString; @end @implementation KJBannerViewCell - (void)setupInit{ _divisor = 0b00000000; self.bannerNoPureBack = YES; self.bannerContentMode = UIViewContentModeScaleToFill; self.bannerCornerRadius = UIRectCornerAllCorners; self.bannerRadiusColor = self.superview.backgroundColor; } - (instancetype)initWithFrame:(CGRect)frame{ if (self = [super initWithFrame:frame]) { self.layer.contentsScale = [UIScreen mainScreen].scale; self.layer.drawsAsynchronously = YES; [self setupInit]; } return self; } /// 🎷 是否使用本库提供的图片加载,支持动态GIF网图混合使用 /// 经过预渲染和暂存在缓存区处理,性能方面更优 /// 前提条件,必须引入网络加载模块 pod 'KJBannerView/Downloader' /// @param imageURLString 图片链接地址,支持动态GIF和网图、本地图等等 /// @param mineLoadImage 是否使用本库提供的图片加载 - (void)setupImageURLString:(NSString *)imageURLString mineLoadImage:(BOOL)mineLoadImage{ UIImage *image = [UIImage imageNamed:@"KJBannerView.bundle/KJBannerPlaceholderImage.png"]; [self setupImageURLString:imageURLString placeholderImage:image mineLoadImage:mineLoadImage]; } - (void)setupImageURLString:(NSString *)imageURLString placeholderImage:(UIImage *)placeholderImage mineLoadImage:(BOOL)mineLoadImage{ if (imageURLString == nil || imageURLString.length == 0) { return; } self.imageURLString = imageURLString; self.placeholderImage = placeholderImage; if (self.bannerImageView.image == nil) { self.bannerImageView.image = self.placeholderImage; } if (mineLoadImage) { [self drawBannerImageWithURLString:imageURLString]; } } /// 绘制图片 - (void)drawBannerImageWithURLString:(NSString *)urlString{ UIImage *cacheImage = [self.bannerView.cacheImages valueForKey:urlString]; if (cacheImage) { self.bannerImageView.image = cacheImage; return; } // 本地图 if (kBannerImageURLStringLocality(urlString)) { NSData * data = kBannerLocalityGIFData(urlString); if (data) { __weak __typeof(self) weakself = self; kBannerAsyncPlayGIFImage(data, ^(UIImage * _Nonnull image) { weakself.bannerImageView.image = image; [weakself.bannerView.cacheImages setValue:image forKey:urlString]; }); } else { UIImage *image = [UIImage imageNamed:urlString]; if (image) { self.bannerImageView.image = image; [self.bannerView.cacheImages setValue:image forKey:urlString]; } } return; } // 停止时刻加载网络图片 [self performSelector:@selector(kj_bannerImageView) withObject:nil afterDelay:0.0 inModes:@[NSDefaultRunLoopMode]]; } /// 下载图片,并渲染到Cell上显示 - (void)kj_bannerImageView{ #if __has_include("KJWebImageHeader.h") __weak __typeof(self) weakself = self; NSURL * imageURL = [NSURL URLWithString:self.imageURLString]; [self.bannerImageView kj_setImageWithURL:imageURL provider:^(id delegate) { delegate.webPlaceholder = weakself.placeholderImage; delegate.webCropScale = weakself.bannerScale; __strong __typeof(self) strongself = weakself; delegate.webCompleted = ^(KJWebImageType imageType, UIImage *image, NSData *data, NSError *error) { if (image) { [strongself.bannerView.cacheImages setValue:image forKey:strongself.imageURLString]; } }; }]; #endif } #pragma mark - private method /// 判断是网络图片还是本地 NS_INLINE bool kBannerImageURLStringLocality(NSString * _Nonnull urlString){ return ([urlString hasPrefix:@"http://"] || [urlString hasPrefix:@"https://"]) ? false : true; } /// 获取本地GIF资源 NS_INLINE NSData * kBannerLocalityGIFData(NSString * string){ NSString *name = [[NSBundle mainBundle] pathForResource:string ofType:@"gif"]; NSData *data = [NSData dataWithContentsOfFile:name]; if (data == nil) { name = [[NSBundle mainBundle] pathForResource:string ofType:@"GIF"]; data = [NSData dataWithContentsOfFile:name]; } return data; } /// 异步播放动态图 /// @param data 数据源 /// @param callback 播放图片回调 NS_INLINE void kBannerAsyncPlayGIFImage(NSData * data, void(^callback)(UIImage *)){ if (callback == nil || data == nil) return; [[[NSOperationQueue alloc] init] addOperationWithBlock:^{ CGImageSourceRef imageSource = CGImageSourceCreateWithData(CFBridgingRetain(data), nil); size_t imageCount = CGImageSourceGetCount(imageSource); UIImage *image; if (imageCount <= 1) { image = [UIImage imageWithData:data]; } else { NSMutableArray *scaleImages = [NSMutableArray arrayWithCapacity:imageCount]; NSTimeInterval time = 0; for (int i = 0; i < imageCount; i++) { CGImageRef cgImage = CGImageSourceCreateImageAtIndex(imageSource, i, nil); UIImage *originalImage = [UIImage imageWithCGImage:cgImage]; [scaleImages addObject:originalImage]; CGImageRelease(cgImage); CFDictionaryRef const properties = CGImageSourceCopyPropertiesAtIndex(imageSource, i, NULL); CFDictionaryRef const GIFPros = CFDictionaryGetValue(properties, kCGImagePropertyGIFDictionary); NSNumber *duration = (__bridge id)CFDictionaryGetValue(GIFPros, kCGImagePropertyGIFUnclampedDelayTime); if (duration == NULL || [duration doubleValue] == 0) { duration = (__bridge id)CFDictionaryGetValue(GIFPros, kCGImagePropertyGIFDelayTime); } CFRelease(properties); time += duration.doubleValue; } image = [UIImage animatedImageWithImages:scaleImages duration:time]; } [[NSOperationQueue mainQueue] addOperationWithBlock:^{ callback(image); }]; CFRelease(imageSource); }]; } /// 异步绘制圆角, /// 原理就是绘制一个镂空图片盖在上面,所以这种只适用于纯色背景 /// @param radius 圆角半径 /// @param callback 蒙版图片回调 /// @param corners 圆角位置,支持特定方位圆角处理 /// @param view 需要覆盖视图 NS_INLINE void kBannerAsyncCornerRadius(CGFloat radius, void(^callback)(UIImage * image), UIRectCorner corners, UIView * view){ if (callback == nil) return; UIColor * backgroundColor = UIColor.whiteColor; if (view.backgroundColor) { backgroundColor = view.backgroundColor; } else if (view.superview.backgroundColor) { backgroundColor = view.superview.backgroundColor; } CGRect bounds = view.bounds; CGFloat scale = [UIScreen mainScreen].scale; [[[NSOperationQueue alloc] init] addOperationWithBlock:^{ UIGraphicsBeginImageContextWithOptions(bounds.size, NO, scale); CGContextRef context = UIGraphicsGetCurrentContext(); UIBezierPath *path = [UIBezierPath bezierPathWithRect:bounds]; UIBezierPath *radiusPath = [UIBezierPath bezierPathWithRoundedRect:bounds byRoundingCorners:corners cornerRadii:CGSizeMake(radius, radius)]; UIBezierPath *cornerPath = [radiusPath bezierPathByReversingPath]; [path appendPath:cornerPath]; CGContextAddPath(context, path.CGPath); [backgroundColor set]; CGContextFillPath(context); UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); [[NSOperationQueue mainQueue] addOperationWithBlock:^{ callback(image); }]; }]; } #pragma mark - setter/getter - (BOOL)bannerScale{ return !!(_divisor & 1); } - (void)setBannerScale:(BOOL)bannerScale{ if (bannerScale) { _divisor |= 1; } else { _divisor &= 0; } } - (BOOL)bannerNoPureBack{ return !!(_divisor & 2); } - (void)setBannerNoPureBack:(BOOL)bannerNoPureBack{ if (bannerNoPureBack) { _divisor |= (1<<1); } else { _divisor &= ~(1<<1); } } #pragma mark - lazy - (UIImageView *)bannerImageView{ if (_bannerImageView == nil) { _bannerImageView = [[UIImageView alloc] initWithFrame:self.bounds]; _bannerImageView.contentMode = self.bannerContentMode; _bannerImageView.image = self.placeholderImage; [self addSubview:_bannerImageView]; if (self.bannerRadius > 0) { CAShapeLayer *shapeLayer = [[CAShapeLayer alloc] init]; shapeLayer.frame = self.bounds; [_bannerImageView.layer addSublayer:shapeLayer]; if (self.bannerNoPureBack) { UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:self.bounds cornerRadius:self.bannerRadius]; shapeLayer.path = path.CGPath; _bannerImageView.layer.mask = shapeLayer; } else { _bannerImageView.clipsToBounds = YES; kBannerAsyncCornerRadius(self.bannerRadius, ^(UIImage * image) { shapeLayer.contents = (id)image.CGImage; }, self.bannerCornerRadius, _bannerImageView); } } } return _bannerImageView; } @end ================================================ FILE: Sources/KJBannerView/KJBannerViewFlowLayout.h ================================================ // // KJBannerViewFlowLayout.h // KJBannerView // // Created by 杨科军 on 2018/2/28. // Copyright © 2018年 杨科军. All rights reserved. // https://github.com/yangKJ/KJBannerViewDemo #import @interface KJBannerViewFlowLayout : UICollectionViewFlowLayout /// 是否缩放 @property (nonatomic,assign) BOOL isZoom; @end ================================================ FILE: Sources/KJBannerView/KJBannerViewFlowLayout.m ================================================ // // KJBannerViewFlowLayout.m // KJBannerView // // Created by 杨科军 on 2018/2/28. // Copyright © 2018年 杨科军. All rights reserved. // https://github.com/yangKJ/KJBannerViewDemo #import "KJBannerViewFlowLayout.h" #import @implementation KJBannerViewFlowLayout /* 多次调用 只要滑出范围就会 调用 * 当CollectionView的显示范围发生改变的时候,是否重新发生布局 * 一旦重新刷新 布局,就会重新调用 * 1.layoutAttributesForElementsInRect:方法 * 2.preparelayout方法 */ - (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds{ return YES; } /// 在CollectionView的第一次布局的时候被调用 - (void)prepareLayout{ [super prepareLayout]; // if (self.collectionView.superview) { // if ([self.collectionView.superview valueForKey:@"autoScroll"]) { // [self.collectionView.superview setValue:@(YES) forKey:@"autoScroll"]; // } // } } /* 重写方法 * 此方法会计算并返回每个item的位置和大小,换句话说就是collectionView里面的布局是怎样布局的就根这个方法的返回值有关 * 此方法会先计算一定数量的数据,当你继续滚动需要一些新数据时会再次来计算新的数据 * 只要在这里有过返回的数据都会缓存起来,下次再滚回来不会再帮你计算新数据 * 1.返回rect中的所有的元素的布局属性 * 2.返回的是包含UICollectionViewLayoutAttributes的NSArray * 3.UICollectionViewLayoutAttributes可以是cell,追加视图或装饰视图的信息 */ - (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect{ NSArray *temps = [[NSArray alloc] initWithArray:[super layoutAttributesForElementsInRect:rect] copyItems:YES]; if(self.isZoom == NO) return temps; CGFloat centerX = self.collectionView.contentOffset.x + self.collectionView.bounds.size.width * 0.5; for (UICollectionViewLayoutAttributes *attributes in temps) { CGFloat centerDistance = ABS(attributes.center.x - centerX); CGFloat scale = 1.0 / (1 + centerDistance * 0.001); attributes.transform = CGAffineTransformMakeScale(scale, scale); } return temps; } @end ================================================ FILE: Sources/KJBannerView/KJBannerViewTimer.h ================================================ // // KJBannerViewTimer.h // KJBannerViewDemo // // Created by 杨科军 on 2020/12/7. // Copyright © 2020 杨科军. All rights reserved. // https://github.com/yangKJ/KJBannerViewDemo // 计时器 #import NS_ASSUME_NONNULL_BEGIN @interface KJBannerViewTimer : NSObject - (instancetype)init UNAVAILABLE_ATTRIBUTE; + (instancetype)new UNAVAILABLE_ATTRIBUTE; /// 是否处于暂停中 @property (nonatomic, assign, readonly) BOOL pausing; /// 创建异步子线程计时器 /// @param inerval 间隔时间 /// @param repeats 是否重复 /// @param task 事件处理 - (instancetype)initWithInterval:(NSTimeInterval)inerval repeats:(BOOL)repeats task:(void(^)(void))task; /// 开启计时器,会延时 `inerval` 间隔秒执行 - (void)kj_startTimer; /// 暂停计时器 - (void)kj_pauseTimer; /// 继续计时器 - (void)kj_resumeTimer; /// 释放计时器 - (void)kj_invalidateTimer; /// 立刻执行 - (void)kj_immediatelyTimer; @end NS_ASSUME_NONNULL_END ================================================ FILE: Sources/KJBannerView/KJBannerViewTimer.m ================================================ // // KJBannerViewTimer.m // KJBannerViewDemo // // Created by 杨科军 on 2020/12/7. // Copyright © 2020 杨科军. All rights reserved. // https://github.com/yangKJ/KJBannerViewDemo #import "KJBannerViewTimer.h" @interface KJBannerViewTimer () @property (nonatomic,assign) BOOL pausing; @property (nonatomic,strong) NSTimer *timer; @property (nonatomic,assign) NSTimeInterval interval; @end @implementation KJBannerViewTimer - (void)dealloc{ [self kj_invalidateTimer]; } /// 开启一个当前线程内可重复执行的NSTimer对象 /// @param inerval 间隔时间 /// @param repeats 是否重复 /// @param task 事件处理 - (instancetype)initWithInterval:(NSTimeInterval)inerval repeats:(BOOL)repeats task:(void(^)(void))task{ if (self = [super init]) { self.pausing = YES; self.interval = inerval; self.timer = [NSTimer scheduledTimerWithTimeInterval:inerval target:self selector:@selector(blcokInvoke:) userInfo:[task copy] repeats:repeats]; } return self; } - (void)blcokInvoke:(NSTimer *)timer{ void (^withBlock)(void) = timer.userInfo; if (withBlock) withBlock(); self.pausing = NO; } /// 开启计时器 - (void)kj_startTimer{ if (![self.timer isValid]) return; [self.timer setFireDate:[NSDate dateWithTimeIntervalSinceNow:self.interval]]; self.pausing = NO; } /// 暂停计时器 - (void)kj_pauseTimer{ if (![self.timer isValid]) return; [self.timer setFireDate:[NSDate distantFuture]]; self.pausing = YES; } /// 继续计时器 - (void)kj_resumeTimer{ if (![self.timer isValid]) return; [self.timer setFireDate:[NSDate date]]; self.pausing = NO; } /// 释放计时器 - (void)kj_invalidateTimer{ if (_timer) { [_timer invalidate]; _timer = nil; } self.pausing = YES; } /// 立刻执行 - (void)kj_immediatelyTimer{ if (![self.timer isValid]) return; [self.timer fire]; self.pausing = NO; } @end ================================================ FILE: Sources/PageControl/KJPageControl.h ================================================ // // KJPageControl.h // KJBannerViewDemo // // Created by 杨科军 on 2019/5/27. // Copyright © 2019 杨科军. All rights reserved. // https://github.com/yangKJ/KJBannerViewDemo #import NS_ASSUME_NONNULL_BEGIN /// 分页控件显示位置 typedef NS_ENUM(NSInteger, KJPageControlDisplayType) { KJPageControlDisplayTypeCenter, KJPageControlDisplayTypeLeft, KJPageControlDisplayTypeRight, }; /// 分页控件类型 typedef NS_ENUM(NSInteger, KJPageControlStyle) { PageControlStyleRectangle = 0,// 长方形 PageControlStyleCircle, // 圆形 PageControlStyleSquare, // 正方形 PageControlStyleSizeDot,// 大小点 }; @interface KJPageControl : UIView /// 总共点数 @property(nonatomic,assign) IBInspectable NSInteger totalPages; /// 当前点,默认第一个点 @property(nonatomic,assign) IBInspectable NSInteger currentIndex; /// 选中的色,默认白色 @property(nonatomic,strong) IBInspectable UIColor *selectColor; /// 背景色,默认灰色 @property(nonatomic,strong) IBInspectable UIColor *normalColor; /// 方块间隙,默认大小点5px,其余8px @property(nonatomic,assign) IBInspectable CGFloat margin; /// 方块宽度 @property(nonatomic,assign) IBInspectable CGFloat dotwidth; /// 方块高度 @property(nonatomic,assign) IBInspectable CGFloat dotheight; /// 类别,默认长方形 @property(nonatomic,assign) KJPageControlStyle pageType; /// 距离边界间隙,默认10px @property(nonatomic,assign) IBInspectable CGFloat space; /// 控件位置,默认居中 @property(nonatomic,assign) KJPageControlDisplayType displayType; /// 滚动当前page - (void)scrollToIndex:(NSInteger)index; @end NS_ASSUME_NONNULL_END ================================================ FILE: Sources/PageControl/KJPageControl.m ================================================ // // KJPageControl.m // KJBannerViewDemo // // Created by 杨科军 on 2019/5/27. // Copyright © 2019 杨科军. All rights reserved. // https://github.com/yangKJ/KJBannerViewDemo #import "KJPageControl.h" /// 大小点控件 @interface KJDotPageView : UIView @property(nonatomic,strong)UIView *backView; @property(nonatomic,assign)NSInteger pages; @property(nonatomic,assign)NSInteger currentPage; @property(nonatomic,strong)UIColor *normalColor,*selectColor; @property(nonatomic,assign)CGFloat margin,normalheight; @property(nonatomic,assign)CGFloat normalWidth,selectWidth; @property(nonatomic,assign)CGFloat space; @property(nonatomic,assign)KJPageControlDisplayType displayType; @end @interface KJPageControl () @property(nonatomic,strong)UIView *backView; @property(nonatomic,strong)KJDotPageView *loopPageView; @end @implementation KJPageControl - (instancetype)initWithFrame:(CGRect)frame{ self = [super initWithFrame:frame]; if (self) { _pageType = PageControlStyleRectangle; _normalColor = UIColor.lightGrayColor; _selectColor = UIColor.whiteColor; _currentIndex = 0; _space = 10; self.backView = [UIView new]; [self addSubview:_backView]; } return self; } /// 设置PageView - (void)setTotalPages:(NSInteger)pages{ _totalPages = pages; if (_pageType == PageControlStyleSizeDot) { self.loopPageView.pages = pages; return; } [self.backView.subviews makeObjectsPerformSelector:@selector(removeFromSuperview) withObject:self]; CGFloat margin = self.margin?:8; CGFloat dotwidth,dotheight = 0.0; CGFloat width = self.frame.size.width; CGFloat height = self.frame.size.height; if (self.dotwidth && self.dotheight) { dotwidth = self.dotwidth; dotheight = self.dotheight; } else { dotwidth = (width - (pages - 1) * margin) / pages; dotwidth = dotwidth > height * .5 ? height * .5 : dotwidth; if (_pageType == PageControlStyleCircle || _pageType == PageControlStyleSquare ) { dotheight = dotwidth; } else if (_pageType == PageControlStyleRectangle) { dotheight = dotwidth/4.; dotwidth *= 1.5; } } CGFloat backwidth = pages * (dotwidth + margin); self.backView.frame = CGRectMake(0, 0, backwidth, height); switch (self.displayType) { case KJPageControlDisplayTypeCenter: self.backView.center = CGPointMake(width * .5, height); break; case KJPageControlDisplayTypeLeft: self.backView.center = CGPointMake(backwidth * .5 + self.space, height); break; case KJPageControlDisplayTypeRight: self.backView.center = CGPointMake(width - backwidth * .5 - self.space + margin, height); break; default: break; } CGFloat x = 0; for (int i = 0; i < pages; i++) { UIView *view = [UIView new]; [self.backView addSubview:view]; view.tag = 520 + i; view.backgroundColor = i == _currentIndex ? _selectColor : _normalColor; switch (_pageType) { case PageControlStyleCircle: view.frame = CGRectMake(x, 0, dotwidth, dotheight); view.layer.cornerRadius = dotwidth * .5; break; case PageControlStyleSquare: view.frame = CGRectMake(x, 0, dotwidth, dotheight); break; case PageControlStyleRectangle: view.frame = CGRectMake(x, 0, dotwidth, dotheight); break; default: break; } x += dotwidth + margin; } } - (void)setCurrentIndex:(NSInteger)currentIndex{ if (_pageType == PageControlStyleSizeDot) { self.loopPageView.currentPage = currentIndex; return; } if (_currentIndex != currentIndex) { bool isRight = false; NSInteger max = _totalPages - 1; if (currentIndex == 0 && _currentIndex == max) { isRight = true; } else if (currentIndex == max && _currentIndex == 0) { isRight = false; } else if (_currentIndex < currentIndex) { isRight = true; } UIView *view = self.backView.subviews[currentIndex]; view.backgroundColor = self.selectColor; if (isRight && currentIndex == 0) { UIView *lastView = self.backView.subviews.lastObject; lastView.backgroundColor = self.normalColor; } else if (!isRight && currentIndex == max) { UIView *lastView = self.backView.subviews.firstObject; lastView.backgroundColor = self.normalColor; } else { UIView *lastView = self.backView.subviews[isRight?currentIndex-1:currentIndex+1]; lastView.backgroundColor = self.normalColor; } _currentIndex = currentIndex; } } /// 非自动滚动情况 - (void)scrollToIndex:(NSInteger)index{ _currentIndex = index; if (_pageType == PageControlStyleSizeDot) { self.loopPageView.currentPage = index; return; } [self.backView.subviews enumerateObjectsUsingBlock:^(__kindof UIView * obj, NSUInteger idx, BOOL * stop) { obj.backgroundColor = idx == index ? self.selectColor : self.normalColor; }]; } #pragma mark - lazy - (KJDotPageView *)loopPageView{ if (!_loopPageView) { _loopPageView = [[KJDotPageView alloc] initWithFrame:self.bounds]; _loopPageView.normalColor = _normalColor; _loopPageView.selectColor = _selectColor; _loopPageView.space = self.space; _loopPageView.displayType = self.displayType; _loopPageView.normalWidth = self.dotwidth?:5; _loopPageView.margin = self.margin?:5.; _loopPageView.selectWidth = (self.dotwidth?:5) * 2; _loopPageView.normalheight = self.dotheight?:5; [self addSubview:_loopPageView]; } return _loopPageView; } @end @implementation KJDotPageView - (instancetype)initWithFrame:(CGRect)frame{ if (self = [super initWithFrame:frame]) { self.backView = [UIView new]; [self addSubview:_backView]; } return self; } - (void)setCurrentPage:(NSInteger)currentPage{ if (_currentPage == currentPage) return; _currentPage = MIN(currentPage, _pages - 1); CGFloat x = 0; for (int i = 0; i < _pages; i++) { UIView * view = [self.backView viewWithTag:520+i]; if (i == _currentPage) { view.frame = CGRectMake(x, 0, _selectWidth, _normalheight); x += _selectWidth + _margin; view.backgroundColor = _selectColor; } else { view.frame = CGRectMake(x, 0, _normalWidth, _normalheight); x += _normalWidth + _margin; view.backgroundColor = _normalColor; } } } - (void)setPages:(NSInteger)pages{ if (pages <= 0 || _pages == pages) return; _pages = pages; [self.backView.subviews makeObjectsPerformSelector:@selector(removeFromSuperview) withObject:self]; CGFloat width = _selectWidth + (pages - 1) * _normalWidth + (pages - 1) * _margin; self.backView.frame = CGRectMake(0, 0, width, _normalheight); switch (self.displayType) { case KJPageControlDisplayTypeCenter: self.backView.center = CGPointMake(self.frame.size.width*.5, self.frame.size.height/2.); break; case KJPageControlDisplayTypeLeft: self.backView.center = CGPointMake(width/2.+self.space, self.frame.size.height/2.); break; case KJPageControlDisplayTypeRight: self.backView.center = CGPointMake(self.frame.size.width-width/2.-self.space, self.frame.size.height/2.); break; default: break; } CGFloat x = 0; for (int i = 0; i < pages; i++) { UIView *view = [UIView new]; view.tag = 520 + i; view.layer.cornerRadius = _normalheight*.5; if (i == _currentPage) { view.frame = CGRectMake(x, 0, _selectWidth, _normalheight); view.backgroundColor = _selectColor; x += _selectWidth + _margin; } else { view.frame = CGRectMake(x, 0, _normalWidth, _normalheight); view.backgroundColor = _normalColor; x += _normalWidth + _margin; } [self.backView addSubview:view]; } } @end ================================================ FILE: Sources/WebImage/KJAutoPurgingCache.h ================================================ // // KJAutoPurgingImageCache.h // KJBannerViewDemo // // Created by 杨科军 on 2021/2/19. // Copyright © 2021 杨科军. All rights reserved. // https://github.com/yangKJ/KJBannerViewDemo // 定时清理指定时间段以前图片资源缓存 #import NS_ASSUME_NONNULL_BEGIN /// 清理时间周期 typedef NS_ENUM(NSInteger, KJAutoPurginCacheType) { KJAutoPurginCacheTypeOneDay, //1天 KJAutoPurginCacheTypeThreeDay,//3天 KJAutoPurginCacheTypeOneWeek, //1周 KJAutoPurginCacheTypeOneMonth,//1月 KJAutoPurginCacheTypeOneYear, //1年 KJAutoPurginCacheTypeAll, //清理全部 }; extern NSString * kBannerTimingUserDefaultsKey; /// 定时清理指定时间段以前图片资源缓存 @interface KJAutoPurgingCache : NSObject /// 该方法需要在程序最开始位置执行,可以是Appdelegate或首页控制器里面 /// @param open 开启清理功能,只有开启过该方法才会存储时间 /// @param type 清除指定时间以前的数据 + (void)autoPurgingCache:(BOOL)open timingTimeType:(KJAutoPurginCacheType)type; /// 自动清理大于多少的数据源,TODO /// @param maxBytes 最大存储数据,单位kb + (void)autoPurgingMaxBytes:(NSInteger)maxBytes; @end NS_ASSUME_NONNULL_END ================================================ FILE: Sources/WebImage/KJAutoPurgingCache.m ================================================ // // KJAutoPurgingImageCache.m // KJBannerViewDemo // // Created by 杨科军 on 2021/2/19. // Copyright © 2021 杨科军. All rights reserved. // https://github.com/yangKJ/KJBannerViewDemo #import "KJAutoPurgingCache.h" #import "KJImageCache.h" NSString * kBannerTimingUserDefaultsKey = @"kBannerTimingUserDefaultsKey"; @implementation KJAutoPurgingCache static BOOL _openTiming = NO; + (BOOL)openTiming{ return _openTiming; } /* 开启清理功能,清除时间以前的数据 */ + (void)autoPurgingCache:(BOOL)open timingTimeType:(KJAutoPurginCacheType)type{ _openTiming = open; if (open == NO) return; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSUserDefaults * userDefaults = [NSUserDefaults standardUserDefaults]; if (type == KJAutoPurginCacheTypeAll) { [KJImageCache kj_clearLocalityImageAndCache]; [userDefaults setObject:nil forKey:kBannerTimingUserDefaultsKey]; [userDefaults synchronize]; return; } NSDictionary * before = [userDefaults dictionaryForKey:kBannerTimingUserDefaultsKey]; if (before == nil) return; NSMutableDictionary * dict = [NSMutableDictionary dictionaryWithDictionary:before]; NSInteger time = (NSInteger)NSDate.date.timeIntervalSince1970; switch (type) { case KJAutoPurginCacheTypeOneDay: time -= 24 * 60 * 60; break; case KJAutoPurginCacheTypeThreeDay: time -= 24 * 60 * 60 * 3; break; case KJAutoPurginCacheTypeOneWeek: time -= 24 * 60 * 60 * 7; break; case KJAutoPurginCacheTypeOneMonth: time -= 24 * 60 * 60 * 30; break; case KJAutoPurginCacheTypeOneYear: time -= 24 * 60 * 60 * 365; break; default: break; } NSArray * keys = dict.allKeys; keys = [self kj_sortDescriptorWithArray:keys key:@"self"]; NSInteger index; if ([keys.lastObject integerValue] <= time) { index = 0; } else { index = [self kj_searchKeys:keys timingTime:time]; if (index == 0 && [keys[0] integerValue] <= time) { NSString * key = [NSString stringWithFormat:@"%@",keys[0]]; if ([self kj_removePath:[dict valueForKey:key]]) { [dict removeObjectForKey:key]; } [userDefaults setObject:dict forKey:kBannerTimingUserDefaultsKey]; [userDefaults synchronize]; return; } index = keys.count - index; } for (NSInteger i = index; i < keys.count; i++) { NSString * key = [NSString stringWithFormat:@"%@",keys[i]]; if ([self kj_removePath:[dict valueForKey:key]]) { [dict removeObjectForKey:key]; } } [userDefaults setObject:dict forKey:kBannerTimingUserDefaultsKey]; [userDefaults synchronize]; }); } /* 自动清理大于多少的数据源 */ + (void)autoPurgingMaxBytes:(NSInteger)maxBytes{ //TODO: } #pragma mark - private /// 升序排列 + (NSArray *)kj_sortDescriptorWithArray:(NSArray *)array key:(NSString *)key{ NSSortDescriptor * des = [NSSortDescriptor sortDescriptorWithKey:key ascending:YES]; NSMutableArray * temp = [NSMutableArray arrayWithArray:array]; [temp sortUsingDescriptors:@[des]]; return [temp mutableCopy]; } /// 谓词匹配查找index + (NSInteger)kj_searchKeys:(NSArray *)keys timingTime:(NSInteger)time{ NSString * string = [NSString stringWithFormat:@"SELF LIKE '%ld'", time]; NSPredicate * predicate = [NSPredicate predicateWithFormat:string]; return [[keys filteredArrayUsingPredicate:predicate].firstObject integerValue]; } /// 删除路径文件 + (BOOL)kj_removePath:(NSString *)path{ NSString *directoryPath = [KJBannerLoadImages stringByAppendingPathComponent:path]; if ([[NSFileManager defaultManager] fileExistsAtPath:directoryPath isDirectory:nil]) { return [[NSFileManager defaultManager] removeItemAtPath:directoryPath error:nil]; } return YES; } @end ================================================ FILE: Sources/WebImage/KJImageCache.h ================================================ // // KJImageCache.h // KJBannerViewDemo // // Created by 杨科军 on 2020/12/7. // Copyright © 2020 杨科军. All rights reserved. // https://github.com/yangKJ/KJBannerViewDemo // 缓存工具 #import #import NS_ASSUME_NONNULL_BEGIN #define KJBannerLoadImages [NSHomeDirectory() stringByAppendingPathComponent:@"Documents/KJLoadImages"] /// 图片资源缓存工具 @interface KJImageCache : NSObject /// 最大缓存,默认50mb @property (nonatomic, assign, class) NSUInteger maxCache; /// 是否允许写入Cache,默认为YES @property (nonatomic, assign, class) BOOL allowCache; /// 先从缓存读取,若没有则读取本地文件 + (nullable UIImage *)readCacheImageWithKey:(NSString *)key; /// 先从缓存读取,若没有则读取本地文件并写入缓存 + (void)readImageWithKey:(NSString *)key completion:(void(^)(UIImage *image))completion; /// 将图片写入缓存和存储到本地 + (void)storeImage:(UIImage *)image Key:(NSString *)key; /// 链接名转换成MD5 + (NSString *)MD5String:(NSString *)key; @end //********************* 动态图缓存相关 ********************* @interface KJImageCache (KJBannerGIF) /// 动态图本地获取 + (nullable NSData *)readGIFImageWithKey:(NSString *)key; /// 将动态图写入本地 + (void)storeGIFData:(NSData *)data Key:(NSString *)key; @end //********************* 缓存大小相关 ********************* @interface KJImageCache (KJBannerCache) /// 清理掉缓存和本地文件 + (BOOL)kj_clearLocalityImageAndCache; /// 获取图片本地文件总大小 + (int64_t)kj_getLocalityImageCacheSize; @end NS_ASSUME_NONNULL_END ================================================ FILE: Sources/WebImage/KJImageCache.m ================================================ // // KJImageCache.m // KJBannerViewDemo // // Created by 杨科军 on 2020/12/7. // Copyright © 2020 杨科军. All rights reserved. // https://github.com/yangKJ/KJBannerViewDemo #import "KJImageCache.h" #import #import #import "KJAutoPurgingCache.h" @interface KJImageCache() @property(nonatomic,strong,class)NSCache *cache; @end @implementation KJImageCache - (void)dealloc{ [[NSNotificationCenter defaultCenter] removeObserver:self]; } /// 先从缓存读取,若没有则读取本地文件 + (nullable UIImage *)readCacheImageWithKey:(NSString *)key{ if (key == nil || key.length == 0) return nil; NSString *subpath = [KJImageCache MD5String:key]; UIImage *image = [self.cache objectForKey:subpath]; if (image == nil) { NSString *path = [KJBannerLoadImages stringByAppendingPathComponent:subpath]; image = [UIImage imageWithContentsOfFile:path]; } return image; } /// 先从缓存读取,若没有则读取本地文件并写入缓存 + (void)readImageWithKey:(NSString *)key completion:(void(^)(UIImage *image))completion{ if (key == nil || key.length == 0) { dispatch_async(dispatch_get_main_queue(), ^{ if (completion) completion(nil); }); return; } dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSString *subpath = [KJImageCache MD5String:key]; UIImage *image = [self.cache objectForKey:subpath]; if (image == nil) { NSString *path = [KJBannerLoadImages stringByAppendingPathComponent:subpath]; image = [UIImage imageWithContentsOfFile:path]; if (image && self.allowCache) { [self kj_config]; NSUInteger cost = kImageCacheSize(image); [self.cache setObject:image forKey:subpath cost:cost]; } } dispatch_async(dispatch_get_main_queue(), ^{ if (completion) completion(image); }); }); } /// 将图片写入缓存和存储到本地 + (void)storeImage:(UIImage *)image Key:(NSString *)key{ if (image == nil || key == nil || key.length == 0) return; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSString *subpath = [KJImageCache MD5String:key]; [self kj_saveExtensionPath:subpath]; if (self.allowCache) { [self kj_config]; NSUInteger cost = kImageCacheSize(image); [self.cache setObject:image forKey:subpath cost:cost]; } NSString *directoryPath = KJBannerLoadImages; if (![[NSFileManager defaultManager] fileExistsAtPath:directoryPath isDirectory:nil]) { NSError *error = nil; BOOL ok = [[NSFileManager defaultManager] createDirectoryAtPath:directoryPath withIntermediateDirectories:YES attributes:nil error:&error]; if (ok && error == nil){}else return; } @autoreleasepool { NSString *path = [directoryPath stringByAppendingPathComponent:subpath]; NSData *data = UIImagePNGRepresentation(image); [[NSFileManager defaultManager] createFileAtPath:path contents:data attributes:nil]; } }); } /// 链接名转换成MD5 + (NSString *)MD5String:(NSString *)key{ const char * original = [key UTF8String]; unsigned char digist[CC_MD5_DIGEST_LENGTH]; CC_MD5(original, (uint)strlen(original), digist); NSMutableString *resultString = [NSMutableString stringWithCapacity:10]; for (int i = 0; i < CC_MD5_DIGEST_LENGTH; i++) [resultString appendFormat:@"%02X", digist[i]]; return [resultString lowercaseString]; } #pragma mark - private static KJImageCache *manager = nil; + (KJImageCache *)kj_config{ @synchronized (self) { if (manager == nil) { manager = [[super allocWithZone:NULL] init]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(kj_clearCaches) name:UIApplicationDidReceiveMemoryWarningNotification object:nil]; } } return manager; } - (void)kj_clearCaches{ [KJImageCache.cache removeAllObjects]; } static inline NSUInteger kImageCacheSize(UIImage * image){ return image.size.height * image.size.width * image.scale * image.scale; } //存储扩展 + (void)kj_saveExtensionPath:(NSString *)subpath{ bool openTiming = [[KJAutoPurgingCache valueForKey:@"openTiming"] boolValue]; if (openTiming) { NSDictionary *defaults = [[NSUserDefaults standardUserDefaults] dictionaryForKey:kBannerTimingUserDefaultsKey]; NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithDictionary:defaults]; [dict setObject:subpath forKey:[NSString stringWithFormat:@"%f",NSDate.date.timeIntervalSince1970]]; [[NSUserDefaults standardUserDefaults] setObject:dict forKey:kBannerTimingUserDefaultsKey]; [[NSUserDefaults standardUserDefaults] synchronize]; } } #pragma mark - lazy static NSCache *_cache = nil; + (NSCache *)cache{ if (_cache == nil) { _cache = [[NSCache alloc] init]; } return _cache; } + (void)setCache:(NSCache *)cache{ _cache = cache; } static BOOL _allowCache = YES; + (BOOL)allowCache{ return _allowCache; } + (void)setAllowCache:(BOOL)allowCache{ _allowCache = allowCache; } static NSUInteger _maxCache = 50; + (NSUInteger)maxCache{ return _maxCache; } + (void)setMaxCache:(NSUInteger)maxCache{ _maxCache = maxCache; self.cache.totalCostLimit = _maxCache * 1024 * 1024; } @end @implementation KJImageCache (KJBannerGIF) + (nullable NSData *)readGIFImageWithKey:(NSString *)key{ if (key == nil || key.length == 0) return nil; NSString *path = [KJBannerLoadImages stringByAppendingPathComponent:[KJImageCache MD5String:key]]; return [NSData dataWithContentsOfFile:path]; } /// 将动态图写入存储到本地 + (void)storeGIFData:(NSData *)data Key:(NSString *)key{ if (data == nil || key == nil || data.length == 0) return; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSString *subpath = [KJImageCache MD5String:key]; SEL sel = NSSelectorFromString(@"kj_saveExtensionPath:"); if ([self respondsToSelector:sel]) { ((void(*)(id, SEL, NSString*))(void*)objc_msgSend)((id)self, sel, subpath); } NSString *directoryPath = KJBannerLoadImages; if (![[NSFileManager defaultManager] fileExistsAtPath:directoryPath isDirectory:nil]) { NSError *error = nil; BOOL isOK = [[NSFileManager defaultManager] createDirectoryAtPath:directoryPath withIntermediateDirectories:YES attributes:nil error:&error]; if (isOK && error == nil) { } else return; } @autoreleasepool { NSString *path = [directoryPath stringByAppendingPathComponent:subpath]; [[NSFileManager defaultManager] createFileAtPath:path contents:data attributes:nil]; } }); } @end @implementation KJImageCache (KJBannerCache) /// 清理掉缓存和本地文件 + (BOOL)kj_clearLocalityImageAndCache{ [self.cache removeAllObjects]; NSString *directoryPath = KJBannerLoadImages; if ([[NSFileManager defaultManager] fileExistsAtPath:directoryPath isDirectory:nil]) { return [[NSFileManager defaultManager] removeItemAtPath:directoryPath error:nil]; } return YES; } /// 获取图片本地文件总大小 + (int64_t)kj_getLocalityImageCacheSize{ BOOL isDir = NO; int64_t total = 0; NSString * directoryPath = KJBannerLoadImages; if ([[NSFileManager defaultManager] fileExistsAtPath:directoryPath isDirectory:&isDir]) { if (isDir == NO) return total; NSError *error = nil; NSArray *array = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:directoryPath error:&error]; if (error) return total; for (NSString * subpath in array) { NSString *path = [directoryPath stringByAppendingPathComponent:subpath]; NSDictionary *dict = [[NSFileManager defaultManager] attributesOfItemAtPath:path error:&error]; if (error == nil) total += [dict[NSFileSize] unsignedIntegerValue]; } } return total; } @end ================================================ FILE: Sources/WebImage/KJNetworkManager.h ================================================ // // KJNetworkManager.h // KJBannerViewDemo // // Created by 杨科军 on 2020/12/7. // Copyright © 2020 杨科军. All rights reserved. // https://github.com/yangKJ/KJBannerViewDemo // 网络请求工具 #import #import NS_ASSUME_NONNULL_BEGIN @class KJBannerDownloadProgress; /// 网络请求回调 typedef void (^_Nullable KJLoadDataBlock)(NSData * _Nullable data, NSError * _Nullable error); /// 下载进度回调 typedef void (^_Nullable KJLoadProgressBlock)(KJBannerDownloadProgress * downloadProgress); @interface KJNetworkManager : NSObject /// 超时时长,默认10秒 @property(nonatomic,assign)NSTimeInterval timeoutInterval; /// 设置最大并发队列数,默认为2条 @property(nonatomic,assign)NSUInteger maxConcurrentOperationCount; /// 下载数据 /// @param URL 下载链接 /// @param progress 下载进度回调 /// @param complete 下载完成回调 - (void)kj_startDownloadImageWithURL:(NSURL *)URL progress:(KJLoadProgressBlock)progress complete:(KJLoadDataBlock)complete; /// 取消下载 - (void)kj_cancelRequest; @end //********************* 下载进度条 ********************* @interface KJBannerDownloadProgress : NSObject @property(nonatomic,assign)int64_t bytesWritten;// 当前下载包大小 @property(nonatomic,assign)int64_t downloadBytes;// 已下载大小 @property(nonatomic,assign)int64_t totalBytes;// 总大小 @property(nonatomic,assign)float progress;// 下载进度 @property(nonatomic,assign)float speed;// 当前下载速度 kb/s @end NS_ASSUME_NONNULL_END ================================================ FILE: Sources/WebImage/KJNetworkManager.m ================================================ // // KJNetworkManager.m // KJBannerViewDemo // // Created by 杨科军 on 2020/12/7. // Copyright © 2020 杨科军. All rights reserved. // https://github.com/yangKJ/KJBannerViewDemo #import "KJNetworkManager.h" @implementation KJBannerDownloadProgress @end @interface KJNetworkManager () @property(nonatomic,strong)NSURLSessionTask *task; @property(nonatomic,strong)NSOperationQueue *queue; @property(nonatomic,strong)NSURLSessionConfiguration *configuration; @property(nonatomic,strong)KJBannerDownloadProgress *downloadProgress; @property(nonatomic,copy,readwrite)KJLoadProgressBlock progressBlock; @property(nonatomic,copy,readwrite)KJLoadDataBlock dataBlock; @end @implementation KJNetworkManager - (void)kj_cancelRequest{ [self.task cancel]; } - (void)kj_startDownloadImageWithURL:(NSURL *)URL progress:(KJLoadProgressBlock)progress complete:(KJLoadDataBlock)complete{ if (URL == nil) { //NSAssert(URL == nil, @"URL is nil."); NSError *error = [NSError errorWithDomain:@"url failed" code:400 userInfo:@{NSLocalizedDescriptionKey:@"URL is nil."}]; complete ? complete(nil, error) : nil; return; } self.maxConcurrentOperationCount = 2; if (progress) { self.dataBlock = complete; self.progressBlock = progress; [self kj_downloadImageWithURL:URL]; } else { [self kj_dataImageWithURL:URL Complete:complete]; } } /// 不需要下载进度的网络请求 - (void)kj_dataImageWithURL:(NSURL *)URL Complete:(KJLoadDataBlock)complete{ NSMutableURLRequest *request = kGetRequest(URL, self.timeoutInterval ?: 10.0); NSURLSession *session = [NSURLSession sessionWithConfiguration:self.configuration delegate:self delegateQueue:self.queue]; NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData * data, NSURLResponse * response, NSError * error) { if (complete) complete(data, error); }]; [dataTask resume]; self.task = dataTask; } /// 下载进度的请求方式 - (void)kj_downloadImageWithURL:(NSURL *)URL{ NSMutableURLRequest *request = kGetRequest(URL, self.timeoutInterval ?: 10.0); NSURLSession *session = [NSURLSession sessionWithConfiguration:self.configuration delegate:self delegateQueue:self.queue]; NSURLSessionDownloadTask *downloadTask = [session downloadTaskWithRequest:request]; [downloadTask resume]; self.task = downloadTask; } /// 创建请求对象 NS_INLINE NSMutableURLRequest * kGetRequest(NSURL * URL, NSTimeInterval timeoutInterval){ NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:URL]; request.timeoutInterval = timeoutInterval; request.HTTPShouldUsePipelining = YES; request.cachePolicy = NSURLRequestReloadIgnoringLocalCacheData; NSMutableDictionary *param = [NSMutableDictionary dictionary]; [param setValue:@"image/webp,image/*;q=0.8" forKey:@"Accept"]; //[param setValue:@"" forKey:@"Accept-Encoding"]; [request setAllHTTPHeaderFields:param]; return request; } #pragma mark - NSURLSessionDataDelegate - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask*)dataTask didReceiveResponse:(NSURLResponse*)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler{ completionHandler(NSURLSessionResponseAllow); } - (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge*)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential *))completionHandler{ NSURLCredential *card = [[NSURLCredential alloc] initWithTrust:challenge.protectionSpace.serverTrust]; completionHandler(NSURLSessionAuthChallengeUseCredential, card); } #pragma mark - NSURLSessionDownloadDelegate - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes{ } /// 下载中 - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite{ @synchronized (self.downloadProgress) { self.downloadProgress.bytesWritten = bytesWritten; self.downloadProgress.downloadBytes = totalBytesWritten; self.downloadProgress.speed = bytesWritten / 1024.; if (totalBytesExpectedToWrite == -1) { self.downloadProgress.totalBytes = 0; self.downloadProgress.progress = 0; } else { self.downloadProgress.totalBytes = totalBytesExpectedToWrite; self.downloadProgress.progress = (double)totalBytesWritten / totalBytesExpectedToWrite; } self.progressBlock(self.downloadProgress); } } /// 下载完成调用 - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location{ if (self.dataBlock) { NSData *data = [NSData dataWithContentsOfURL:location]; self.dataBlock(data, nil); _dataBlock = nil; } } /// 下载失败 - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError*)error{ if ([error code] != NSURLErrorCancelled) { if (self.dataBlock) { self.dataBlock(nil, error); _dataBlock = nil; } } } /// 后台下载 - (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session{ } #pragma mark - getter/setter - (void)setMaxConcurrentOperationCount:(NSUInteger)maxConcurrentOperationCount{ _maxConcurrentOperationCount = maxConcurrentOperationCount; self.queue.maxConcurrentOperationCount = maxConcurrentOperationCount; } #pragma mark - lazy - (KJBannerDownloadProgress *)downloadProgress{ if (!_downloadProgress) { _downloadProgress = [[KJBannerDownloadProgress alloc]init]; } return _downloadProgress; } - (NSOperationQueue *)queue{ if (!_queue) { _queue = [[NSOperationQueue alloc]init]; } return _queue; } - (NSURLSessionConfiguration *)configuration{ if (!_configuration) { _configuration = [NSURLSessionConfiguration defaultSessionConfiguration]; _configuration.networkServiceType = NSURLNetworkServiceTypeDefault; } return _configuration; } @end ================================================ FILE: Sources/WebImage/KJWebImageDownloader.h ================================================ // // KJWebImageDownloader.h // KJBannerViewDemo // // Created by 杨科军 on 2020/12/7. // Copyright © 2020 杨科军. All rights reserved. // https://github.com/yangKJ/KJBannerViewDemo // 网图下载工具 #import #import "KJNetworkManager.h" NS_ASSUME_NONNULL_BEGIN /// 网图下载工具 @interface KJWebImageDownloader : NSObject /// 失败次数,默认2次 @property(nonatomic,assign,class)NSInteger kMaxLoadNum; /// 是否使用异步,默认NO @property(nonatomic,assign,class)BOOL useAsync; /// 带缓存机制的下载图片 + (void)kj_loadImageWithURL:(NSString *)url complete:(void(^)(UIImage *image))complete; + (void)kj_loadImageWithURL:(NSString *)url complete:(void(^)(UIImage * image))complete progress:(KJLoadProgressBlock)progress; /// 下载数据,未使用缓存机制 + (NSData *)kj_downloadDataWithURL:(NSString *)url progress:(KJLoadProgressBlock)progress; @end NS_ASSUME_NONNULL_END ================================================ FILE: Sources/WebImage/KJWebImageDownloader.m ================================================ // // KJWebImageDownloader.m // KJBannerViewDemo // // Created by 杨科军 on 2020/12/7. // Copyright © 2020 杨科军. All rights reserved. // https://github.com/yangKJ/KJBannerViewDemo #import "KJWebImageDownloader.h" #import "KJImageCache.h" @interface KJWebImageDownloader () @property(nonatomic,strong,class)NSMutableDictionary *dict; @end @implementation KJWebImageDownloader static KJWebImageDownloader *manager = nil; /// 带缓存机制的下载图片 + (void)kj_loadImageWithURL:(NSString *)url complete:(void(^)(UIImage *image))complete{ [self kj_loadImageWithURL:url complete:complete progress:nil]; } + (void)kj_loadImageWithURL:(NSString *)url complete:(void(^)(UIImage *image))complete progress:(KJLoadProgressBlock)progress{ void (^kGetNetworkingImage)(void) = ^{ if ([self kj_failureNumsForKey:url] >= self.kMaxLoadNum) { if (complete) complete(nil); return; } void (^kAnalysis)(NSData *data, NSError *error) = ^(NSData *data, NSError *error){ UIImage *image = nil; if (error) { [self kj_cacheFailureForKey:url]; [self kj_loadImageWithURL:url complete:complete progress:progress]; return; } else { image = [UIImage imageWithData:data]; if (image) { dispatch_async(dispatch_get_main_queue(), ^{ if (complete) complete(image); }); [KJImageCache storeImage:image Key:url]; [self kj_resetFailureDictForKey:url]; return; } } dispatch_async(dispatch_get_main_queue(), ^{ if (complete) complete(image); }); }; KJNetworkManager *downloader = [[KJNetworkManager alloc] init]; NSURL * __autoreleasing URL = [NSURL URLWithString:url]; if (progress) { [downloader kj_startDownloadImageWithURL:URL progress:^(KJBannerDownloadProgress * __progress) { progress(__progress); } complete:^(NSData * _Nullable data, NSError * _Nullable error) { kAnalysis(data, error); }]; } else { [downloader kj_startDownloadImageWithURL:URL progress:nil complete:^(NSData *data, NSError *error) { kAnalysis(data, error); }]; } }; if (self.useAsync) { [KJImageCache readImageWithKey:url completion:^(UIImage * _Nonnull image) { if (image) { dispatch_async(dispatch_get_main_queue(), ^{ if (complete) complete(image); }); } else { kGetNetworkingImage(); } }]; } else { UIImage *image = [KJImageCache readCacheImageWithKey:url]; if (image) { dispatch_async(dispatch_get_main_queue(), ^{ if (complete) complete(image); }); } else { kGetNetworkingImage(); } } } /// 下载数据,未使用缓存机制 + (NSData *)kj_downloadDataWithURL:(NSString *)url progress:(KJLoadProgressBlock)progress{ @synchronized (self) { if (manager == nil) manager = [self new]; } return [manager kj_recursionDataWithURL:[NSURL URLWithString:url] progress:progress]; } /// 递归拿到DATA - (NSData *)kj_recursionDataWithURL:(NSURL *)URL progress:(KJLoadProgressBlock)progress{ NSInteger count = [KJWebImageDownloader kj_failureNumsForKey:URL.absoluteString]; if (count >= KJWebImageDownloader.kMaxLoadNum) { return nil; } NSData *resultData = ({ if (URL == nil) return nil; __block NSData * __data = nil; dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); dispatch_group_async(dispatch_group_create(), queue, ^{ KJNetworkManager * downloader = [[KJNetworkManager alloc] init]; [downloader kj_startDownloadImageWithURL:URL progress:^(KJBannerDownloadProgress * __progress) { progress ? progress(__progress) : nil; } complete:^(NSData *data, NSError *error) { if (error) { [KJWebImageDownloader kj_cacheFailureForKey:URL.absoluteString]; } __data = data; dispatch_semaphore_signal(semaphore); }]; }); dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); __data; }); if (resultData) { [KJWebImageDownloader kj_resetFailureDictForKey:URL.absoluteString]; return resultData; } else { return [self kj_recursionDataWithURL:URL progress:progress]; } } #pragma mark - private /// 重置失败次数 + (void)kj_resetFailureDictForKey:(NSString *)key{ [self.dict setObject:@(0) forKey:[KJImageCache MD5String:key]]; } /// 失败次数 + (NSUInteger)kj_failureNumsForKey:(NSString *)key{ NSNumber * number = [self.dict objectForKey:[KJImageCache MD5String:key]]; return (number && [number respondsToSelector:@selector(integerValue)]) ? number.integerValue : 0; } /// 缓存失败 + (void)kj_cacheFailureForKey:(NSString *)key{ key = [KJImageCache MD5String:key]; NSNumber *number = [self.dict objectForKey:key]; NSUInteger index = 0; if (number && [number respondsToSelector:@selector(integerValue)]) { index = [number integerValue]; } index++; [self.dict setObject:@(index) forKey:key]; } #pragma mark - lazy static NSMutableDictionary *_dict = nil; + (NSMutableDictionary *)dict{ if (_dict == nil) { _dict = [NSMutableDictionary dictionary]; } return _dict; } + (void)setDict:(NSMutableDictionary *)dict{ _dict = dict; } static NSInteger _kMaxLoadNum = 2; + (NSInteger)kMaxLoadNum{ return _kMaxLoadNum; } + (void)setKMaxLoadNum:(NSInteger)kMaxLoadNum{ _kMaxLoadNum = kMaxLoadNum; } static BOOL _useAsync = NO; + (BOOL)useAsync{ return _useAsync; } + (void)setUseAsync:(BOOL)useAsync{ _useAsync = useAsync; } @end ================================================ FILE: Sources/WebImage/KJWebImageHeader.h ================================================ // // KJWebImageHeader.h // KJBannerViewDemo // // Created by 77。 on 2021/11/6. // Copyright © 2021 杨科军. All rights reserved. // #ifndef KJWebImageHeader_h #define KJWebImageHeader_h #import "UIView+KJWebImage.h" #import "KJAutoPurgingCache.h" #import "KJImageCache.h" #import "KJNetworkManager.h" #endif /* KJWebImageHeader_h */ ================================================ FILE: Sources/WebImage/UIView+KJWebImage.h ================================================ // // UIView+KJWebImage.h // KJBannerViewDemo // // Created by 杨科军 on 2021/1/28. // Copyright © 2021 杨科军. All rights reserved. // https://github.com/yangKJ/KJBannerViewDemo // 网图显示扩展 #import #import #import "KJNetworkManager.h" NS_ASSUME_NONNULL_BEGIN /// 图片类型 typedef NS_ENUM(NSInteger, KJWebImageType) { KJWebImageTypeUnknown = 0, /// 未知 KJWebImageTypeJpeg = 1, /// jpg KJWebImageTypePng = 2, /// png KJWebImageTypeGIF = 3, /// gif KJWebImageTypeTiff = 4, /// tiff KJWebImageTypeWebp = 5, /// webp }; @protocol KJWebImageDelegate; typedef void(^_Nullable KJWebImageProvider)(id provider); /// 网图显示扩展 @interface UIView (KJWebImage) /// 显示网络图片(目前支持设置UIImageView,UIButton,UIView三种) /// @param url 图片链接 /// @param provider 设置参数回调 - (void)kj_setImageWithURL:(NSURL *)url provider:(KJWebImageProvider)provider; @end /// 图片下载完成回调 typedef void (^_Nullable KJWebImageCompleted)(KJWebImageType imageType, UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error); typedef void(^_Nullable KJWebScaleImageBlock)(UIImage * originalImgae, UIImage * scaleImage); @protocol KJWebImageDelegate @optional; #pragma mark - common /// 图片下载完成回调 @property (nonatomic, copy, readwrite) KJWebImageCompleted webCompleted; /// 下载进度回调 @property (nonatomic, copy, readwrite) KJLoadProgressBlock webProgressBlock; /// 获取原始图回调,裁剪开启才有效果 @property (nonatomic, copy, readwrite) KJWebScaleImageBlock webScaleImageBlock; /// 占位图 @property (nonatomic, strong) UIImage *webPlaceholder; /// 是否缓存数据至本地,默认开启 @property (nonatomic, assign) bool webCacheDatas; /// 是否等比裁剪图片,默认关闭 @property (nonatomic, assign) bool webCropScale; /// 是否使用预渲染图像,默认开启(动态图暂不支持预渲染处理) @property (nonatomic, assign) bool webPreRendering; #pragma mark - button /// 按钮状态 @property (nonatomic, assign) UIControlState webButtonState; #pragma mark - view /// 图片填充方式 @property (nonatomic, copy) CALayerContentsGravity webContentsGravity; @end NS_ASSUME_NONNULL_END ================================================ FILE: Sources/WebImage/UIView+KJWebImage.m ================================================ // // UIView+KJWebImage.m // KJBannerViewDemo // // Created by 杨科军 on 2021/1/28. // Copyright © 2021 杨科军. All rights reserved. // https://github.com/yangKJ/KJBannerViewDemo #import "UIView+KJWebImage.h" #import #import "KJImageCache.h" @interface UIView () @end @implementation UIView (KJWebImage) - (void)kj_setImageWithURL:(NSURL *)url provider:(KJWebImageProvider)provider{ if (url == nil) return; self.webCacheDatas = true; self.webPreRendering = true; if (provider) provider(self); if ([self isKindOfClass:[UIImageView class]]) { [self kj_setImageViewImageWithURL:url provider:(id)self]; } else if ([self isKindOfClass:[UIButton class]]) { [self kj_setButtonImageWithURL:url provider:(id)self]; } else if ([self isKindOfClass:[UIView class]]) { [self kj_setViewImageContentsWithURL:url provider:(id)self]; } } #pragma mark - UIImageView - (void)kj_setImageViewImageWithURL:(NSURL *)url provider:(id)provider{ UIImageView *imageView = (UIImageView *)self; CGSize size = imageView.frame.size; if (provider.webPlaceholder) imageView.image = provider.webPlaceholder; [[[NSOperationQueue alloc] init] addOperationWithBlock:^{ NSData *data = [KJImageCache readGIFImageWithKey:url.absoluteString]; if (data) { kBannerWebImageSetImage(^(UIImage *image) { imageView.image = image; }, data, size, provider); } else { kBannerWebImageDownloader(url, size, provider, ^(UIImage * _Nonnull image) { [[NSOperationQueue mainQueue] addOperationWithBlock:^{ imageView.image = image; }]; }); } }]; } #pragma mark - UIButton - (UIControlState)webButtonState{ return (UIControlState)[objc_getAssociatedObject(self, _cmd) intValue]; } - (void)setWebButtonState:(UIControlState)webButtonState{ objc_setAssociatedObject(self, @selector(webButtonState), @(webButtonState), OBJC_ASSOCIATION_ASSIGN); } - (void)kj_setButtonImageWithURL:(NSURL *)url provider:(id)provider{ UIButton *button = (UIButton *)self; CGSize size = button.imageView.frame.size; if (provider.webPlaceholder) { [button setImage:provider.webPlaceholder forState:provider.webButtonState]; } [[[NSOperationQueue alloc] init] addOperationWithBlock:^{ NSData *data = [KJImageCache readGIFImageWithKey:url.absoluteString]; if (data) { kBannerWebImageSetImage(^(UIImage *image) { [button setImage:image forState:provider.webButtonState?:UIControlStateNormal]; }, data, size, provider); } else { kBannerWebImageDownloader(url, size, provider, ^(UIImage * _Nonnull image) { [[NSOperationQueue mainQueue] addOperationWithBlock:^{ [button setImage:image forState:provider.webButtonState?:UIControlStateNormal]; }]; }); } }]; } #pragma mark - UIView - (CALayerContentsGravity)webContentsGravity{ return objc_getAssociatedObject(self, _cmd); } - (void)setWebContentsGravity:(CALayerContentsGravity)webContentsGravity{ objc_setAssociatedObject(self, @selector(webContentsGravity), webContentsGravity, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } - (void)kj_setViewImageContentsWithURL:(NSURL *)url provider:(id)provider{ __weak __typeof(self) weakself = self; CGSize size = self.frame.size; [[[NSOperationQueue alloc] init] addOperationWithBlock:^{ NSData *data = [KJImageCache readGIFImageWithKey:url.absoluteString]; if (data) { kBannerWebImageSetImage(^(UIImage *image) { CALayer *layer = [weakself kj_setLayerImageContents:image?:provider.webPlaceholder]; layer.contentsGravity = provider.webContentsGravity?:kCAGravityResize; }, data, size, provider); } else { kBannerWebImageDownloader(url, size, provider, ^(UIImage * _Nonnull image) { [[NSOperationQueue mainQueue] addOperationWithBlock:^{ CALayer *layer = [weakself kj_setLayerImageContents:image?:provider.webPlaceholder]; layer.contentsGravity = provider.webContentsGravity?:kCAGravityResize; }]; }); } }]; } /// 设置Layer上面的内容,默认充满的填充方式 - (CALayer*)kj_setLayerImageContents:(UIImage *)image{ CALayer * imageLayer = [CALayer layer]; imageLayer.bounds = self.bounds; imageLayer.position = CGPointMake(self.bounds.size.width*.5, self.bounds.size.height*.5); imageLayer.contents = (id)image.CGImage; [self.layer addSublayer:imageLayer]; return imageLayer; } #pragma mark - function /// 播放图片 /// @param data 数据源 /// @param size 尺寸 /// @param provider 参数 NS_INLINE UIImage * kBannerPlayImage(NSData * data, CGSize size, id provider){ if (data == nil || data.length == 0) return nil; CGImageSourceRef imageSource = CGImageSourceCreateWithData(CFBridgingRetain(data), nil); size_t imageCount = CGImageSourceGetCount(imageSource); UIImage *animatedImage; if (imageCount == 1) { animatedImage = [[UIImage alloc] initWithData:data]; if (provider.webCropScale) { UIImage * scaleImage = kBannerEqualRatioCropImage(animatedImage, size); animatedImage = scaleImage; if (provider.webScaleImageBlock) { provider.webScaleImageBlock(animatedImage, scaleImage); } } } else { NSMutableArray *scaleImages = [NSMutableArray arrayWithCapacity:imageCount]; NSMutableArray *originalImages = [NSMutableArray arrayWithCapacity:imageCount]; NSTimeInterval time = 0; for (int i = 0; i < imageCount; i++) { CGImageRef cgImage = CGImageSourceCreateImageAtIndex(imageSource, i, nil); UIImage *originalImage = [UIImage imageWithCGImage:cgImage]; if (provider.webCropScale) { UIImage * scaleImage = kBannerEqualRatioCropImage(originalImage, size); originalImage = scaleImage; if (provider.webScaleImageBlock) [originalImages addObject:originalImage]; } [scaleImages addObject:originalImage]; CGImageRelease(cgImage); CFDictionaryRef const properties = CGImageSourceCopyPropertiesAtIndex(imageSource, i, NULL); CFDictionaryRef const GIFPros = CFDictionaryGetValue(properties, kCGImagePropertyGIFDictionary); NSNumber *duration = (__bridge id)CFDictionaryGetValue(GIFPros, kCGImagePropertyGIFUnclampedDelayTime); if (duration == NULL || [duration doubleValue] == 0) { duration = (__bridge id)CFDictionaryGetValue(GIFPros, kCGImagePropertyGIFDelayTime); } CFRelease(properties); time += duration.doubleValue; } animatedImage = [UIImage animatedImageWithImages:scaleImages duration:time]; if (provider.webCropScale && provider.webScaleImageBlock) { UIImage *originalImage = [UIImage animatedImageWithImages:originalImages duration:time]; provider.webScaleImageBlock(originalImage, animatedImage); } } CFRelease(imageSource); return animatedImage; } /// 获取图片 NS_INLINE void kBannerWebImageSetImage(void(^imageblock)(UIImage * image), NSData * data, CGSize size, id provider){ [[[NSOperationQueue alloc] init] addOperationWithBlock:^{ UIImage *image = kBannerPlayImage(data, size, provider); KJWebImageType type = kBannerImageContentType(data); if (provider.webPreRendering && type != KJWebImageTypeGIF) { UIGraphicsBeginImageContextWithOptions(image.size, YES, 0); [image drawInRect:CGRectMake(0, 0, image.size.width, image.size.height)]; image = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); } [[NSOperationQueue mainQueue] addOperationWithBlock:^{ imageblock ? imageblock(image) : nil; if (provider.webCompleted) { provider.webCompleted(type, image, data, nil); } }]; }]; } /// 下载图片 NS_INLINE void kBannerWebImageDownloader(NSURL * url, CGSize size, id provider, void(^imageblock)(UIImage *image)){ void (^kDownloaderAnalysis)(NSData *) = ^(NSData * __data){ if (__data == nil) return; kBannerWebImageSetImage(imageblock, __data, size, provider); if (provider.webCacheDatas) { [KJImageCache storeGIFData:__data Key:url.absoluteString]; } }; KJNetworkManager * downloader = [[KJNetworkManager alloc] init]; if (provider.webProgressBlock) { [downloader kj_startDownloadImageWithURL:url progress:^(KJBannerDownloadProgress * __progress) { provider.webProgressBlock(__progress); } complete:^(NSData * _Nullable data, NSError * _Nullable error) { if (error) { if (provider.webCompleted) { provider.webCompleted(KJWebImageTypeUnknown, nil, nil, error); } } else { kDownloaderAnalysis(data); } }]; } else { [downloader kj_startDownloadImageWithURL:url progress:nil complete:^(NSData * data, NSError * error) { if (error) { if (provider.webCompleted) { provider.webCompleted(KJWebImageTypeUnknown, nil, nil, error); } } else { kDownloaderAnalysis(data); } }]; } } /// 根据DATA判断图片类型 NS_INLINE KJWebImageType kBannerImageContentType(NSData * _Nonnull data){ uint8_t c; [data getBytes:&c length:1]; switch (c) { case 0xFF: return KJWebImageTypeJpeg; case 0x89: return KJWebImageTypePng; case 0x47: return KJWebImageTypeGIF; case 0x49: case 0x4D: return KJWebImageTypeTiff; case 0x52: if ([data length] < 12) return KJWebImageTypeUnknown; NSString *string = [[NSString alloc] initWithData:[data subdataWithRange:NSMakeRange(0, 12)] encoding:NSASCIIStringEncoding]; if ([string hasPrefix:@"RIFF"] && [string hasSuffix:@"WEBP"]) { return KJWebImageTypeWebp; } return KJWebImageTypeUnknown; } return KJWebImageTypeUnknown; } /// 等比改变图片尺寸 NS_INLINE UIImage * _Nullable kBannerEqualRatioCropImage(UIImage * _Nonnull image, CGSize size){ CGFloat scale = UIScreen.mainScreen.scale; float imgHeight = image.size.height; float imgWidth = image.size.width; float maxHeight = size.width * scale; float maxWidth = size.height * scale; if (imgHeight <= maxHeight && imgWidth <= maxWidth) return image; float imgRatio = imgWidth/imgHeight; float maxRatio = maxWidth/maxHeight; if (imgHeight > maxHeight || imgWidth > maxWidth) { if (imgRatio < maxRatio) { imgRatio = maxHeight / imgHeight; imgWidth = imgRatio * imgWidth; imgHeight = maxHeight; } else if (imgRatio > maxRatio) { imgRatio = maxWidth / imgWidth; imgWidth = maxWidth; imgHeight = imgRatio * imgHeight; } else { imgWidth = maxWidth; imgHeight = maxHeight; } } CGRect rect = CGRectMake(0.0, 0.0, imgWidth, imgHeight); UIGraphicsBeginImageContext(rect.size); [image drawInRect:rect]; UIImage *img = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return img; } #pragma maek - Associated - (UIImage *)webPlaceholder{ return objc_getAssociatedObject(self, _cmd); } - (void)setWebPlaceholder:(UIImage *)webPlaceholder{ objc_setAssociatedObject(self, @selector(webPlaceholder), webPlaceholder, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } - (KJWebImageCompleted)webCompleted{ return objc_getAssociatedObject(self, _cmd); } - (void)setWebCompleted:(KJWebImageCompleted)webCompleted{ objc_setAssociatedObject(self, @selector(webCompleted), webCompleted, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } - (KJLoadProgressBlock)webProgressBlock{ return objc_getAssociatedObject(self, _cmd); } - (void)setWebProgressBlock:(KJLoadProgressBlock)webProgressBlock{ objc_setAssociatedObject(self, @selector(webProgressBlock), webProgressBlock, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } - (KJWebScaleImageBlock)webScaleImageBlock{ return objc_getAssociatedObject(self, _cmd); } - (void)setWebScaleImageBlock:(KJWebScaleImageBlock)webScaleImageBlock{ objc_setAssociatedObject(self, @selector(webScaleImageBlock), webScaleImageBlock, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } - (bool)webCacheDatas{ return [objc_getAssociatedObject(self, _cmd) intValue]; } - (void)setWebCacheDatas:(bool)webCacheDatas{ objc_setAssociatedObject(self, @selector(webCacheDatas), @(webCacheDatas), OBJC_ASSOCIATION_ASSIGN); } - (bool)webCropScale{ return [objc_getAssociatedObject(self, _cmd) intValue]; } - (void)setWebCropScale:(bool)webCropScale{ objc_setAssociatedObject(self, @selector(webCropScale), @(webCropScale), OBJC_ASSOCIATION_ASSIGN); } - (bool)webPreRendering{ return [objc_getAssociatedObject(self, _cmd) intValue]; } - (void)setWebPreRendering:(bool)webPreRendering{ objc_setAssociatedObject(self, @selector(webPreRendering), @(webPreRendering), OBJC_ASSOCIATION_ASSIGN); } @end