├── .github
└── issue_template.md
├── .gitignore
├── .swift-version
├── .travis.yml
├── Assets
├── Gif
│ ├── _allen.gif
│ ├── _arc.gif
│ ├── _arrow.gif
│ ├── _circle.gif
│ ├── _dot.gif
│ ├── _native.gif
│ ├── _ring.gif
│ ├── _triangle.gif
│ ├── _woody.gif
│ ├── allen.gif
│ ├── arc.gif
│ ├── arrow.gif
│ ├── circle.gif
│ ├── dot.gif
│ ├── native.gif
│ ├── ring.gif
│ ├── triangle.gif
│ └── woody.gif
└── titleView.png
├── CREADME.md
├── Demo
├── .DS_Store
├── KafkaExample.xcodeproj
│ ├── project.pbxproj
│ ├── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ │ └── IDEWorkspaceChecks.plist
│ └── xcshareddata
│ │ └── xcschemes
│ │ └── KafkaExample.xcscheme
├── KafkaExample
│ ├── Assets.xcassets
│ │ ├── AppIcon.appiconset
│ │ │ ├── Contents.json
│ │ │ ├── logoiPadApp_76pt.png
│ │ │ ├── logoiPadApp_76pt@2x.png
│ │ │ ├── logoiPadNotifications_20pt.png
│ │ │ ├── logoiPadNotifications_20pt@2x.png
│ │ │ ├── logoiPadProApp_83.5pt@2x.png
│ │ │ ├── logoiPadSpootlight5_29pt.png
│ │ │ ├── logoiPadSpootlight5_29pt@2x.png
│ │ │ ├── logoiPadSpootlight7_40pt.png
│ │ │ ├── logoiPadSpootlight7_40pt@2x.png
│ │ │ ├── logoiPhoneApp_60pt@2x.png
│ │ │ ├── logoiPhoneApp_60pt@3x.png
│ │ │ ├── logoiPhoneNotification_20pt@2x.png
│ │ │ ├── logoiPhoneNotification_20pt@3x.png
│ │ │ ├── logoiPhoneSpootlight5_29pt@2x.png
│ │ │ ├── logoiPhoneSpootlight5_29pt@3x.png
│ │ │ ├── logoiPhoneSpootlight7_40pt@2x.png
│ │ │ ├── logoiPhoneSpootlight7_40pt@3x.png
│ │ │ └── logostore_1024pt.png
│ │ ├── Contents.json
│ │ ├── LaunchImage.launchimage
│ │ │ ├── Contents.json
│ │ │ ├── launchiPadPortraitWOSBiOS56_768x1004pt.png
│ │ │ ├── launchiPadPortraitWOSBiOS56_768x1004pt@2x.png
│ │ │ ├── launchiPadPortraitiOS56_768x1024pt.png
│ │ │ ├── launchiPadPortraitiOS56_768x1024pt@2x.png
│ │ │ ├── launchiPadPortraitiOS789_768x1024pt.png
│ │ │ ├── launchiPadPortraitiOS789_768x1024pt@2x.png
│ │ │ ├── launchiPhonePortraitiOS56_320x480pt.png
│ │ │ ├── launchiPhonePortraitiOS56_320x480pt@2x.png
│ │ │ ├── launchiPhonePortraitiOS56_320x568pt@2x.png
│ │ │ ├── launchiPhonePortraitiOS789_320x480pt@2x.png
│ │ │ ├── launchiPhonePortraitiOS789_320x568pt@2x.png
│ │ │ ├── launchiPhonePortraitiOS89_375x667pt@2x.png
│ │ │ ├── launchiPhonePortraitiOS89_414x736pt@3x.png
│ │ │ └── launchiPhoneXPortraitiOS11_375x812pt@3x.png
│ │ └── icon.imageset
│ │ │ ├── Contents.json
│ │ │ └── icon.png
│ ├── Base.lproj
│ │ └── Main.storyboard
│ ├── Controllers
│ │ ├── KafkaCollectionViewController.h
│ │ ├── KafkaCollectionViewController.m
│ │ ├── KafkaCustomTableViewController.h
│ │ ├── KafkaCustomTableViewController.m
│ │ ├── KafkaMainListController.h
│ │ ├── KafkaMainListController.m
│ │ ├── KafkaScrollViewController.h
│ │ ├── KafkaScrollViewController.m
│ │ ├── KafkaTableViewController.h
│ │ └── KafkaTableViewController.m
│ ├── Info.plist
│ ├── KafkaAppDelegate.h
│ ├── KafkaAppDelegate.m
│ ├── KafkaExample.entitlements
│ ├── en.lproj
│ │ └── InfoPlist.strings
│ ├── main.m
│ └── zh-Hans.lproj
│ │ └── InfoPlist.strings
├── KafkaExampleTests
│ ├── Info.plist
│ └── KafkaExampleTests.m
└── KafkaExampleUITests
│ ├── Info.plist
│ └── KafkaExampleUITests.m
├── KafkaRefresh.podspec
├── KafkaRefresh
├── Category
│ ├── KafkaCategories.h
│ └── KafkaCategories.m
├── Configuration
│ ├── UIScrollView+KafkaConfiguration.h
│ └── UIScrollView+KafkaConfiguration.m
├── Core
│ ├── KafkaFootRefreshControl.h
│ ├── KafkaFootRefreshControl.m
│ ├── KafkaHeadRefreshControl.h
│ ├── KafkaHeadRefreshControl.m
│ ├── KafkaRefreshControl.h
│ ├── KafkaRefreshControl.m
│ ├── KafkaRefreshProtocol.h
│ ├── UIScrollView+KafkaRefreshControl.h
│ └── UIScrollView+KafkaRefreshControl.m
├── Default
│ ├── KafkaRefreshDefaults.h
│ └── KafkaRefreshDefaults.m
├── KafkaRefresh.h
├── Resource
│ └── Image.bundle
│ │ └── arrow48.png
├── Style
│ └── KafkaRefreshStyle.h
└── UIKit
│ ├── FootKit
│ ├── KafkaArrowFooter.h
│ ├── KafkaArrowFooter.m
│ ├── KafkaNativeFooter.h
│ ├── KafkaNativeFooter.m
│ ├── KafkaReplicatorFooter.h
│ ├── KafkaReplicatorFooter.m
│ ├── KafkaRingIndicatorFooter.h
│ └── KafkaRingIndicatorFooter.m
│ ├── HeadKit
│ ├── KafkaArrowHeader.h
│ ├── KafkaArrowHeader.m
│ ├── KafkaNativeHeader.h
│ ├── KafkaNativeHeader.m
│ ├── KafkaReplicatorHeader.h
│ ├── KafkaReplicatorHeader.m
│ ├── KafkaRingIndicatorHeader.h
│ └── KafkaRingIndicatorHeader.m
│ └── LayerKit
│ ├── KafkaAnimatableProtocol.h
│ ├── KafkaArcLayer.h
│ ├── KafkaArcLayer.m
│ ├── KafkaReplicatorLayer.h
│ └── KafkaReplicatorLayer.m
├── LICENSE
├── LICENSE.txt
├── README.md
└── _config.yml
/.github/issue_template.md:
--------------------------------------------------------------------------------
1 | !!! 提交bug前:
2 |
3 | - 请定位该bug是否由自身代码引发,当该bug模糊不清时,您应该继续思索;
4 | - 如果您发现该bug由KafkaRefresh引发,且您能修改源码解决,欢迎您提交解决方案或提交一个request,在此先表示感谢。
5 |
6 | 提交bug必须包含下列要点:
7 |
8 | - iOS版本;
9 | - KafkaRefresh版本(可查看cocoapods中提示的版本信息);
10 | - 请您务必给出详细的重现步骤。
11 |
12 |
13 | # ☕️ [赞助](https://github.com/OpenFeyn/KafkaRefresh/blob/master/sponsor.jpg)
14 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | #
3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
4 |
5 | ## Build generated
6 | build/
7 | DerivedData/
8 |
9 | ## Various settings
10 | *.pbxuser
11 | !default.pbxuser
12 | *.mode1v3
13 | !default.mode1v3
14 | *.mode2v3
15 | !default.mode2v3
16 | *.perspectivev3
17 | !default.perspectivev3
18 | xcuserdata/
19 |
20 | ## Other
21 | *.moved-aside
22 | *.xccheckout
23 | *.xcscmblueprint
24 |
25 | ## Obj-C/Swift specific
26 | *.hmap
27 | *.ipa
28 | *.dSYM.zip
29 | *.dSYM
30 |
31 | # CocoaPods
32 | #
33 | # We recommend against adding the Pods directory to your .gitignore. However
34 | # you should judge for yourself, the pros and cons are mentioned at:
35 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
36 | #
37 | # Pods/
38 |
39 | # Carthage
40 | #
41 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
42 | # Carthage/Checkouts
43 |
44 | Carthage/Build
45 |
46 | # fastlane
47 | #
48 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
49 | # screenshots whenever they are needed.
50 | # For more information about the recommended setup visit:
51 | # https://docs.fastlane.tools/best-practices/source-control/#source-control
52 |
53 | fastlane/report.xml
54 | fastlane/Preview.html
55 | fastlane/screenshots
56 | fastlane/test_output
57 |
58 | # Code Injection
59 | #
60 | # After new code Injection tools there's a generated folder /iOSInjectionProject
61 | # https://github.com/johnno1962/injectionforxcode
62 |
63 | iOSInjectionProject/
64 |
--------------------------------------------------------------------------------
/.swift-version:
--------------------------------------------------------------------------------
1 | 2.3
2 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | osx_image: xcode9
2 | language: objective-c
3 | xcode_project: Demo/KafkaExample.xcodeproj
4 | xcode_scheme: KafkaExample
5 |
6 |
7 | before_install:
8 | - env
9 | - xcodebuild -version
10 | - xcodebuild -showsdks
11 | - xcpretty --version
12 |
13 | script:
14 | - xcodebuild clean build -project "$TRAVIS_XCODE_PROJECT" -scheme "$TRAVIS_XCODE_SCHEME" | xcpretty
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/Assets/Gif/_allen.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BeatsKitano/KafkaRefresh/7d3bb0a5ce8d6ab11e976398cff8e52332f7ff63/Assets/Gif/_allen.gif
--------------------------------------------------------------------------------
/Assets/Gif/_arc.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BeatsKitano/KafkaRefresh/7d3bb0a5ce8d6ab11e976398cff8e52332f7ff63/Assets/Gif/_arc.gif
--------------------------------------------------------------------------------
/Assets/Gif/_arrow.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BeatsKitano/KafkaRefresh/7d3bb0a5ce8d6ab11e976398cff8e52332f7ff63/Assets/Gif/_arrow.gif
--------------------------------------------------------------------------------
/Assets/Gif/_circle.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BeatsKitano/KafkaRefresh/7d3bb0a5ce8d6ab11e976398cff8e52332f7ff63/Assets/Gif/_circle.gif
--------------------------------------------------------------------------------
/Assets/Gif/_dot.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BeatsKitano/KafkaRefresh/7d3bb0a5ce8d6ab11e976398cff8e52332f7ff63/Assets/Gif/_dot.gif
--------------------------------------------------------------------------------
/Assets/Gif/_native.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BeatsKitano/KafkaRefresh/7d3bb0a5ce8d6ab11e976398cff8e52332f7ff63/Assets/Gif/_native.gif
--------------------------------------------------------------------------------
/Assets/Gif/_ring.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BeatsKitano/KafkaRefresh/7d3bb0a5ce8d6ab11e976398cff8e52332f7ff63/Assets/Gif/_ring.gif
--------------------------------------------------------------------------------
/Assets/Gif/_triangle.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BeatsKitano/KafkaRefresh/7d3bb0a5ce8d6ab11e976398cff8e52332f7ff63/Assets/Gif/_triangle.gif
--------------------------------------------------------------------------------
/Assets/Gif/_woody.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BeatsKitano/KafkaRefresh/7d3bb0a5ce8d6ab11e976398cff8e52332f7ff63/Assets/Gif/_woody.gif
--------------------------------------------------------------------------------
/Assets/Gif/allen.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BeatsKitano/KafkaRefresh/7d3bb0a5ce8d6ab11e976398cff8e52332f7ff63/Assets/Gif/allen.gif
--------------------------------------------------------------------------------
/Assets/Gif/arc.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BeatsKitano/KafkaRefresh/7d3bb0a5ce8d6ab11e976398cff8e52332f7ff63/Assets/Gif/arc.gif
--------------------------------------------------------------------------------
/Assets/Gif/arrow.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BeatsKitano/KafkaRefresh/7d3bb0a5ce8d6ab11e976398cff8e52332f7ff63/Assets/Gif/arrow.gif
--------------------------------------------------------------------------------
/Assets/Gif/circle.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BeatsKitano/KafkaRefresh/7d3bb0a5ce8d6ab11e976398cff8e52332f7ff63/Assets/Gif/circle.gif
--------------------------------------------------------------------------------
/Assets/Gif/dot.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BeatsKitano/KafkaRefresh/7d3bb0a5ce8d6ab11e976398cff8e52332f7ff63/Assets/Gif/dot.gif
--------------------------------------------------------------------------------
/Assets/Gif/native.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BeatsKitano/KafkaRefresh/7d3bb0a5ce8d6ab11e976398cff8e52332f7ff63/Assets/Gif/native.gif
--------------------------------------------------------------------------------
/Assets/Gif/ring.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BeatsKitano/KafkaRefresh/7d3bb0a5ce8d6ab11e976398cff8e52332f7ff63/Assets/Gif/ring.gif
--------------------------------------------------------------------------------
/Assets/Gif/triangle.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BeatsKitano/KafkaRefresh/7d3bb0a5ce8d6ab11e976398cff8e52332f7ff63/Assets/Gif/triangle.gif
--------------------------------------------------------------------------------
/Assets/Gif/woody.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BeatsKitano/KafkaRefresh/7d3bb0a5ce8d6ab11e976398cff8e52332f7ff63/Assets/Gif/woody.gif
--------------------------------------------------------------------------------
/Assets/titleView.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BeatsKitano/KafkaRefresh/7d3bb0a5ce8d6ab11e976398cff8e52332f7ff63/Assets/titleView.png
--------------------------------------------------------------------------------
/CREADME.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
KafkaRefresh
7 |
8 | 内置多种动画、可自定义和灵活的iOS下拉刷新框架。
9 |
10 |
11 | Bug提交
12 | ·
13 | 需求提交
14 |
15 |
16 |
17 |
18 | ### Status
19 |
20 | [](https://github.com/xorshine/KafkaRefresh/blob/master/LICENSE)
21 | [](https://img.shields.io/cocoapods/v/KafkaRefresh.svg)
22 | 
23 | 
24 | [](mailto:xorshine@icloud.com)
25 |
26 | ### Screenshots
27 |
28 |
29 | KafkaRefreshStyle |
30 | Top Screenshots |
31 | Bottom Screenshots |
32 |
33 |
34 | Native |
35 |  |
36 |  |
37 |
38 |
39 | ReplicatorWoody |
40 |  |
41 |  |
42 |
43 |
44 | ReplicatorAllen |
45 |  |
46 |  |
47 |
48 |
49 | ReplicatorCircle |
50 |  |
51 |  |
52 |
53 |
54 | ReplicatorDot |
55 |  |
56 |  |
57 |
58 |
59 | ReplicatorArc |
60 |  |
61 |  |
62 |
63 |
64 | ReplicatorTriangle |
65 |  |
66 |  |
67 |
68 |
69 | AnimatableRing |
70 |  |
71 |  |
72 |
73 |
74 | AnimatableArrow |
75 |  |
76 |  |
77 |
78 |
79 |
80 |
81 | ### 关于新版2.0
82 |
83 | * 开源KafkaRefresh纯属自娱自乐。那时较闲。KafkaRefresh有很大不足,如:更细粒度的视觉动画、更规范的API命名、支持Swift、支持更多的自定义...等等。无奈的是,手头事情之多,使之暂无闲心更新2.0。怕是要很久之后吧。
84 |
85 | * 有人问为何要以Kafka命名,Apache基金会开源过一款分布式消息系统也叫做Kafka,KafkaRefresh跟Apache Kafka没任何关系。当时不知道,即使知道,还是会叫KafkaRefresh。其实是个人喜欢的一个作家叫Kafka,KafkaRefresh的内置样式也用了一些我喜欢的人的名字命名,这没什么意义,或许是为了当时的方便罢了。
86 |
87 | ### 不支持嵌套的ScrollView
88 |
89 | ### 特点
90 | * 支持多样式选择与自定义
91 |
92 | > 内置9种动画样式
93 |
94 | * 非刷新状态自动隐藏
95 |
96 | > 即使手动调整过contentInset,依然能够在非刷新状态自动隐藏影。最常见的情况是:当数据量过少,UITableView停止刷新后,用户依旧能看到刷新控件的存在,从而影响的视觉体验。KafkaRefresh首次解决了该问题。
97 |
98 | * 刷新结束时抗抖动
99 |
100 | > 当UIScrollView处于刷新状态,且用户滑动UIScrollView,当刷新结束时,KafkaRefresh不会调整UIScrollView的内容,从而导致页面跳动;
101 |
102 | * 支持设置控件高度
103 |
104 | > `stretchOffsetYAxisThreshold`是根据刷新控件的高度进行的比例调整。如:当设置`stretchOffsetYAxisThreshold`为1.5时,触发刷新的偏移距离将调整为原来的1.5倍。
105 |
106 | * 支持全局配置
107 |
108 | > `KafkaRefreshDefaults`类似一个配置表,通过该配置表配置全局的刷新样式,而无需在每一个页面初始化或者绑定刷新控件。
109 |
110 | * 支持进度回调
111 |
112 | > 实时回调拖拽的偏移比例,对于扩展接口,可根据进度调整动画。该接口的开放可用于扩展更多的刷新东亚样式。
113 |
114 | * 自适应contentInset系统调整与手动调整
115 |
116 | > 自适应iOS7以后UINavigationController自动调整scrollview contentOffset,KafkaRefresh也对iOS 11进行了适配;当您手动设置了contentInset的值,也无需担心KafkaRefresh会影响到视觉效果。
117 |
118 | * 解决刷新状态分组视图悬停问题
119 |
120 | > 即使在列表滑动时,分组视图都将跟随ScrollView滑动(即使处于高速滑动状态下!)。
121 |
122 | * 支持预加载
123 |
124 | 当用户滑动scrollview接近至底部时,将会自动触发刷新,无需用户再滑至底部后拉起scrollview。该功能默认不开启,因为多数人不查看文档便欣然使用改功能,不正确使用极容易引发刷新无法停止。
125 |
126 | 使用预加载功能,请严格按照下面要求使用:
127 |
128 | * ```self.tableView.footRefreshControl.autoRefreshOnFoot = YES;``` 请手动将刷新该属性至为TRUE;
129 |
130 | * 在刷新调用的block块中,严格按下列逻辑编写:
131 |
132 | ```objective-c
133 | if (没有数据需要拼接了) {
134 | [weakSelf.tableView.footRefreshControl endRefreshingAndNoLongerRefreshingWithAlertText:@"no more"];
135 | } else {
136 | [weakSelf.tableView.footRefreshControl endRefreshingWithAlertText:@"Did load successfully" completion:nil];
137 | }
138 | ```
139 |
140 | * 文档覆盖率100%、支持横竖屏切换自适应、iOS 7+。
141 |
142 |
143 | ### 安装
144 | * CocoaPods
145 | ```ruby
146 | pod 'KafkaRefresh'
147 | ```
148 |
149 | * Carthage
150 |
151 | > 如果您想通过*carthage*安装 , 请您申请一个pull request.
152 |
153 | ### 使用
154 |
155 | ##### 1.引入头文件
156 | ```objective-c
157 | #import "KafkaRefresh.h"
158 | ```
159 |
160 | ##### 2.初始化控件
161 | * 方式一
162 | ```objective-c
163 | #pragma mark - head
164 |
165 | [self.tableView bindHeadRefreshHandler:^{
166 |
167 | } themeColor:MainColor refreshStyle:KafkaRefreshStyleAnimatableArrow];
168 |
169 | #pragma mark - foot
170 |
171 | [self.tableView bindFootRefreshHandler:^{
172 |
173 | } themeColor:MainColor refreshStyle:KafkaRefreshStyleAnimatableArrow];
174 |
175 | #pragma mark - auto refresh
176 |
177 | self.tableView.footRefreshControl.autoRefreshOnFoot = YES;
178 | ```
179 | * 方式二
180 | ```objective-c
181 | KafkaArrowHeader * arrow = [[KafkaArrowHeader alloc] init];
182 | arrow.refreshHandler = ^{
183 | //.....
184 | };
185 | self.tableView.headRefreshControl = arrow;
186 | ```
187 |
188 | * 方式三 全局配置
189 | ```objective-c
190 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
191 | {
192 | [[KafkaRefreshDefaults standardRefreshDefaults] setHeaderDefaultStyle:KafkaRefreshStyleAnimatableRing];
193 | return YES;
194 | }
195 |
196 | #pragma mark - global
197 |
198 | [self.tableView bindGlobalStyleForFootRefreshHandler:^{
199 |
200 | }];
201 |
202 | ```
203 | ##### 3.手动触发刷新
204 | ```objective-c
205 | [self.tableView.headRefreshControl beginRefreshing];
206 | [self.tableView.footRefreshControl beginRefreshing];
207 | ```
208 |
209 | ##### 4.结束刷新
210 | ```objective-c
211 |
212 | /*
213 | 一般方式结束刷新
214 | */
215 | - (void)endRefreshing;
216 |
217 | /*
218 | 结束刷新且需要提示文字
219 | */
220 | - (void)endRefreshingWithAlertText:(NSString *)text completion:(dispatch_block_t)completion;
221 |
222 | /*
223 | 结束刷新且不再需要刷新功能
224 | */
225 | - (void)endRefreshingAndNoLongerRefreshingWithAlertText:(NSString *)text;
226 | ```
227 |
228 | ##### 5.重新恢复刷新
229 |
230 | ```objective-c
231 | /**
232 | 当调用过 ‘endRefreshingAndNoLongerRefreshingWithAlertText’,
233 | 且重新需要恢复刷新功能室,调用下面方法
234 | */
235 | - (void)resumeRefreshAvailable;
236 | ```
237 |
238 | ### 自定义
239 |
240 | 以KafkaheadRefreshControl为例:
241 | ```objective-c
242 | #import "KafkaheadRefreshControl.h"
243 | @interface CustomHeader : KafkafootRefreshControl
244 | @end
245 | ```
246 | ```objective-c
247 | @implementation CustomHeader
248 |
249 | - (void)setupProperties
250 | {
251 | [super setupProperties];
252 | //初始化属性
253 | }
254 |
255 | - (void)kafkaDidScrollWithProgress:(CGFloat)progress max:(const CGFloat)max
256 | {
257 | //进度回调
258 | }
259 |
260 | - (void)kafkaRefreshStateDidChange:(KafkaRefreshState)state
261 | {
262 | [super kafkaRefreshStateDidChange:state];
263 | }
264 | @end
265 | ```
266 |
267 | ### FAQ
268 |
269 | * 遇到很多人咨询这样一个问题:上拉加载数据时,调用 `insertRowsAtIndexPaths: withAnimation:`拼接数据,发现tableView刷新过程发生抖动,位置发生偏移。首先申明!这不是KafkaRefresh的原因。如果想要解决这个问题,请设置`tableView.estimatedRowHeight = UITableViewAutomaticDimension;`.
270 | * 关于Bug的提交,请您一定要记住!应该提交基本信息!当前版本号,重现步骤(请不要过于相信您的中文能力,您写一句话我未必能看懂。)
271 |
272 | ### 交流
273 |
274 | > 1. 如需要帮助,请邮件
275 | > 2. 个人精力有限,KafkaRefresh开放的接口足够去扩展更丰富的UI效果,欢迎您参与,并提交pull request
276 |
277 | ### 协议
278 | > KafkaRefresh采用MIT开源协议。
279 |
--------------------------------------------------------------------------------
/Demo/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BeatsKitano/KafkaRefresh/7d3bb0a5ce8d6ab11e976398cff8e52332f7ff63/Demo/.DS_Store
--------------------------------------------------------------------------------
/Demo/KafkaExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Demo/KafkaExample.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Demo/KafkaExample.xcodeproj/xcshareddata/xcschemes/KafkaExample.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
29 |
35 |
36 |
37 |
43 |
49 |
50 |
51 |
52 |
53 |
59 |
60 |
62 |
68 |
69 |
70 |
72 |
78 |
79 |
80 |
81 |
82 |
88 |
89 |
90 |
91 |
92 |
93 |
104 |
106 |
112 |
113 |
114 |
115 |
116 |
117 |
123 |
125 |
131 |
132 |
133 |
134 |
136 |
137 |
140 |
141 |
142 |
--------------------------------------------------------------------------------
/Demo/KafkaExample/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "size" : "20x20",
5 | "idiom" : "iphone",
6 | "filename" : "logoiPhoneNotification_20pt@2x.png",
7 | "scale" : "2x"
8 | },
9 | {
10 | "size" : "20x20",
11 | "idiom" : "iphone",
12 | "filename" : "logoiPhoneNotification_20pt@3x.png",
13 | "scale" : "3x"
14 | },
15 | {
16 | "size" : "29x29",
17 | "idiom" : "iphone",
18 | "filename" : "logoiPhoneSpootlight5_29pt@2x.png",
19 | "scale" : "2x"
20 | },
21 | {
22 | "size" : "29x29",
23 | "idiom" : "iphone",
24 | "filename" : "logoiPhoneSpootlight5_29pt@3x.png",
25 | "scale" : "3x"
26 | },
27 | {
28 | "size" : "40x40",
29 | "idiom" : "iphone",
30 | "filename" : "logoiPhoneSpootlight7_40pt@2x.png",
31 | "scale" : "2x"
32 | },
33 | {
34 | "size" : "40x40",
35 | "idiom" : "iphone",
36 | "filename" : "logoiPhoneSpootlight7_40pt@3x.png",
37 | "scale" : "3x"
38 | },
39 | {
40 | "size" : "60x60",
41 | "idiom" : "iphone",
42 | "filename" : "logoiPhoneApp_60pt@2x.png",
43 | "scale" : "2x"
44 | },
45 | {
46 | "size" : "60x60",
47 | "idiom" : "iphone",
48 | "filename" : "logoiPhoneApp_60pt@3x.png",
49 | "scale" : "3x"
50 | },
51 | {
52 | "size" : "20x20",
53 | "idiom" : "ipad",
54 | "filename" : "logoiPadNotifications_20pt.png",
55 | "scale" : "1x"
56 | },
57 | {
58 | "size" : "20x20",
59 | "idiom" : "ipad",
60 | "filename" : "logoiPadNotifications_20pt@2x.png",
61 | "scale" : "2x"
62 | },
63 | {
64 | "size" : "29x29",
65 | "idiom" : "ipad",
66 | "filename" : "logoiPadSpootlight5_29pt.png",
67 | "scale" : "1x"
68 | },
69 | {
70 | "size" : "29x29",
71 | "idiom" : "ipad",
72 | "filename" : "logoiPadSpootlight5_29pt@2x.png",
73 | "scale" : "2x"
74 | },
75 | {
76 | "size" : "40x40",
77 | "idiom" : "ipad",
78 | "filename" : "logoiPadSpootlight7_40pt.png",
79 | "scale" : "1x"
80 | },
81 | {
82 | "size" : "40x40",
83 | "idiom" : "ipad",
84 | "filename" : "logoiPadSpootlight7_40pt@2x.png",
85 | "scale" : "2x"
86 | },
87 | {
88 | "size" : "76x76",
89 | "idiom" : "ipad",
90 | "filename" : "logoiPadApp_76pt.png",
91 | "scale" : "1x"
92 | },
93 | {
94 | "size" : "76x76",
95 | "idiom" : "ipad",
96 | "filename" : "logoiPadApp_76pt@2x.png",
97 | "scale" : "2x"
98 | },
99 | {
100 | "size" : "83.5x83.5",
101 | "idiom" : "ipad",
102 | "filename" : "logoiPadProApp_83.5pt@2x.png",
103 | "scale" : "2x"
104 | },
105 | {
106 | "size" : "1024x1024",
107 | "idiom" : "ios-marketing",
108 | "filename" : "logostore_1024pt.png",
109 | "scale" : "1x"
110 | }
111 | ],
112 | "info" : {
113 | "version" : 1,
114 | "author" : "xcode"
115 | }
116 | }
--------------------------------------------------------------------------------
/Demo/KafkaExample/Assets.xcassets/AppIcon.appiconset/logoiPadApp_76pt.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BeatsKitano/KafkaRefresh/7d3bb0a5ce8d6ab11e976398cff8e52332f7ff63/Demo/KafkaExample/Assets.xcassets/AppIcon.appiconset/logoiPadApp_76pt.png
--------------------------------------------------------------------------------
/Demo/KafkaExample/Assets.xcassets/AppIcon.appiconset/logoiPadApp_76pt@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BeatsKitano/KafkaRefresh/7d3bb0a5ce8d6ab11e976398cff8e52332f7ff63/Demo/KafkaExample/Assets.xcassets/AppIcon.appiconset/logoiPadApp_76pt@2x.png
--------------------------------------------------------------------------------
/Demo/KafkaExample/Assets.xcassets/AppIcon.appiconset/logoiPadNotifications_20pt.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BeatsKitano/KafkaRefresh/7d3bb0a5ce8d6ab11e976398cff8e52332f7ff63/Demo/KafkaExample/Assets.xcassets/AppIcon.appiconset/logoiPadNotifications_20pt.png
--------------------------------------------------------------------------------
/Demo/KafkaExample/Assets.xcassets/AppIcon.appiconset/logoiPadNotifications_20pt@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BeatsKitano/KafkaRefresh/7d3bb0a5ce8d6ab11e976398cff8e52332f7ff63/Demo/KafkaExample/Assets.xcassets/AppIcon.appiconset/logoiPadNotifications_20pt@2x.png
--------------------------------------------------------------------------------
/Demo/KafkaExample/Assets.xcassets/AppIcon.appiconset/logoiPadProApp_83.5pt@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BeatsKitano/KafkaRefresh/7d3bb0a5ce8d6ab11e976398cff8e52332f7ff63/Demo/KafkaExample/Assets.xcassets/AppIcon.appiconset/logoiPadProApp_83.5pt@2x.png
--------------------------------------------------------------------------------
/Demo/KafkaExample/Assets.xcassets/AppIcon.appiconset/logoiPadSpootlight5_29pt.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BeatsKitano/KafkaRefresh/7d3bb0a5ce8d6ab11e976398cff8e52332f7ff63/Demo/KafkaExample/Assets.xcassets/AppIcon.appiconset/logoiPadSpootlight5_29pt.png
--------------------------------------------------------------------------------
/Demo/KafkaExample/Assets.xcassets/AppIcon.appiconset/logoiPadSpootlight5_29pt@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BeatsKitano/KafkaRefresh/7d3bb0a5ce8d6ab11e976398cff8e52332f7ff63/Demo/KafkaExample/Assets.xcassets/AppIcon.appiconset/logoiPadSpootlight5_29pt@2x.png
--------------------------------------------------------------------------------
/Demo/KafkaExample/Assets.xcassets/AppIcon.appiconset/logoiPadSpootlight7_40pt.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BeatsKitano/KafkaRefresh/7d3bb0a5ce8d6ab11e976398cff8e52332f7ff63/Demo/KafkaExample/Assets.xcassets/AppIcon.appiconset/logoiPadSpootlight7_40pt.png
--------------------------------------------------------------------------------
/Demo/KafkaExample/Assets.xcassets/AppIcon.appiconset/logoiPadSpootlight7_40pt@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BeatsKitano/KafkaRefresh/7d3bb0a5ce8d6ab11e976398cff8e52332f7ff63/Demo/KafkaExample/Assets.xcassets/AppIcon.appiconset/logoiPadSpootlight7_40pt@2x.png
--------------------------------------------------------------------------------
/Demo/KafkaExample/Assets.xcassets/AppIcon.appiconset/logoiPhoneApp_60pt@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BeatsKitano/KafkaRefresh/7d3bb0a5ce8d6ab11e976398cff8e52332f7ff63/Demo/KafkaExample/Assets.xcassets/AppIcon.appiconset/logoiPhoneApp_60pt@2x.png
--------------------------------------------------------------------------------
/Demo/KafkaExample/Assets.xcassets/AppIcon.appiconset/logoiPhoneApp_60pt@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BeatsKitano/KafkaRefresh/7d3bb0a5ce8d6ab11e976398cff8e52332f7ff63/Demo/KafkaExample/Assets.xcassets/AppIcon.appiconset/logoiPhoneApp_60pt@3x.png
--------------------------------------------------------------------------------
/Demo/KafkaExample/Assets.xcassets/AppIcon.appiconset/logoiPhoneNotification_20pt@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BeatsKitano/KafkaRefresh/7d3bb0a5ce8d6ab11e976398cff8e52332f7ff63/Demo/KafkaExample/Assets.xcassets/AppIcon.appiconset/logoiPhoneNotification_20pt@2x.png
--------------------------------------------------------------------------------
/Demo/KafkaExample/Assets.xcassets/AppIcon.appiconset/logoiPhoneNotification_20pt@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BeatsKitano/KafkaRefresh/7d3bb0a5ce8d6ab11e976398cff8e52332f7ff63/Demo/KafkaExample/Assets.xcassets/AppIcon.appiconset/logoiPhoneNotification_20pt@3x.png
--------------------------------------------------------------------------------
/Demo/KafkaExample/Assets.xcassets/AppIcon.appiconset/logoiPhoneSpootlight5_29pt@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BeatsKitano/KafkaRefresh/7d3bb0a5ce8d6ab11e976398cff8e52332f7ff63/Demo/KafkaExample/Assets.xcassets/AppIcon.appiconset/logoiPhoneSpootlight5_29pt@2x.png
--------------------------------------------------------------------------------
/Demo/KafkaExample/Assets.xcassets/AppIcon.appiconset/logoiPhoneSpootlight5_29pt@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BeatsKitano/KafkaRefresh/7d3bb0a5ce8d6ab11e976398cff8e52332f7ff63/Demo/KafkaExample/Assets.xcassets/AppIcon.appiconset/logoiPhoneSpootlight5_29pt@3x.png
--------------------------------------------------------------------------------
/Demo/KafkaExample/Assets.xcassets/AppIcon.appiconset/logoiPhoneSpootlight7_40pt@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BeatsKitano/KafkaRefresh/7d3bb0a5ce8d6ab11e976398cff8e52332f7ff63/Demo/KafkaExample/Assets.xcassets/AppIcon.appiconset/logoiPhoneSpootlight7_40pt@2x.png
--------------------------------------------------------------------------------
/Demo/KafkaExample/Assets.xcassets/AppIcon.appiconset/logoiPhoneSpootlight7_40pt@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BeatsKitano/KafkaRefresh/7d3bb0a5ce8d6ab11e976398cff8e52332f7ff63/Demo/KafkaExample/Assets.xcassets/AppIcon.appiconset/logoiPhoneSpootlight7_40pt@3x.png
--------------------------------------------------------------------------------
/Demo/KafkaExample/Assets.xcassets/AppIcon.appiconset/logostore_1024pt.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BeatsKitano/KafkaRefresh/7d3bb0a5ce8d6ab11e976398cff8e52332f7ff63/Demo/KafkaExample/Assets.xcassets/AppIcon.appiconset/logostore_1024pt.png
--------------------------------------------------------------------------------
/Demo/KafkaExample/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/Demo/KafkaExample/Assets.xcassets/LaunchImage.launchimage/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "extent" : "full-screen",
5 | "idiom" : "iphone",
6 | "subtype" : "2436h",
7 | "filename" : "launchiPhoneXPortraitiOS11_375x812pt@3x.png",
8 | "minimum-system-version" : "11.0",
9 | "orientation" : "portrait",
10 | "scale" : "3x"
11 | },
12 | {
13 | "extent" : "full-screen",
14 | "idiom" : "iphone",
15 | "subtype" : "736h",
16 | "filename" : "launchiPhonePortraitiOS89_414x736pt@3x.png",
17 | "minimum-system-version" : "8.0",
18 | "orientation" : "portrait",
19 | "scale" : "3x"
20 | },
21 | {
22 | "extent" : "full-screen",
23 | "idiom" : "iphone",
24 | "subtype" : "667h",
25 | "filename" : "launchiPhonePortraitiOS89_375x667pt@2x.png",
26 | "minimum-system-version" : "8.0",
27 | "orientation" : "portrait",
28 | "scale" : "2x"
29 | },
30 | {
31 | "orientation" : "portrait",
32 | "idiom" : "iphone",
33 | "filename" : "launchiPhonePortraitiOS789_320x480pt@2x.png",
34 | "extent" : "full-screen",
35 | "minimum-system-version" : "7.0",
36 | "scale" : "2x"
37 | },
38 | {
39 | "extent" : "full-screen",
40 | "idiom" : "iphone",
41 | "subtype" : "retina4",
42 | "filename" : "launchiPhonePortraitiOS789_320x568pt@2x.png",
43 | "minimum-system-version" : "7.0",
44 | "orientation" : "portrait",
45 | "scale" : "2x"
46 | },
47 | {
48 | "orientation" : "portrait",
49 | "idiom" : "ipad",
50 | "filename" : "launchiPadPortraitiOS789_768x1024pt.png",
51 | "extent" : "full-screen",
52 | "minimum-system-version" : "7.0",
53 | "scale" : "1x"
54 | },
55 | {
56 | "orientation" : "portrait",
57 | "idiom" : "ipad",
58 | "filename" : "launchiPadPortraitiOS789_768x1024pt@2x.png",
59 | "extent" : "full-screen",
60 | "minimum-system-version" : "7.0",
61 | "scale" : "2x"
62 | },
63 | {
64 | "orientation" : "portrait",
65 | "idiom" : "iphone",
66 | "filename" : "launchiPhonePortraitiOS56_320x480pt.png",
67 | "extent" : "full-screen",
68 | "scale" : "1x"
69 | },
70 | {
71 | "orientation" : "portrait",
72 | "idiom" : "iphone",
73 | "filename" : "launchiPhonePortraitiOS56_320x480pt@2x.png",
74 | "extent" : "full-screen",
75 | "scale" : "2x"
76 | },
77 | {
78 | "orientation" : "portrait",
79 | "idiom" : "iphone",
80 | "filename" : "launchiPhonePortraitiOS56_320x568pt@2x.png",
81 | "extent" : "full-screen",
82 | "subtype" : "retina4",
83 | "scale" : "2x"
84 | },
85 | {
86 | "orientation" : "portrait",
87 | "idiom" : "ipad",
88 | "filename" : "launchiPadPortraitWOSBiOS56_768x1004pt.png",
89 | "extent" : "to-status-bar",
90 | "scale" : "1x"
91 | },
92 | {
93 | "orientation" : "portrait",
94 | "idiom" : "ipad",
95 | "filename" : "launchiPadPortraitiOS56_768x1024pt.png",
96 | "extent" : "full-screen",
97 | "scale" : "1x"
98 | },
99 | {
100 | "orientation" : "portrait",
101 | "idiom" : "ipad",
102 | "filename" : "launchiPadPortraitWOSBiOS56_768x1004pt@2x.png",
103 | "extent" : "to-status-bar",
104 | "scale" : "2x"
105 | },
106 | {
107 | "orientation" : "portrait",
108 | "idiom" : "ipad",
109 | "filename" : "launchiPadPortraitiOS56_768x1024pt@2x.png",
110 | "extent" : "full-screen",
111 | "scale" : "2x"
112 | }
113 | ],
114 | "info" : {
115 | "version" : 1,
116 | "author" : "xcode"
117 | }
118 | }
--------------------------------------------------------------------------------
/Demo/KafkaExample/Assets.xcassets/LaunchImage.launchimage/launchiPadPortraitWOSBiOS56_768x1004pt.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BeatsKitano/KafkaRefresh/7d3bb0a5ce8d6ab11e976398cff8e52332f7ff63/Demo/KafkaExample/Assets.xcassets/LaunchImage.launchimage/launchiPadPortraitWOSBiOS56_768x1004pt.png
--------------------------------------------------------------------------------
/Demo/KafkaExample/Assets.xcassets/LaunchImage.launchimage/launchiPadPortraitWOSBiOS56_768x1004pt@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BeatsKitano/KafkaRefresh/7d3bb0a5ce8d6ab11e976398cff8e52332f7ff63/Demo/KafkaExample/Assets.xcassets/LaunchImage.launchimage/launchiPadPortraitWOSBiOS56_768x1004pt@2x.png
--------------------------------------------------------------------------------
/Demo/KafkaExample/Assets.xcassets/LaunchImage.launchimage/launchiPadPortraitiOS56_768x1024pt.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BeatsKitano/KafkaRefresh/7d3bb0a5ce8d6ab11e976398cff8e52332f7ff63/Demo/KafkaExample/Assets.xcassets/LaunchImage.launchimage/launchiPadPortraitiOS56_768x1024pt.png
--------------------------------------------------------------------------------
/Demo/KafkaExample/Assets.xcassets/LaunchImage.launchimage/launchiPadPortraitiOS56_768x1024pt@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BeatsKitano/KafkaRefresh/7d3bb0a5ce8d6ab11e976398cff8e52332f7ff63/Demo/KafkaExample/Assets.xcassets/LaunchImage.launchimage/launchiPadPortraitiOS56_768x1024pt@2x.png
--------------------------------------------------------------------------------
/Demo/KafkaExample/Assets.xcassets/LaunchImage.launchimage/launchiPadPortraitiOS789_768x1024pt.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BeatsKitano/KafkaRefresh/7d3bb0a5ce8d6ab11e976398cff8e52332f7ff63/Demo/KafkaExample/Assets.xcassets/LaunchImage.launchimage/launchiPadPortraitiOS789_768x1024pt.png
--------------------------------------------------------------------------------
/Demo/KafkaExample/Assets.xcassets/LaunchImage.launchimage/launchiPadPortraitiOS789_768x1024pt@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BeatsKitano/KafkaRefresh/7d3bb0a5ce8d6ab11e976398cff8e52332f7ff63/Demo/KafkaExample/Assets.xcassets/LaunchImage.launchimage/launchiPadPortraitiOS789_768x1024pt@2x.png
--------------------------------------------------------------------------------
/Demo/KafkaExample/Assets.xcassets/LaunchImage.launchimage/launchiPhonePortraitiOS56_320x480pt.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BeatsKitano/KafkaRefresh/7d3bb0a5ce8d6ab11e976398cff8e52332f7ff63/Demo/KafkaExample/Assets.xcassets/LaunchImage.launchimage/launchiPhonePortraitiOS56_320x480pt.png
--------------------------------------------------------------------------------
/Demo/KafkaExample/Assets.xcassets/LaunchImage.launchimage/launchiPhonePortraitiOS56_320x480pt@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BeatsKitano/KafkaRefresh/7d3bb0a5ce8d6ab11e976398cff8e52332f7ff63/Demo/KafkaExample/Assets.xcassets/LaunchImage.launchimage/launchiPhonePortraitiOS56_320x480pt@2x.png
--------------------------------------------------------------------------------
/Demo/KafkaExample/Assets.xcassets/LaunchImage.launchimage/launchiPhonePortraitiOS56_320x568pt@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BeatsKitano/KafkaRefresh/7d3bb0a5ce8d6ab11e976398cff8e52332f7ff63/Demo/KafkaExample/Assets.xcassets/LaunchImage.launchimage/launchiPhonePortraitiOS56_320x568pt@2x.png
--------------------------------------------------------------------------------
/Demo/KafkaExample/Assets.xcassets/LaunchImage.launchimage/launchiPhonePortraitiOS789_320x480pt@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BeatsKitano/KafkaRefresh/7d3bb0a5ce8d6ab11e976398cff8e52332f7ff63/Demo/KafkaExample/Assets.xcassets/LaunchImage.launchimage/launchiPhonePortraitiOS789_320x480pt@2x.png
--------------------------------------------------------------------------------
/Demo/KafkaExample/Assets.xcassets/LaunchImage.launchimage/launchiPhonePortraitiOS789_320x568pt@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BeatsKitano/KafkaRefresh/7d3bb0a5ce8d6ab11e976398cff8e52332f7ff63/Demo/KafkaExample/Assets.xcassets/LaunchImage.launchimage/launchiPhonePortraitiOS789_320x568pt@2x.png
--------------------------------------------------------------------------------
/Demo/KafkaExample/Assets.xcassets/LaunchImage.launchimage/launchiPhonePortraitiOS89_375x667pt@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BeatsKitano/KafkaRefresh/7d3bb0a5ce8d6ab11e976398cff8e52332f7ff63/Demo/KafkaExample/Assets.xcassets/LaunchImage.launchimage/launchiPhonePortraitiOS89_375x667pt@2x.png
--------------------------------------------------------------------------------
/Demo/KafkaExample/Assets.xcassets/LaunchImage.launchimage/launchiPhonePortraitiOS89_414x736pt@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BeatsKitano/KafkaRefresh/7d3bb0a5ce8d6ab11e976398cff8e52332f7ff63/Demo/KafkaExample/Assets.xcassets/LaunchImage.launchimage/launchiPhonePortraitiOS89_414x736pt@3x.png
--------------------------------------------------------------------------------
/Demo/KafkaExample/Assets.xcassets/LaunchImage.launchimage/launchiPhoneXPortraitiOS11_375x812pt@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BeatsKitano/KafkaRefresh/7d3bb0a5ce8d6ab11e976398cff8e52332f7ff63/Demo/KafkaExample/Assets.xcassets/LaunchImage.launchimage/launchiPhoneXPortraitiOS11_375x812pt@3x.png
--------------------------------------------------------------------------------
/Demo/KafkaExample/Assets.xcassets/icon.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "icon.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/Demo/KafkaExample/Assets.xcassets/icon.imageset/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BeatsKitano/KafkaRefresh/7d3bb0a5ce8d6ab11e976398cff8e52332f7ff63/Demo/KafkaExample/Assets.xcassets/icon.imageset/icon.png
--------------------------------------------------------------------------------
/Demo/KafkaExample/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
--------------------------------------------------------------------------------
/Demo/KafkaExample/Controllers/KafkaCollectionViewController.h:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, K.
3 | * All rights reserved.
4 | *
5 | * e-mail:xorshine@icloud.com
6 | * github:https://github.com/xorshine
7 | *
8 | * This source code is licensed under the MIT license.
9 | */
10 |
11 | #import
12 | #import "KafkaRefresh.h"
13 |
14 | @interface KafkaCollectionViewController : UICollectionViewController
15 |
16 | - (instancetype)initWithCollectionViewLayout:(UICollectionViewLayout *)layout
17 | refreshStyle:(KafkaRefreshStyle)style;
18 |
19 | @end
20 |
--------------------------------------------------------------------------------
/Demo/KafkaExample/Controllers/KafkaCollectionViewController.m:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, K.
3 | * All rights reserved.
4 | *
5 | * e-mail:xorshine@icloud.com
6 | * github:https://github.com/xorshine
7 | *
8 | * This source code is licensed under the MIT license.
9 | */
10 |
11 | #import "KafkaCollectionViewController.h"
12 |
13 | #define MainColor [UIColor colorWithRed:28./255. green:164./255. blue:252/255. alpha:1.]
14 |
15 | @interface KafkaCollectionViewController ()
16 | @property (assign, nonatomic) KafkaRefreshStyle style;
17 | @end
18 |
19 | @implementation KafkaCollectionViewController
20 |
21 | static NSString * const reuseIdentifier = @"Cell";
22 |
23 | - (void)dealloc
24 | {
25 | NSLog(@"KafkaCollectionViewController dealloc");
26 | }
27 |
28 | - (instancetype)initWithCollectionViewLayout:(UICollectionViewLayout *)layout
29 | refreshStyle:(KafkaRefreshStyle)style{
30 | self = [super initWithCollectionViewLayout:layout];
31 | if (self) {
32 | _style = style;
33 | }
34 | return self;
35 | }
36 |
37 | - (void)viewDidLoad {
38 | [super viewDidLoad];
39 | self.navigationItem.title = @"UICollectionView";
40 |
41 | self.collectionView.backgroundColor = [UIColor whiteColor];
42 | [self.collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:reuseIdentifier];
43 |
44 | __weak typeof(self) weakSelf = self;
45 |
46 | __block NSInteger count = 3;
47 | [self.collectionView bindHeadRefreshHandler:^{
48 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
49 | if (count > 0) {
50 | [weakSelf.collectionView.headRefreshControl endRefreshingWithAlertText:@"Did load successfully" completion:nil];
51 | count--;
52 | }else{
53 | [weakSelf.collectionView.headRefreshControl endRefreshingWithAlertText:@"使用中文测试" completion:nil];
54 | }
55 | });
56 | } themeColor:MainColor refreshStyle:_style];
57 |
58 | [self.collectionView bindFootRefreshHandler:^{
59 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
60 | [weakSelf.collectionView.footRefreshControl endRefreshingAndNoLongerRefreshingWithAlertText:@"no more"];
61 | });
62 | } themeColor:MainColor refreshStyle:_style];
63 |
64 | self.collectionView.headRefreshControl.backgroundColor = [UIColor grayColor];
65 | }
66 |
67 | #pragma mark
68 |
69 | - (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
70 | return 2;
71 | }
72 |
73 | - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
74 | return 15;
75 | }
76 |
77 | - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
78 | UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:reuseIdentifier forIndexPath:indexPath];
79 | NSInteger items = [collectionView numberOfItemsInSection:indexPath.section];
80 | cell.contentView.backgroundColor = [MainColor colorWithAlphaComponent:(items - indexPath.row *0.5)/items];
81 |
82 | return cell;
83 | }
84 |
85 | @end
86 |
--------------------------------------------------------------------------------
/Demo/KafkaExample/Controllers/KafkaCustomTableViewController.h:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, K.
3 | * All rights reserved.
4 | *
5 | * e-mail:xorshine@icloud.com
6 | * github:https://github.com/xorshine
7 | *
8 | * This source code is licensed under the MIT license.
9 | */
10 |
11 | #import
12 | #import "KafkaRefresh.h"
13 |
14 | @interface KafkaCustomTableViewController : UITableViewController
15 | - (instancetype)initWithRefreshStyle:(KafkaRefreshStyle)style;
16 | @end
17 |
--------------------------------------------------------------------------------
/Demo/KafkaExample/Controllers/KafkaCustomTableViewController.m:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, K.
3 | * All rights reserved.
4 | *
5 | * e-mail:xorshine@icloud.com
6 | * github:https://github.com/xorshine
7 | *
8 | * This source code is licensed under the MIT license.
9 | */
10 |
11 | #import "KafkaCustomTableViewController.h"
12 |
13 | #define MainColor [UIColor colorWithRed:28./255. green:164./255. blue:252/255. alpha:1.]
14 |
15 | @interface KafkaCustomTableViewController ()
16 | @property (assign, nonatomic) KafkaRefreshStyle style;
17 | @end
18 |
19 | @implementation KafkaCustomTableViewController
20 |
21 | - (void)dealloc
22 | {
23 | NSLog(@"KafkaCustomTableViewController dealloc");
24 | }
25 |
26 | - (instancetype)initWithRefreshStyle:(KafkaRefreshStyle)style{
27 | self = [super init];
28 | if (self) {
29 | _style = style;
30 | }
31 | return self;
32 | }
33 |
34 | - (void)viewDidLoad {
35 | [super viewDidLoad];
36 |
37 | self.navigationItem.title = @"Custom";
38 | self.tableView.rowHeight = 60.;
39 | self.tableView.sectionHeaderHeight = 35.;
40 | self.tableView.separatorStyle = UITableViewCellSeparatorStyleSingleLine;
41 | self.tableView.tableFooterView = [UIView new];
42 | self.tableView.backgroundColor = [UIColor whiteColor];
43 |
44 | __weak typeof(self) weakSelf = self;
45 | [self.tableView bindRefreshStyle:_style fillColor:[UIColor whiteColor] atPosition:0 refreshHanler:^{
46 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
47 | [weakSelf.tableView.headRefreshControl endRefreshing];
48 | });
49 | }];
50 | self.tableView.headRefreshControl.backgroundColor = [UIColor grayColor];
51 |
52 | [self.tableView.headRefreshControl beginRefreshing];
53 |
54 | [self.tableView bindRefreshStyle:_style fillColor:[UIColor whiteColor] atPosition:1 refreshHanler:^{
55 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
56 | [weakSelf.tableView.footRefreshControl endRefreshing];
57 | });
58 | }];
59 | self.tableView.footRefreshControl.backgroundColor = [UIColor grayColor];
60 | }
61 |
62 | #pragma mark - Table view data source
63 |
64 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
65 | return 2;
66 | }
67 |
68 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
69 | return 10;
70 | }
71 |
72 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
73 | static NSString * const reuseId = @"id";
74 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:reuseId];
75 | if (!cell) {
76 | cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:reuseId];
77 | }
78 | return cell;
79 | }
80 |
81 | - (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section{
82 | UILabel * label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, 35)];
83 | label.backgroundColor = MainColor;
84 | label.textAlignment = NSTextAlignmentCenter;
85 | label.textColor = [UIColor whiteColor];
86 | label.text = @"—————————————————————";
87 | return label;
88 | }
89 |
90 | - (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath{
91 | cell.separatorInset = UIEdgeInsetsMake(0, 0, 0, 0);
92 | cell.contentView.backgroundColor = [MainColor colorWithAlphaComponent:0.7];
93 | cell.selectionStyle = UITableViewCellSelectionStyleNone;
94 | }
95 | @end
96 |
--------------------------------------------------------------------------------
/Demo/KafkaExample/Controllers/KafkaMainListController.h:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, K.
3 | * All rights reserved.
4 | *
5 | * e-mail:xorshine@icloud.com
6 | * github:https://github.com/xorshine
7 | *
8 | * This source code is licensed under the MIT license.
9 | */
10 |
11 | #import
12 |
13 | @interface KafkaMainListController : UITableViewController
14 |
15 | @end
16 |
--------------------------------------------------------------------------------
/Demo/KafkaExample/Controllers/KafkaMainListController.m:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, K.
3 | * All rights reserved.
4 | *
5 | * e-mail:xorshine@icloud.com
6 | * github:https://github.com/xorshine
7 | *
8 | * This source code is licensed under the MIT license.
9 | */
10 |
11 | #import "KafkaMainListController.h"
12 | #import "KafkaTableViewController.h"
13 | #import "KafkaCollectionViewController.h"
14 | #import "KafkaCustomTableViewController.h"
15 | #import "KafkaScrollViewController.h"
16 | #import "KafkaRefresh.h"
17 |
18 |
19 | #define MainColor [UIColor colorWithRed:28./255. green:164./255. blue:252/255. alpha:1.]
20 |
21 | @interface KafkaMainListController ()
22 |
23 | @end
24 |
25 | @implementation KafkaMainListController
26 |
27 | - (void)dealloc{
28 | NSLog(@"call Dealloc");
29 | }
30 |
31 | - (void)viewDidLoad {
32 | [super viewDidLoad];
33 |
34 | [self.navigationController.navigationBar setTitleTextAttributes:@{NSForegroundColorAttributeName:[UIColor blackColor]}];
35 |
36 | self.navigationItem.title = @"Kafka";
37 | self.tableView.rowHeight = 55.;
38 | self.tableView.sectionHeaderHeight = 35.;
39 | self.tableView.separatorStyle = UITableViewCellSeparatorStyleSingleLine;
40 | self.tableView.tableFooterView = [UIView new];
41 |
42 | __weak typeof(self) weakSelf = self;
43 |
44 | [self.tableView bindDefaultRefreshStyleAtPosition:KafkaRefreshPositionHeader refreshHanler:^{
45 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2. * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
46 | [weakSelf.tableView.headRefreshControl endRefreshing];
47 | });
48 | }];
49 | [self.tableView bindDefaultRefreshStyleAtPosition:KafkaRefreshPositionFooter refreshHanler:^{
50 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1. * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
51 | [weakSelf.tableView.footRefreshControl endRefreshingAndNoLongerRefreshingWithAlertText:NSLocalizedString(@"nothing", nil)];
52 | });
53 | }];
54 | }
55 |
56 | #pragma mark - Table view data source
57 |
58 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
59 | return 4;
60 | }
61 |
62 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
63 | return [self textArray].count;
64 | }
65 |
66 | - (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section{
67 | UILabel * label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, 35)];
68 | label.backgroundColor = [UIColor whiteColor];
69 | label.textAlignment = NSTextAlignmentCenter;
70 | label.textColor = MainColor;
71 | if (section == 0) {
72 | label.text = @"UITableView";
73 | }
74 | else if (section == 1){
75 | label.text = @"UICollectionView";
76 | }
77 | else if (section == 2){
78 | label.text = @"Custom";
79 | } else if (section == 3){
80 | label.text = @"ScrollView";
81 | }
82 |
83 | return label;
84 | }
85 |
86 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
87 | static NSString * const reuseId = @"id";
88 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:reuseId];
89 | if (!cell) {
90 | cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:reuseId];
91 | }
92 | return cell;
93 | }
94 |
95 | - (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath{
96 | cell.separatorInset = UIEdgeInsetsMake(0, 0, 0, 0);
97 | NSInteger rows = [tableView numberOfRowsInSection:indexPath.section];
98 | cell.backgroundColor = [MainColor colorWithAlphaComponent:(rows - indexPath.row *0.5)/rows];
99 | cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
100 | cell.selectionStyle = UITableViewCellSelectionStyleNone;
101 | cell.detailTextLabel.textColor = [UIColor whiteColor];
102 | cell.textLabel.textColor = [UIColor whiteColor];
103 | NSInteger section = indexPath.section;
104 | if (section == 0) {
105 | cell.textLabel.text = @"UITableView";
106 | }
107 | else if (section == 1){
108 | cell.textLabel.text = @"UICollectionView";
109 | }
110 | else if (section == 2){
111 | cell.textLabel.text = @"Custom";
112 | }
113 | else if (section == 3){
114 | cell.textLabel.text = @"ScrollView";
115 | }
116 | cell.detailTextLabel.text = [[self textArray] objectAtIndex:indexPath.row];
117 | }
118 |
119 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
120 | if (indexPath.section == 0) {
121 | KafkaTableViewController * tableVC = [[KafkaTableViewController alloc] initWithRefreshStyle:indexPath.row];;
122 | [self.navigationController pushViewController:tableVC animated:YES];
123 | }
124 | else if (indexPath.section == 1){
125 | KafkaCollectionViewController *collectVC = [[KafkaCollectionViewController alloc] initWithCollectionViewLayout:[self flow] refreshStyle:indexPath.row];
126 | [self.navigationController pushViewController:collectVC animated:YES];
127 | }
128 | else if (indexPath.section == 2){
129 | KafkaCustomTableViewController *customVC = [[KafkaCustomTableViewController alloc] initWithRefreshStyle:indexPath.row];
130 | [self.navigationController pushViewController:customVC animated:YES];
131 | }
132 | else if (indexPath.section == 3){
133 | KafkaScrollViewController *customVC = [[KafkaScrollViewController alloc] initWithRefreshStyle:indexPath.row];
134 | [self.navigationController pushViewController:customVC animated:YES];
135 | }
136 | }
137 |
138 | - (UICollectionViewFlowLayout *)flow{
139 | UICollectionViewFlowLayout * flow = [[UICollectionViewFlowLayout alloc] init];
140 | flow.minimumLineSpacing = 1.0;
141 | flow.minimumInteritemSpacing = 1.0;
142 | flow.itemSize = CGSizeMake((self.view.bounds.size.width - 3.)/4, (self.view.bounds.size.width - 3.)/4 * 1.3);
143 | // flow.headerReferenceSize = CGSizeMake(self.view.bounds.size.width, 5.);
144 | return flow;
145 | }
146 |
147 | - (NSArray *)textArray{
148 | return @[
149 | @"KafkaRefreshStyleNative",
150 | @"KafkaRefreshStyleReplicatorWoody",
151 | @"KafkaRefreshStyleReplicatorAllen",
152 | @"KafkaRefreshStyleReplicatorCircle",
153 | @"KafkaRefreshStyleReplicatorDot",
154 | @"KafkaRefreshStyleReplicatorArc",
155 | @"KafkaRefreshStyleReplicatorTriangle",
156 | @"KafkaRefreshStyleAnimatableRing",
157 | @"KafkaRefreshStyleAnimatableArrow"
158 | ];
159 | }
160 |
161 | @end
162 |
--------------------------------------------------------------------------------
/Demo/KafkaExample/Controllers/KafkaScrollViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // KafkaScrollViewController.h
3 | // KafkaExample
4 | //
5 | // Created by 向小辉 on 2019/5/3.
6 | // Copyright © 2019 Kinx. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "KafkaRefresh.h"
11 |
12 | NS_ASSUME_NONNULL_BEGIN
13 |
14 | @interface KafkaScrollViewController : UIViewController
15 | - (instancetype)initWithRefreshStyle:(KafkaRefreshStyle)style;
16 | @end
17 |
18 | NS_ASSUME_NONNULL_END
19 |
--------------------------------------------------------------------------------
/Demo/KafkaExample/Controllers/KafkaScrollViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // KafkaScrollViewController.m
3 | // KafkaExample
4 | //
5 | // Created by 向小辉 on 2019/5/3.
6 | // Copyright © 2019 Kinx. All rights reserved.
7 | //
8 |
9 | #import "KafkaScrollViewController.h"
10 |
11 |
12 | #define MainColor [UIColor colorWithRed:28./255. green:164./255. blue:252/255. alpha:1.]
13 | #define MinorColor [UIColor colorWithRed:180./255. green:144./255. blue:202/255. alpha:1.]
14 |
15 |
16 | @interface KafkaScrollViewController ()
17 | @property (assign, nonatomic) KafkaRefreshStyle style;
18 | @property(nonatomic, strong) UIScrollView *scrollView;
19 | @end
20 |
21 | @implementation KafkaScrollViewController
22 |
23 | - (instancetype)initWithRefreshStyle:(KafkaRefreshStyle)style{
24 | self = [super init];
25 | if (self) {
26 | _style = style;
27 | }
28 | return self;
29 | }
30 |
31 |
32 | - (void)viewDidLoad {
33 | [super viewDidLoad];
34 |
35 | if (@available(iOS 11, *)) {
36 | self.scrollView.contentInsetAdjustmentBehavior = UIApplicationBackgroundFetchIntervalNever;
37 | } else {
38 | self.automaticallyAdjustsScrollViewInsets = false;
39 | }
40 | [self.navigationController.navigationBar setBackgroundImage:[UIImage new] forBarMetrics:(UIBarMetricsDefault)];
41 |
42 | [self.view addSubview:self.scrollView];
43 | self.scrollView.backgroundColor = [UIColor whiteColor];
44 | self.scrollView.frame = CGRectMake(0, 0, self.view.bounds.size.width, self.view.bounds.size.height);
45 | self.scrollView.contentSize = CGSizeMake(self.view.bounds.size.width, self.view.bounds.size.height*1.5);
46 | UIView *contentView = [UIView new];
47 | contentView.frame = CGRectMake(0, 0, self.view.bounds.size.width, self.view.bounds.size.height*1.5);
48 | contentView.backgroundColor = [UIColor blueColor];
49 | [self.scrollView addSubview:contentView];
50 |
51 | KafkaRefreshHandler headBlock = ^(void){
52 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
53 | [self.scrollView.headRefreshControl endRefreshing];
54 | });
55 | };
56 |
57 | [self.scrollView bindHeadRefreshHandler:headBlock themeColor:[UIColor redColor] refreshStyle:_style];
58 | }
59 |
60 | - (UIScrollView *)scrollView {
61 | if (!_scrollView) {
62 | _scrollView = [UIScrollView new];
63 | }
64 | return _scrollView;
65 | }
66 |
67 | @end
68 |
--------------------------------------------------------------------------------
/Demo/KafkaExample/Controllers/KafkaTableViewController.h:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, K.
3 | * All rights reserved.
4 | *
5 | * e-mail:xorshine@icloud.com
6 | * github:https://github.com/xorshine
7 | *
8 | * This source code is licensed under the MIT license.
9 | */
10 |
11 | #import
12 | #import "KafkaRefresh.h"
13 |
14 | @interface KafkaTableViewController : UITableViewController
15 |
16 | - (instancetype)initWithRefreshStyle:(KafkaRefreshStyle)style;
17 |
18 | @end
19 |
--------------------------------------------------------------------------------
/Demo/KafkaExample/Controllers/KafkaTableViewController.m:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, K.
3 | * All rights reserved.
4 | *
5 | * e-mail:xorshine@icloud.com
6 | * github:https://github.com/xorshine
7 | *
8 | * This source code is licensed under the MIT license.
9 | */
10 |
11 | #import "KafkaTableViewController.h"
12 |
13 | #define MainColor [UIColor colorWithRed:28./255. green:164./255. blue:252/255. alpha:1.]
14 | #define MinorColor [UIColor colorWithRed:180./255. green:144./255. blue:202/255. alpha:1.]
15 |
16 | @interface KafkaTableViewController ()
17 | @property (assign, nonatomic) KafkaRefreshStyle style;
18 | @property (strong, nonatomic) NSMutableArray * source;
19 | @end
20 |
21 | @implementation KafkaTableViewController
22 |
23 | - (void)dealloc
24 | {
25 | NSLog(@"KafkaTableViewController dealloc");
26 | }
27 |
28 | - (instancetype)initWithRefreshStyle:(KafkaRefreshStyle)style{
29 | self = [super init];
30 | if (self) {
31 | _style = style;
32 | }
33 | return self;
34 | }
35 |
36 | - (void)viewDidLoad {
37 | [super viewDidLoad];
38 |
39 | self.navigationItem.title = @"UITableView";
40 |
41 | self.navigationController.navigationBar.translucent = YES;
42 | [self.navigationController.navigationBar setBackgroundImage:[UIImage new] forBarMetrics:UIBarMetricsDefault];
43 |
44 | self.tableView.estimatedRowHeight = 2.;
45 | self.tableView.sectionHeaderHeight = 35.;
46 | self.tableView.separatorStyle = UITableViewCellSeparatorStyleSingleLine;
47 | self.tableView.tableFooterView = [UIView new];
48 |
49 | UIBarButtonItem * refresh = [[UIBarButtonItem alloc] initWithTitle:@"Refresh" style:UIBarButtonItemStylePlain target:self action:@selector(refresh)];
50 | self.navigationItem.rightBarButtonItem = refresh;
51 | __block NSInteger count = 2;
52 | __weak typeof(self) weakSelf = self;
53 |
54 | KafkaRefreshHandler headBlock = ^(void){
55 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
56 | if (count > 0) {
57 | for (NSInteger i = 0; i < 3; i++) {
58 | [weakSelf.source insertObject:@"" atIndex:0];
59 | }
60 | [weakSelf.tableView.headRefreshControl endRefreshingWithAlertText:@"Did load successfully" completion:nil];
61 | [weakSelf.tableView reloadData];
62 | count--;
63 | }else{
64 | [weakSelf.tableView.headRefreshControl endRefreshingWithAlertText:@"使用中文测试" completion:nil];
65 | }
66 | });
67 | };
68 |
69 | [self.tableView bindHeadRefreshHandler:headBlock themeColor:[UIColor redColor] refreshStyle:_style];
70 |
71 | KafkaRefreshHandler footBlock = ^(void){
72 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
73 | [weakSelf.source addObjectsFromArray:@[@"",@"",@"",@"",@"",@""]];
74 |
75 | [weakSelf.tableView reloadData];
76 | [weakSelf.tableView.footRefreshControl endRefreshingWithAlertText:@"Did load successfully" completion:^{
77 | [weakSelf.tableView reloadData];
78 | }];
79 | });
80 | };
81 |
82 | [self.tableView bindFootRefreshHandler:footBlock themeColor:MainColor refreshStyle:_style];
83 | [self.tableView.footRefreshControl setAlertTextColor:[UIColor redColor]];
84 | self.tableView.footRefreshControl.autoRefreshOnFoot = YES;
85 | }
86 |
87 | - (void)refresh{
88 | [self.tableView.headRefreshControl beginRefreshing];
89 | }
90 |
91 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
92 | return 2;
93 | }
94 |
95 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
96 | if (section == 0) {
97 | return self.source.count;
98 | }
99 | return 0;
100 | }
101 |
102 | - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
103 | if (indexPath.row % 2 == 0) {
104 | return 40;
105 | }
106 | return 60;
107 | }
108 |
109 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
110 | static NSString * const reuseId = @"id";
111 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:reuseId];
112 | if (!cell) {
113 | cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:reuseId];
114 | }
115 | return cell;
116 | }
117 |
118 | - (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section{
119 | UILabel * label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, 35)];
120 | label.backgroundColor = MainColor;
121 | label.textAlignment = NSTextAlignmentCenter;
122 | label.textColor = [UIColor whiteColor];
123 | return label;
124 | }
125 |
126 | - (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath{
127 | cell.separatorInset = UIEdgeInsetsMake(0, 0, 0, 0);
128 | cell.contentView.backgroundColor = [MainColor colorWithAlphaComponent:0.9];
129 | cell.selectionStyle = UITableViewCellSelectionStyleNone;
130 | cell.textLabel.backgroundColor = [UIColor clearColor];
131 | cell.textLabel.textColor = [UIColor whiteColor];
132 | cell.textLabel.text = [NSString stringWithFormat:@"section: %ld row: %ld",(long)indexPath.section,(long)indexPath.row];
133 | }
134 |
135 | - (NSMutableArray *)source{
136 | if (!_source) {
137 | _source = @[@"",@"",@"",@"",@"",@"",@""].mutableCopy;
138 | }
139 | return _source;
140 | }
141 |
142 | @end
143 |
--------------------------------------------------------------------------------
/Demo/KafkaExample/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleDisplayName
8 | KafkaRefresh
9 | CFBundleExecutable
10 | $(EXECUTABLE_NAME)
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | APPL
19 | CFBundleShortVersionString
20 | 1.0
21 | CFBundleVersion
22 | 1
23 | LSRequiresIPhoneOS
24 |
25 | UIMainStoryboardFile
26 | Main
27 | UIRequiredDeviceCapabilities
28 |
29 | armv7
30 |
31 | UISupportedInterfaceOrientations
32 |
33 | UIInterfaceOrientationPortrait
34 | UIInterfaceOrientationLandscapeLeft
35 | UIInterfaceOrientationLandscapeRight
36 |
37 | UISupportedInterfaceOrientations~ipad
38 |
39 | UIInterfaceOrientationPortrait
40 | UIInterfaceOrientationPortraitUpsideDown
41 | UIInterfaceOrientationLandscapeLeft
42 | UIInterfaceOrientationLandscapeRight
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/Demo/KafkaExample/KafkaAppDelegate.h:
--------------------------------------------------------------------------------
1 | /*************************************************************
2 | * Copyright (c) 2016-present, K. *
3 | * All rights reserved. *
4 | * *
5 | * e-mail: xorshine@icloud.com *
6 | * github:https://github.com/xorshine *
7 | * *
8 | * This source code is licensed under the MIT license. *
9 | *************************************************************/
10 |
11 |
12 | #import
13 |
14 | @interface KafkaAppDelegate : UIResponder
15 |
16 | @property (strong, nonatomic) UIWindow *window;
17 |
18 |
19 | @end
20 |
21 |
--------------------------------------------------------------------------------
/Demo/KafkaExample/KafkaAppDelegate.m:
--------------------------------------------------------------------------------
1 | /*************************************************************
2 | * Copyright (c) 2016-present, K. *
3 | * All rights reserved. *
4 | * *
5 | * e-mail: xorshine@icloud.com *
6 | * github:https://github.com/xorshine *
7 | * *
8 | * This source code is licensed under the MIT license. *
9 | *************************************************************/
10 |
11 | #import "KafkaAppDelegate.h"
12 | #import "KafkaRefresh.h"
13 |
14 | @interface KafkaAppDelegate ()
15 |
16 | @end
17 |
18 | @implementation KafkaAppDelegate
19 |
20 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
21 | [[KafkaRefreshDefaults standardRefreshDefaults] setHeadDefaultStyle:KafkaRefreshStyleAnimatableRing];
22 | return YES;
23 | }
24 |
25 | @end
26 |
--------------------------------------------------------------------------------
/Demo/KafkaExample/KafkaExample.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | com.apple.external-accessory.wireless-configuration
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Demo/KafkaExample/en.lproj/InfoPlist.strings:
--------------------------------------------------------------------------------
1 |
2 | "nothing" = "nothing";
3 |
4 | "All data is loaded" = "All data is loaded";
5 |
6 | "did load successfully" = "did load successfully";
7 |
--------------------------------------------------------------------------------
/Demo/KafkaExample/main.m:
--------------------------------------------------------------------------------
1 |
2 |
3 | #import
4 | #import "KafkaAppDelegate.h"
5 |
6 | int main(int argc, char * argv[]) {
7 | @autoreleasepool {
8 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([KafkaAppDelegate class]));
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/Demo/KafkaExample/zh-Hans.lproj/InfoPlist.strings:
--------------------------------------------------------------------------------
1 |
2 | "nothing" = "没有了";
3 |
4 | "All data is loaded" = "数据已经全部加载";
5 |
6 | "did load successfully" = "加载成功";
7 |
--------------------------------------------------------------------------------
/Demo/KafkaExampleTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 |
22 |
23 |
--------------------------------------------------------------------------------
/Demo/KafkaExampleTests/KafkaExampleTests.m:
--------------------------------------------------------------------------------
1 |
2 | #import
3 |
4 | @interface KafkaExampleTests : XCTestCase
5 |
6 | @end
7 |
8 | @implementation KafkaExampleTests
9 |
10 | - (void)setUp {
11 | [super setUp];
12 | // Put setup code here. This method is called before the invocation of each test method in the class.
13 | }
14 |
15 | - (void)tearDown {
16 | // Put teardown code here. This method is called after the invocation of each test method in the class.
17 | [super tearDown];
18 | }
19 |
20 | - (void)testExample {
21 | // This is an example of a functional test case.
22 | // Use XCTAssert and related functions to verify your tests produce the correct results.
23 | }
24 |
25 | - (void)testPerformanceExample {
26 | // This is an example of a performance test case.
27 | [self measureBlock:^{
28 | // Put the code you want to measure the time of here.
29 | }];
30 | }
31 |
32 | @end
33 |
--------------------------------------------------------------------------------
/Demo/KafkaExampleUITests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 |
22 |
23 |
--------------------------------------------------------------------------------
/Demo/KafkaExampleUITests/KafkaExampleUITests.m:
--------------------------------------------------------------------------------
1 |
2 | #import
3 |
4 | @interface KafkaExampleUITests : XCTestCase
5 |
6 | @end
7 |
8 | @implementation KafkaExampleUITests
9 |
10 | - (void)setUp {
11 | [super setUp];
12 |
13 | // Put setup code here. This method is called before the invocation of each test method in the class.
14 |
15 | // In UI tests it is usually best to stop immediately when a failure occurs.
16 | self.continueAfterFailure = NO;
17 | // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method.
18 | [[[XCUIApplication alloc] init] launch];
19 |
20 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this.
21 | }
22 |
23 | - (void)tearDown {
24 | // Put teardown code here. This method is called after the invocation of each test method in the class.
25 | [super tearDown];
26 | }
27 |
28 | - (void)testExample {
29 | // Use recording to get started writing UI tests.
30 | // Use XCTAssert and related functions to verify your tests produce the correct results.
31 | }
32 |
33 | @end
34 |
--------------------------------------------------------------------------------
/KafkaRefresh.podspec:
--------------------------------------------------------------------------------
1 | Pod::Spec.new do |s|
2 | s.name = "KafkaRefresh"
3 | s.version = "1.7.0"
4 | s.summary = "Highly scalable, customize, multi-style refresh framework."
5 |
6 | s.homepage = "https://github.com/xorshine/KafkaRefresh"
7 | s.license = "MIT"
8 | s.author = { "k" => "xorshine@icloud.com" }
9 |
10 | s.platform = :ios, '9.0'
11 | s.requires_arc = true
12 | s.ios.deployment_target = '9.0'
13 |
14 | s.source = { :git => "https://github.com/xorshine/KafkaRefresh.git", :tag => s.version}
15 |
16 | s.source_files = "KafkaRefresh/KafkaRefresh.h"
17 | s.public_header_files = "KafkaRefresh/KafkaRefresh.h"
18 | s.resource = 'KafkaRefresh/Resource/Image.bundle'
19 |
20 | s.subspec 'Category' do |ss|
21 | ss.source_files = "KafkaRefresh/Category/**/*"
22 | ss.public_header_files = "KafkaRefresh/Category/*.{h}"
23 | end
24 |
25 | s.subspec 'Core' do |ss|
26 | ss.source_files = "KafkaRefresh/Core/**/*"
27 | ss.public_header_files = "KafkaRefresh/Core/*.{h}"
28 | ss.dependency 'KafkaRefresh/Category'
29 | end
30 |
31 | s.subspec 'Configuration' do |ss|
32 | ss.source_files = "KafkaRefresh/Configuration/**/*"
33 | ss.public_header_files = "KafkaRefresh/Configuration/**/*.{h}"
34 | ss.dependency 'KafkaRefresh/UIKit/HeadKit'
35 | ss.dependency 'KafkaRefresh/UIKit/FootKit'
36 | end
37 |
38 | s.subspec 'Default' do |ss|
39 | ss.source_files = "KafkaRefresh/Default/**/*"
40 | ss.public_header_files = "KafkaRefresh/Default/**/*.{h}"
41 | ss.dependency 'KafkaRefresh/Style'
42 | end
43 |
44 | s.subspec 'Style' do |ss|
45 | ss.source_files = "KafkaRefresh/Style/**/*"
46 | ss.public_header_files = "KafkaRefresh/Style/**/*.{h}"
47 | end
48 |
49 | s.subspec 'UIKit' do |ss|
50 |
51 | ss.subspec 'HeadKit' do |sss|
52 | sss.source_files = "KafkaRefresh/UIKit/HeadKit/**/*"
53 | sss.public_header_files = "KafkaRefresh/UIKit/HeadKit/*.{h}"
54 | sss.dependency 'KafkaRefresh/UIKit/LayerKit'
55 | sss.dependency 'KafkaRefresh/Category'
56 | sss.dependency 'KafkaRefresh/Core'
57 | sss.dependency 'KafkaRefresh/Default'
58 | sss.dependency 'KafkaRefresh/Style'
59 | end
60 |
61 | ss.subspec 'FootKit' do |sss|
62 | sss.source_files = "KafkaRefresh/UIKit/FootKit/**/*"
63 | sss.public_header_files = "KafkaRefresh/UIKit/FootKit/*.{h}"
64 | sss.dependency 'KafkaRefresh/UIKit/LayerKit'
65 | sss.dependency 'KafkaRefresh/Category'
66 | sss.dependency 'KafkaRefresh/Core'
67 | sss.dependency 'KafkaRefresh/Default'
68 | sss.dependency 'KafkaRefresh/Style'
69 | end
70 |
71 | ss.subspec 'LayerKit' do |sss|
72 | sss.source_files = "KafkaRefresh/UIKit/LayerKit/**/*"
73 | sss.public_header_files = "KafkaRefresh/UIKit/LayerKit/*.{h}"
74 | sss.dependency 'KafkaRefresh/Category'
75 | sss.dependency 'KafkaRefresh/Default'
76 | end
77 | end
78 |
79 | end
80 |
--------------------------------------------------------------------------------
/KafkaRefresh/Category/KafkaCategories.h:
--------------------------------------------------------------------------------
1 | /*************************************************************
2 | * Copyright (c) 2016-present, K.
3 | * All rights reserved.
4 | *
5 | * e-mail: xorshine@icloud.com
6 | * github:https://github.com/xorshine
7 | *
8 | * This source code is licensed under the MIT license.
9 | *************************************************************/
10 |
11 | #import
12 | #import
13 |
14 | #ifndef weakify
15 | # define weakify(object) try{} @finally{} {} __weak __typeof__(object) weak##_##object = object;
16 | #endif
17 |
18 | #ifndef strongify
19 | # define strongify(object) try{} @finally{} __typeof__(object) object = weak##_##object;
20 | #endif
21 |
22 | #ifndef UIColorMake
23 | # define UIColorMake(r, g, b, a) [UIColor colorWithRed:(r)/255. green:(g)/255. blue:(b)/255. alpha:(a)]
24 | #endif
25 |
26 | @interface UIView (KafkaLayout)
27 |
28 | @property (nonatomic) CGFloat kr_left;
29 | @property (nonatomic) CGFloat kr_top;
30 | @property (nonatomic) CGFloat kr_right;
31 | @property (nonatomic) CGFloat kr_bottom;
32 | @property (nonatomic) CGFloat kr_width;
33 | @property (nonatomic) CGFloat kr_height;
34 | @property (nonatomic) CGFloat kr_centerX;
35 | @property (nonatomic) CGFloat kr_centerY;
36 | @property (nonatomic) CGPoint kr_origin;
37 | @property (nonatomic) CGSize kr_size;
38 |
39 | @end
40 |
41 | #pragma mark -
42 |
43 | @interface CALayer (KafkaLayout)
44 |
45 | @property (nonatomic) CGFloat kr_left;
46 | @property (nonatomic) CGFloat kr_top;
47 | @property (nonatomic) CGFloat kr_right;
48 | @property (nonatomic) CGFloat kr_bottom;
49 | @property (nonatomic) CGFloat kr_width;
50 | @property (nonatomic) CGFloat kr_height;
51 | @property (nonatomic) CGPoint kr_origin;
52 | @property (nonatomic) CGSize kr_size;
53 | @property (nonatomic) CGFloat kr_positionX;
54 | @property (nonatomic) CGFloat kr_positionY;
55 |
56 | @end
57 |
58 | #pragma mark -
59 |
60 | @interface UIScrollView (KafkaLayout)
61 |
62 | @property (nonatomic) CGFloat offsetX;
63 | @property (nonatomic) CGFloat offsetY;
64 |
65 | @property (nonatomic) CGFloat insetTop;
66 | @property (nonatomic) CGFloat insetBottom;
67 |
68 | @property (nonatomic, readonly) CGFloat contentHeight;
69 |
70 | /**
71 | scrollView's contentInset not refreshed
72 | */
73 | @property (assign, readonly) UIEdgeInsets realContentInset;
74 |
75 | @end
76 |
77 | #pragma mark -
78 |
79 | @interface NSObject (KafkaAnimation)
80 |
81 | - (void)setAnimateBlock:(dispatch_block_t)block;
82 | - (void)setAnimateBlock:(dispatch_block_t)block completion:(dispatch_block_t)completion;
83 |
84 | @end
85 |
--------------------------------------------------------------------------------
/KafkaRefresh/Category/KafkaCategories.m:
--------------------------------------------------------------------------------
1 | /*************************************************************
2 | * Copyright (c) 2016-present, K. *
3 | * All rights reserved. *
4 | * *
5 | * e-mail: xorshine@icloud.com *
6 | * github:https://github.com/xorshine *
7 | * *
8 | * This source code is licensed under the MIT license. *
9 | *************************************************************/
10 |
11 | #import "KafkaCategories.h"
12 |
13 | @implementation UIView (KafkaLayout)
14 |
15 | - (CGFloat)kr_left {
16 | return self.frame.origin.x;
17 | }
18 |
19 | - (void)setKr_left:(CGFloat)x {
20 | CGRect frame = self.frame;
21 | frame.origin.x = x;
22 | self.frame = frame;
23 | }
24 |
25 | - (CGFloat)kr_top {
26 | return self.frame.origin.y;
27 | }
28 |
29 | - (void)setKr_top:(CGFloat)y {
30 | CGRect frame = self.frame;
31 | frame.origin.y = y;
32 | self.frame = frame;
33 | }
34 |
35 | - (CGFloat)kr_right {
36 | return self.frame.origin.x + self.frame.size.width;
37 | }
38 |
39 | - (void)setKr_right:(CGFloat)right {
40 | CGRect frame = self.frame;
41 | frame.origin.x = right - frame.size.width;
42 | self.frame = frame;
43 | }
44 |
45 | - (CGFloat)kr_bottom {
46 | return self.frame.origin.y + self.frame.size.height;
47 | }
48 |
49 | - (void)setKr_bottom:(CGFloat)bottom {
50 | CGRect frame = self.frame;
51 | frame.origin.y = bottom - frame.size.height;
52 | self.frame = frame;
53 | }
54 |
55 | - (CGFloat)kr_width {
56 | return self.frame.size.width;
57 | }
58 |
59 | - (void)setKr_width:(CGFloat)width {
60 | CGRect frame = self.frame;
61 | frame.size.width = width;
62 | self.frame = frame;
63 | }
64 |
65 | - (CGFloat)kr_height {
66 | return self.frame.size.height;
67 | }
68 |
69 | - (void)setKr_height:(CGFloat)height {
70 | CGRect frame = self.frame;
71 | frame.size.height = height;
72 | self.frame = frame;
73 | }
74 |
75 | - (CGFloat)kr_centerX {
76 | return self.center.x;
77 | }
78 |
79 | - (void)setKr_centerX:(CGFloat)centerX {
80 | self.center = CGPointMake(centerX, self.center.y);
81 | }
82 |
83 | - (CGFloat)kr_centerY {
84 | return self.center.y;
85 | }
86 |
87 | - (void)setKr_centerY:(CGFloat)centerY {
88 | self.center = CGPointMake(self.center.x, centerY);
89 | }
90 |
91 | - (CGPoint)kr_origin {
92 | return self.frame.origin;
93 | }
94 |
95 | - (void)setKr_origin:(CGPoint)origin {
96 | CGRect frame = self.frame;
97 | frame.origin = origin;
98 | self.frame = frame;
99 | }
100 |
101 | - (CGSize)kr_size {
102 | return self.frame.size;
103 | }
104 |
105 | - (void)setKr_size:(CGSize)size {
106 | CGRect frame = self.frame;
107 | frame.size = size;
108 | self.frame = frame;
109 | }
110 |
111 | @end
112 |
113 | #pragma mark -
114 |
115 | @implementation CALayer (KafkaLayout)
116 |
117 | - (CGFloat)kr_left{
118 | return self.frame.origin.x;
119 | }
120 |
121 | - (void)setKr_left:(CGFloat)left{
122 | CGRect frame = self.frame;
123 | frame.origin.x = left;
124 | self.frame = frame;
125 | }
126 |
127 | - (CGFloat)kr_top {
128 | return self.frame.origin.y;
129 | }
130 |
131 | - (void)setKr_top:(CGFloat)y {
132 | CGRect frame = self.frame;
133 | frame.origin.y = y;
134 | self.frame = frame;
135 | }
136 |
137 | - (CGFloat)kr_right {
138 | return self.frame.origin.x + self.frame.size.width;
139 | }
140 |
141 | - (void)setKr_right:(CGFloat)right {
142 | CGRect frame = self.frame;
143 | frame.origin.x = right - frame.size.width;
144 | self.frame = frame;
145 | }
146 |
147 | - (CGFloat)kr_bottom {
148 | return self.frame.origin.y + self.frame.size.height;
149 | }
150 |
151 | - (void)setKr_bottom:(CGFloat)bottom {
152 | CGRect frame = self.frame;
153 | frame.origin.y = bottom - frame.size.height;
154 | self.frame = frame;
155 | }
156 |
157 | - (CGFloat)kr_width {
158 | return self.frame.size.width;
159 | }
160 |
161 | - (void)setKr_width:(CGFloat)width {
162 | CGRect frame = self.frame;
163 | frame.size.width = width;
164 | self.frame = frame;
165 | }
166 |
167 | - (CGFloat)kr_height {
168 | return self.frame.size.height;
169 | }
170 |
171 | - (void)setKr_height:(CGFloat)height {
172 | CGRect frame = self.frame;
173 | frame.size.height = height;
174 | self.frame = frame;
175 | }
176 |
177 | - (CGFloat)kr_positionX {
178 | return self.position.x;
179 | }
180 |
181 | - (void)setKr_positionX:(CGFloat)positionX {
182 | self.position = CGPointMake(positionX, self.position.y);
183 | }
184 |
185 | - (CGFloat)kr_positionY {
186 | return self.position.y;
187 | }
188 |
189 | - (void)setKr_positionY:(CGFloat)positionY {
190 | self.position = CGPointMake(self.position.x, positionY);
191 | }
192 |
193 | - (CGPoint)kr_origin {
194 | return self.frame.origin;
195 | }
196 |
197 | - (void)setKr_origin:(CGPoint)origin {
198 | CGRect frame = self.frame;
199 | frame.origin = origin;
200 | self.frame = frame;
201 | }
202 |
203 | - (CGSize)kr_size {
204 | return self.frame.size;
205 | }
206 |
207 | - (void)setKr_size:(CGSize)size {
208 | CGRect frame = self.frame;
209 | frame.size = size;
210 | self.frame = frame;
211 | }
212 |
213 | @end
214 |
215 | #pragma mark -
216 |
217 | @implementation UIScrollView (KafkaLayout)
218 |
219 | - (CGFloat)offsetX{
220 | return self.contentOffset.x;
221 | }
222 |
223 | - (void)setOffsetX:(CGFloat)offsetX{
224 | CGPoint offset = self.contentOffset;
225 | offset.x = offsetX;
226 | self.contentOffset = offset;
227 | }
228 |
229 | - (CGFloat)offsetY{
230 | return self.contentOffset.y;
231 | }
232 |
233 | -(void)setOffsetY:(CGFloat)offsetY{
234 | CGPoint offset = self.contentOffset;
235 | offset.y = offsetY;
236 | self.contentOffset = offset;
237 | }
238 |
239 | - (CGFloat)insetTop{
240 | return self.realContentInset.top;
241 | }
242 |
243 | - (void)setInsetTop:(CGFloat)insetTop{
244 | UIEdgeInsets inset = self.contentInset;
245 | inset.top = insetTop;
246 | if (@available(iOS 11.0, *)) {
247 | inset.top -= (self.adjustedContentInset.top - self.contentInset.top);
248 | }
249 | // self.contentInset = inset;
250 | CGFloat top = self.contentInset.top;
251 | if (round(top) != round(inset.top)) {
252 | self.contentInset = inset;
253 | }
254 | }
255 |
256 | - (CGFloat)insetBottom{
257 | return self.realContentInset.bottom;
258 | }
259 |
260 | - (void)setInsetBottom:(CGFloat)insetBottom{
261 | UIEdgeInsets inset = self.contentInset;
262 | inset.bottom = insetBottom;
263 | if (@available(iOS 11.0, *)) {
264 | inset.bottom -= (self.adjustedContentInset.bottom - self.contentInset.bottom);
265 | }
266 | self.contentInset = inset;
267 | }
268 |
269 | - (CGFloat)contentHeight{
270 | return self.contentSize.height;
271 | }
272 |
273 | - (UIEdgeInsets)realContentInset{
274 | if (@available(iOS 11.0, *)) {
275 | return self.adjustedContentInset;
276 | } else {
277 | return self.contentInset;
278 | }
279 | }
280 |
281 | @end
282 |
283 | #pragma mark -
284 |
285 | @implementation NSObject (KafkaAnimation)
286 |
287 | - (void)setAnimateBlock:(dispatch_block_t)block{
288 | [self setAnimateBlock:block completion:nil];
289 | }
290 |
291 | - (void)setAnimateBlock:(dispatch_block_t)block completion:(dispatch_block_t)completion{
292 | [UIView animateWithDuration:0.15
293 | delay:0
294 | options:UIViewAnimationOptionCurveLinear
295 | animations:block
296 | completion:^(BOOL finished) {
297 | if (completion) completion();
298 | }];
299 | }
300 |
301 | @end
302 |
303 |
304 |
--------------------------------------------------------------------------------
/KafkaRefresh/Configuration/UIScrollView+KafkaConfiguration.h:
--------------------------------------------------------------------------------
1 | /*************************************************************
2 | * Copyright (c) 2016-present, K. *
3 | * All rights reserved. *
4 | * *
5 | * e-mail: xorshine@icloud.com *
6 | * github:https://github.com/xorshine *
7 | * *
8 | * This source code is licensed under the MIT license. *
9 | *************************************************************/
10 |
11 | #import
12 | #import "KafkaRefreshStyle.h"
13 | #import "KafkaRefreshControl.h"
14 |
15 | @interface UIScrollView (KafkaConfiguration)
16 |
17 | - (void)bindGlobalStyleForHeadRefreshHandler:(KafkaRefreshHandler)block;
18 | - (void)bindHeadRefreshHandler:(KafkaRefreshHandler)block themeColor:(UIColor *)color refreshStyle:(KafkaRefreshStyle)style;
19 |
20 | - (void)bindGlobalStyleForFootRefreshHandler:(KafkaRefreshHandler)block;
21 | - (void)bindFootRefreshHandler:(KafkaRefreshHandler)block themeColor:(UIColor *)color refreshStyle:(KafkaRefreshStyle)style;
22 |
23 | @end
24 |
25 | #pragma mark - deprecated
26 |
27 | @interface UIScrollView (KafakaDeprecated)
28 |
29 | - (void)bindRefreshStyle:(KafkaRefreshStyle)style fillColor:(UIColor *)fillColor atPosition:(KafkaRefreshPosition)position refreshHanler:(KafkaRefreshHandler)handler __attribute__((deprecated("please use bindHeadRefreshHandler: themeColor: refreshStyle")));
30 |
31 | - (void)bindRefreshStyle:(KafkaRefreshStyle)style fillColor:(UIColor *)fillColor animatedBackgroundColor:(UIColor *)backgroundColor atPosition:(KafkaRefreshPosition)position refreshHanler:(KafkaRefreshHandler)handler __attribute__((deprecated("please use bindHeadRefreshHandler: themeColor: refreshStyle")));
32 |
33 | - (void)bindDefaultRefreshStyleAtPosition:(KafkaRefreshPosition)position refreshHanler:(KafkaRefreshHandler)handler __attribute__((deprecated("please use bindGlobalStyleForHeadRefreshHandler:")));
34 |
35 | @end
36 |
37 |
38 |
--------------------------------------------------------------------------------
/KafkaRefresh/Configuration/UIScrollView+KafkaConfiguration.m:
--------------------------------------------------------------------------------
1 | /*************************************************************
2 | * Copyright (c) 2016-present, K. *
3 | * All rights reserved. *
4 | * *
5 | * e-mail: xorshine@icloud.com *
6 | * github:https://github.com/xorshine *
7 | * *
8 | * This source code is licensed under the MIT license. *
9 | *************************************************************/
10 |
11 | #import "UIScrollView+KafkaConfiguration.h"
12 | #import "KafkaRefreshDefaults.h"
13 | #import "UIScrollView+KafkaRefreshControl.h"
14 | #import "KafkaNativeHeader.h"
15 | #import "KafkaReplicatorHeader.h"
16 | #import "KafkaRingIndicatorHeader.h"
17 | #import "KafkaArrowHeader.h"
18 | #import "KafkaNativeFooter.h"
19 | #import "KafkaReplicatorFooter.h"
20 | #import "KafkaRingIndicatorFooter.h"
21 | #import "KafkaArrowFooter.h"
22 |
23 | @implementation UIScrollView (KafkaConfiguration)
24 |
25 | - (void)bindGlobalStyleForHeadRefreshHandler:(KafkaRefreshHandler)block{
26 | [self bindHeadRefreshHandler:block themeColor:[KafkaRefreshDefaults standardRefreshDefaults].themeColor refreshStyle:[KafkaRefreshDefaults standardRefreshDefaults].headDefaultStyle];
27 | }
28 |
29 | - (void)bindHeadRefreshHandler:(KafkaRefreshHandler)block themeColor:(UIColor *)color refreshStyle:(KafkaRefreshStyle)style {
30 | __kindof KafkaRefreshControl *head = nil;
31 | switch (style) {
32 | case KafkaRefreshStyleNative: {
33 | KafkaNativeHeader *h = [[KafkaNativeHeader alloc] init];
34 | h.indicator.color = color;
35 | head = h;
36 | break;
37 | }
38 | case KafkaRefreshStyleReplicatorWoody:
39 | case KafkaRefreshStyleReplicatorAllen:
40 | case KafkaRefreshStyleReplicatorCircle:
41 | case KafkaRefreshStyleReplicatorDot:
42 | case KafkaRefreshStyleReplicatorArc:
43 | case KafkaRefreshStyleReplicatorTriangle:{
44 | head = [[KafkaReplicatorHeader alloc] init];
45 | ((KafkaReplicatorHeader *)head).animationStyle = style - 1;
46 | break;
47 | }
48 | case KafkaRefreshStyleAnimatableRing: {
49 | head = [[KafkaRingIndicatorHeader alloc] init];
50 | break;
51 | }
52 | case KafkaRefreshStyleAnimatableArrow: {
53 | head = [[KafkaArrowHeader alloc] init];
54 | break;
55 | }
56 | }
57 | head.refreshHandler = block;
58 | head.themeColor = color;
59 | self.headRefreshControl = head;
60 | }
61 |
62 | #pragma mark -
63 |
64 | - (void)bindGlobalStyleForFootRefreshHandler:(KafkaRefreshHandler)block {
65 | [self bindFootRefreshHandler:block themeColor:[KafkaRefreshDefaults standardRefreshDefaults].themeColor refreshStyle:[KafkaRefreshDefaults standardRefreshDefaults].footDefaultStyle];
66 | }
67 |
68 | - (void)bindFootRefreshHandler:(KafkaRefreshHandler)block themeColor:(UIColor *)color refreshStyle:(KafkaRefreshStyle)style {
69 | __kindof KafkaFootRefreshControl *foot = nil;
70 | switch (style) {
71 | case KafkaRefreshStyleNative:{
72 | foot = [[KafkaNativeFooter alloc] init];
73 | break;
74 | }
75 | case KafkaRefreshStyleReplicatorWoody:
76 | case KafkaRefreshStyleReplicatorAllen:
77 | case KafkaRefreshStyleReplicatorCircle:
78 | case KafkaRefreshStyleReplicatorDot:
79 | case KafkaRefreshStyleReplicatorArc:
80 | case KafkaRefreshStyleReplicatorTriangle: {
81 | foot = [[KafkaReplicatorFooter alloc] init];
82 | ((KafkaReplicatorFooter *)foot).animationStyle = style - 1;
83 | break;
84 | }
85 | case KafkaRefreshStyleAnimatableRing: {
86 | foot = [[KafkaRingIndicatorFooter alloc] init];
87 | break;
88 | }
89 | case KafkaRefreshStyleAnimatableArrow: {
90 | foot = [[KafkaArrowFooter alloc] init];
91 | break;
92 | }
93 | }
94 | foot.themeColor = color;
95 | foot.refreshHandler = block;
96 | self.footRefreshControl = foot;
97 | }
98 |
99 | @end
100 |
101 | #pragma mark -
102 |
103 | @implementation UIScrollView(KafakaDeprecated)
104 |
105 | - (void)bindRefreshStyle:(KafkaRefreshStyle)style fillColor:(UIColor *)fillColor atPosition:(KafkaRefreshPosition)position refreshHanler:(KafkaRefreshHandler)handler {
106 | __kindof KafkaRefreshControl *control = [self _classWithRefreshStyle:style color:fillColor position:position];
107 | if (!control) return;
108 | control.refreshHandler = handler;
109 | if (position == KafkaRefreshPositionHeader) {
110 | self.headRefreshControl = control;
111 | }else{
112 | self.footRefreshControl = control;
113 | }
114 | }
115 |
116 | - (void)bindRefreshStyle:(KafkaRefreshStyle)style fillColor:(UIColor *)fillColor animatedBackgroundColor:(UIColor *)backgroundColor atPosition:(KafkaRefreshPosition)position refreshHanler:(KafkaRefreshHandler)handler {
117 | __kindof KafkaRefreshControl *control = [self _classWithRefreshStyle:style color:fillColor position:position];
118 | if (!control) return;
119 | control.refreshHandler = handler;
120 | control.animatedBackgroundColor = backgroundColor;
121 | if (position == KafkaRefreshPositionHeader) {
122 | self.headRefreshControl = control;
123 | }else{
124 | self.footRefreshControl = control;
125 | }
126 | }
127 |
128 | - (void)bindDefaultRefreshStyleAtPosition:(KafkaRefreshPosition)position refreshHanler:(KafkaRefreshHandler)handler {
129 | if (position == KafkaRefreshPositionHeader) {
130 | __kindof KafkaRefreshControl *control = [self _classWithRefreshStyle:[KafkaRefreshDefaults standardRefreshDefaults].headDefaultStyle
131 | color:[KafkaRefreshDefaults standardRefreshDefaults].themeColor
132 | position:position];
133 | self.headRefreshControl = control;
134 | control.refreshHandler = handler;
135 | }else{
136 | __kindof KafkaRefreshControl *control = [self _classWithRefreshStyle:[KafkaRefreshDefaults standardRefreshDefaults].footDefaultStyle
137 | color:[KafkaRefreshDefaults standardRefreshDefaults].themeColor
138 | position:position];
139 | self.footRefreshControl = control;
140 | control.refreshHandler = handler;
141 | }
142 | }
143 |
144 | - (__kindof KafkaRefreshControl *)_classWithRefreshStyle:(KafkaRefreshStyle)style
145 | color:(UIColor *)color
146 | position:(KafkaRefreshPosition)position{
147 | KafkaRefreshControl *cls = nil;
148 | switch (style) {
149 | case KafkaRefreshStyleNative:{
150 | if (position == KafkaRefreshPositionHeader) {
151 | cls = [[KafkaNativeHeader alloc] init];
152 | }else{
153 | cls = [[KafkaNativeFooter alloc] init];
154 | }
155 | break;
156 | }
157 | case KafkaRefreshStyleReplicatorWoody:
158 | case KafkaRefreshStyleReplicatorAllen:
159 | case KafkaRefreshStyleReplicatorCircle:
160 | case KafkaRefreshStyleReplicatorDot:
161 | case KafkaRefreshStyleReplicatorArc:
162 | case KafkaRefreshStyleReplicatorTriangle:{
163 | if (position == KafkaRefreshPositionHeader) {
164 | cls = [[KafkaReplicatorHeader alloc] init];
165 | }else{
166 | cls = [[KafkaReplicatorFooter alloc] init];
167 | }
168 | ((KafkaReplicatorHeader *)cls).animationStyle = style - 1;
169 | break;
170 | }
171 | case KafkaRefreshStyleAnimatableRing:{
172 | if (position == KafkaRefreshPositionHeader) {
173 | cls = [[KafkaRingIndicatorHeader alloc] init];
174 | }else{
175 | cls = [[KafkaRingIndicatorFooter alloc] init];
176 | }
177 | break;
178 | }
179 | case KafkaRefreshStyleAnimatableArrow:{
180 | if (position == KafkaRefreshPositionHeader) {
181 | cls = [[KafkaArrowHeader alloc] init];
182 | }else{
183 | cls = [[KafkaArrowFooter alloc] init];
184 | }
185 | break;
186 | }
187 | }
188 | cls.themeColor = color;
189 | return cls;
190 | }
191 |
192 | @end
193 |
194 |
--------------------------------------------------------------------------------
/KafkaRefresh/Core/KafkaFootRefreshControl.h:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, K.
3 | * All rights reserved.
4 | *
5 | * e-mail:xiaohuiprivate@gmail.com
6 | * github:https://github.com/xorshine
7 | *
8 | * This source code is licensed under the MIT license.
9 | */
10 |
11 |
12 | #import "KafkaRefreshControl.h"
13 |
14 | @interface KafkaFootRefreshControl : KafkaRefreshControl
15 |
16 | @end
17 |
--------------------------------------------------------------------------------
/KafkaRefresh/Core/KafkaFootRefreshControl.m:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, K.
3 | * All rights reserved.
4 | *
5 | * e-mail:xorshine@icloud.com
6 | * github:https://github.com/xorshine
7 | *
8 | * This source code is licensed under the MIT license.
9 | */
10 |
11 | #import "KafkaFootRefreshControl.h"
12 | #import "KafkaCategories.h"
13 |
14 | @implementation KafkaFootRefreshControl
15 |
16 | - (void)layoutSubviews {
17 | [super layoutSubviews];
18 | self.kr_top = self.scrollView.contentHeight;
19 | }
20 |
21 | - (void)setScrollViewToRefreshLocation {
22 | [super setScrollViewToRefreshLocation];
23 | dispatch_block_t animation = ^(void){
24 | if (self.isTriggeredRefreshByUser) {
25 | self.refreshState = KafkaRefreshStateScrolling;
26 | if (self.scrollView.contentHeight > self.scrollView.kr_height &&
27 | self.scrollView.offsetY >= OffsetOfTriggeringFootRefreshControlToRefresh(self)) {
28 | /**
29 | This condition can be pre-execute refreshHandler, and will not feel scrollview scroll
30 | */
31 | [self.scrollView setContentOffset:RefreshingPoint(self)];
32 | [self kafkaDidScrollWithProgress:0.5 max:self.stretchOffsetYAxisThreshold];
33 | }
34 | }
35 | self.scrollView.insetBottom = self.presetContentInsets.bottom + self.kr_height;
36 | };
37 |
38 | dispatch_block_t completion = ^(void){
39 | if (self.refreshHandler) self.refreshHandler();
40 | if (self.isTriggeredRefreshByUser) {
41 | self.refreshState = KafkaRefreshStateReady;
42 | self.refreshState = KafkaRefreshStateRefreshing;
43 | [self kafkaDidScrollWithProgress:1. max:self.stretchOffsetYAxisThreshold];
44 | }
45 | };
46 | @weakify(self);
47 | dispatch_async(dispatch_get_main_queue(), ^{
48 | @strongify(self);
49 | self.presetContentInsets = self.scrollView.realContentInset;
50 | [self setAnimateBlock:animation completion:completion];
51 | });
52 | }
53 |
54 | - (void)setScrollViewToOriginalLocation:(dispatch_block_t)completion {
55 | [super setScrollViewToOriginalLocation:completion];
56 | @weakify(self);
57 | [self setAnimateBlock:^{
58 | @strongify(self);
59 | self.animating = YES;
60 | self.scrollView.insetBottom = self.presetContentInsets.bottom;
61 | } completion:^{
62 | @strongify(self);
63 | self.animating = NO;
64 | self.triggeredRefreshByUser = NO;
65 | self.refreshState = KafkaRefreshStateNone;
66 | if (completion) {
67 | completion();
68 | }
69 | }];
70 | }
71 |
72 | #pragma mark - contentOffset
73 |
74 |
75 | static CGPoint RefreshingPoint(KafkaFootRefreshControl *cSelf) {
76 | UIScrollView * sc = cSelf.scrollView;
77 | CGFloat x = sc.kr_left;
78 | // CGFloat y = sc.contentHeight - sc.height + cSelf.height*cSelf.stretchOffsetYAxisThreshold;
79 | return CGPointMake(x, OffsetOfTriggeringFootRefreshControlToRefresh(cSelf));
80 | }
81 |
82 |
83 | static CGFloat OffsetOfTriggeringFootRefreshControlToRefresh(KafkaRefreshControl * cSelf) {
84 | UIScrollView * sc = cSelf.scrollView;
85 | CGFloat y = sc.contentHeight - sc.kr_height + cSelf.stretchOffsetYAxisThreshold*cSelf.kr_height + cSelf.presetContentInsets.bottom;
86 | return y;
87 | }
88 |
89 | static CGFloat OffsetOfTriggeringFootRefreshControlToAutoRefresh(KafkaRefreshControl * cSelf) {
90 | UIScrollView * sc = cSelf.scrollView;
91 | CGFloat y = sc.contentHeight - sc.kr_height + cSelf.presetContentInsets.bottom;
92 | return y;
93 | }
94 |
95 | static CGFloat OffsetOfFootRefreshControlToRestore(KafkaRefreshControl * cSelf) {
96 | UIScrollView * sc = cSelf.scrollView;
97 | CGFloat y = sc.contentHeight - sc.kr_height + cSelf.presetContentInsets.bottom;
98 | return y;
99 | }
100 |
101 | - (void)privateContentOffsetOfScrollViewDidChange:(CGPoint)contentOffset{
102 | [super privateContentOffsetOfScrollViewDidChange:contentOffset];
103 | if(self.isRefresh || self.isAnimating) return;
104 |
105 | if (self.refreshState != KafkaRefreshStateRefreshing) {
106 | if (self.isTriggeredRefreshByUser) return;
107 | self.presetContentInsets = self.scrollView.realContentInset;
108 |
109 | CGFloat originY = 0.0, maxY = 0.0, minY = 0.0 , contentOffsetYInBottom = 0.0;
110 |
111 | if (self.scrollView.contentHeight + self.presetContentInsets.top <= self.scrollView.kr_height) {
112 | maxY = self.stretchOffsetYAxisThreshold * self.kr_height;
113 | minY = 0;
114 | originY = contentOffset.y + self.presetContentInsets.top;
115 |
116 | if (self.refreshState == KafkaRefreshStateScrolling) {
117 | CGFloat progress = fabs(originY) / self.kr_height;
118 | if (progress <= self.stretchOffsetYAxisThreshold) {
119 | self.progress = progress;
120 | }
121 | }
122 | } else {
123 | maxY = OffsetOfTriggeringFootRefreshControlToRefresh(self);
124 | minY = OffsetOfFootRefreshControlToRestore(self);
125 | originY = contentOffset.y;
126 | contentOffsetYInBottom = self.scrollView.contentHeight - self.scrollView.kr_height;
127 | /////////////////////////
128 | ///uncontinuous callback
129 | /////////////////////////
130 | static CGFloat uncontinuousOpt = 50.;
131 | if (originY < minY - uncontinuousOpt) return;
132 |
133 | if (self.refreshState == KafkaRefreshStateScrolling){
134 | CGFloat progress = fabs((originY - contentOffsetYInBottom - self.presetContentInsets.bottom))/self.kr_height;
135 | if (progress <= self.stretchOffsetYAxisThreshold) {
136 | self.progress = progress;
137 | }
138 | }
139 |
140 | if (self.autoRefreshOnFoot) {
141 | if (self.scrollView.isDragging && originY > OffsetOfTriggeringFootRefreshControlToAutoRefresh(self) && !self.isAnimating && self.refreshState == KafkaRefreshStateNone && !self.isShouldNoLongerRefresh) {
142 | [self beginRefreshing];
143 | }
144 | return;
145 | }
146 | }
147 |
148 | if (!self.scrollView.isDragging && self.refreshState == KafkaRefreshStateReady) {
149 | self.triggeredRefreshByUser = NO;
150 | self.refreshState = KafkaRefreshStateRefreshing;
151 | [self setScrollViewToRefreshLocation];
152 | }
153 | else if (originY <= minY && !self.isAnimating) {
154 | self.refreshState = KafkaRefreshStateNone;
155 | }
156 | else if (self.scrollView.isDragging && originY >= minY && originY <= maxY && self.refreshState != KafkaRefreshStateScrolling) {
157 | self.refreshState = KafkaRefreshStateScrolling;
158 | }
159 | else if (self.scrollView.isDragging && originY > maxY && !self.isAnimating && self.refreshState != KafkaRefreshStateReady && !self.isShouldNoLongerRefresh) {
160 | self.refreshState = KafkaRefreshStateReady;
161 | }
162 | }
163 | }
164 |
165 | @end
166 |
--------------------------------------------------------------------------------
/KafkaRefresh/Core/KafkaHeadRefreshControl.h:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, K.
3 | * All rights reserved.
4 | *
5 | * e-mail:xorshine@icloud.com
6 | * github:https://github.com/xorshine
7 | *
8 | * This source code is licensed under the MIT license.
9 | */
10 |
11 | #import "KafkaRefreshControl.h"
12 |
13 | @interface KafkaHeadRefreshControl : KafkaRefreshControl
14 |
15 | @end
16 |
--------------------------------------------------------------------------------
/KafkaRefresh/Core/KafkaHeadRefreshControl.m:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, K.
3 | * All rights reserved.
4 | *
5 | * e-mail:xiaohuiprivate@gmail.com
6 | * github:https://github.com/xorshine
7 | *
8 | * This source code is licensed under the MIT license.
9 | */
10 |
11 | #import "KafkaHeadRefreshControl.h"
12 | #import "KafkaCategories.h"
13 |
14 | @implementation KafkaHeadRefreshControl
15 |
16 | - (void)layoutSubviews{
17 | [super layoutSubviews];
18 | self.kr_top = -self.kr_height;
19 | }
20 |
21 | static inline CGPoint RefreshingPoint(KafkaHeadRefreshControl *cSelf){
22 | UIScrollView * sc = cSelf.scrollView;
23 | CGFloat x = sc.kr_left;
24 | CGFloat y = -(cSelf.kr_height + cSelf.presetContentInsets.top);
25 | return CGPointMake(x, y);
26 | }
27 |
28 | - (void)setScrollViewToRefreshLocation{
29 | [super setScrollViewToRefreshLocation];
30 | dispatch_block_t animatedBlock = ^(void){
31 | if (self.isTriggeredRefreshByUser) {
32 | self.refreshState = KafkaRefreshStateScrolling;
33 | /*///////////////////////////////////////////////////////////////////////////////////////////
34 | In general, we use UITableView, especially UITableView need to use the drop-down refresh,
35 | we rarely set SectionHeader. Unfortunately, if you use SectionHeader and integrate with
36 | UIRefreshControl or other third-party libraries, the refresh effect will be very ugly.
37 |
38 | This code has two effects:
39 | 1. when using SectionHeader refresh effect is still very natural.
40 | 2. when your scrollView using preloading technology, only in the right place,
41 | such as pull down a pixel you can see the refresh control case, will show the
42 | refresh effect. If the pull-down distance exceeds the height of the refresh control,
43 | then the refresh control has long been unable to appear on the screen,
44 | indicating that the top of the contentOffset office there is a long distance,
45 | this time, even if you call the beginRefreshing method, ScrollView position and effect
46 | are Will not be affected, so the deal is very friendly in the data preloading technology.
47 | ///////////////////////////////////////////////////////////////////////////////////////////*/
48 | CGFloat min = -self.presetContentInsets.top;
49 | CGFloat max = -(self.presetContentInsets.top-self.kr_height);
50 | if (self.scrollView.offsetY >= min && self.scrollView.offsetY <= max) {
51 | [self.scrollView setContentOffset:RefreshingPoint(self)];
52 | [self kafkaDidScrollWithProgress:0.5 max:self.stretchOffsetYAxisThreshold];
53 | self.scrollView.insetTop = self.kr_height + self.presetContentInsets.top;
54 | }
55 | }else{
56 | self.scrollView.insetTop = self.kr_height + self.presetContentInsets.top;
57 | }
58 | };
59 |
60 | dispatch_block_t completionBlock = ^(void){
61 | if (self.isTriggeredRefreshByUser) {
62 | self.refreshState = KafkaRefreshStateReady;
63 | self.refreshState = KafkaRefreshStateRefreshing;
64 | [self kafkaDidScrollWithProgress:1. max:self.stretchOffsetYAxisThreshold];
65 | }
66 | if (self.refreshHandler) self.refreshHandler();
67 | };
68 | @weakify(self);
69 | dispatch_async(dispatch_get_main_queue(), ^{
70 | @strongify(self);
71 | self.presetContentInsets = self.scrollView.realContentInset;
72 | [self setAnimateBlock:animatedBlock completion:completionBlock];
73 | });
74 | }
75 |
76 | - (void)setScrollViewToOriginalLocation:(dispatch_block_t)block{
77 | [super setScrollViewToOriginalLocation:block];
78 | dispatch_block_t animation = ^{
79 | self.animating = YES;
80 | self.scrollView.insetTop = self.presetContentInsets.top;
81 | };
82 |
83 | dispatch_block_t completion = ^{
84 | self.animating = NO;
85 | self.triggeredRefreshByUser = NO;
86 | self.refreshState = KafkaRefreshStateNone;
87 | if (block) {
88 | block();
89 | }
90 | };
91 | [self setAnimateBlock:animation completion:completion];
92 | }
93 |
94 | #pragma mark - contentOffset
95 |
96 | static CGFloat MaxYForTriggeringRefresh(KafkaRefreshControl * cSelf){
97 | CGFloat y = -cSelf.presetContentInsets.top + cSelf.stretchOffsetYAxisThreshold * cSelf.kr_top;
98 | return y;
99 | }
100 |
101 | static CGFloat MinYForNone(KafkaRefreshControl * cSelf){
102 | return -cSelf.presetContentInsets.top;;
103 | }
104 |
105 | - (void)privateContentOffsetOfScrollViewDidChange:(CGPoint)contentOffset{
106 | [super privateContentOffsetOfScrollViewDidChange:contentOffset];
107 | CGFloat maxY = MaxYForTriggeringRefresh(self);
108 | CGFloat minY = MinYForNone(self);
109 | CGFloat originY = contentOffset.y;
110 |
111 | if (self.refreshState == KafkaRefreshStateRefreshing) {
112 | /////////////////////////////////////////////////////
113 | //fix hover problem of sectionHeader
114 | /////////////////////////////////////////////////////
115 | if (originY < 0 && (-originY >= self.presetContentInsets.top)) {
116 | CGFloat threshold = self.presetContentInsets.top + self.kr_height;
117 | if (-originY > threshold) {
118 | self.scrollView.insetTop = threshold;
119 | }else{
120 | self.scrollView.insetTop = -originY;
121 | }
122 | }else{
123 | if (self.scrollView.insetTop != self.presetContentInsets.top) {
124 | self.scrollView.insetTop = self.presetContentInsets.top;
125 | }
126 | }
127 | }else{
128 | if (self.isTriggeredRefreshByUser) return;
129 |
130 | self.presetContentInsets = self.scrollView.realContentInset;
131 |
132 | if (self.refreshState == KafkaRefreshStateScrolling){
133 | CGFloat progress = (fabs((double)originY) - self.presetContentInsets.top)/self.kr_height;
134 | if (progress <= self.stretchOffsetYAxisThreshold) {
135 | self.progress = progress;
136 | }
137 | }
138 |
139 | if (!self.scrollView.isDragging && self.refreshState == KafkaRefreshStateReady){
140 | self.triggeredRefreshByUser = NO;
141 | self.refreshState = KafkaRefreshStateRefreshing;
142 | [self setScrollViewToRefreshLocation];
143 | }
144 | else if (originY >= minY && !self.isAnimating){
145 | self.refreshState = KafkaRefreshStateNone;
146 | }
147 | else if (self.scrollView.isDragging && originY <= minY
148 | && originY >= maxY && self.refreshState != KafkaRefreshStateScrolling){
149 | self.refreshState = KafkaRefreshStateScrolling;
150 | }
151 | else if (self.scrollView.isDragging && originY < maxY && !self.isAnimating
152 | && self.refreshState != KafkaRefreshStateReady && !self.isShouldNoLongerRefresh){
153 | self.refreshState = KafkaRefreshStateReady;
154 | }
155 | }
156 | }
157 |
158 | @end
159 |
--------------------------------------------------------------------------------
/KafkaRefresh/Core/KafkaRefreshControl.h:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, K.
3 | * All rights reserved.
4 | *
5 | * e-mail:xiaohuiprivate@gmail.com
6 | * github:https://github.com/xorshine
7 | *
8 | * This source code is licensed under the MIT license.
9 | */
10 |
11 |
12 | #import
13 | #import "KafkaRefreshProtocol.h"
14 |
15 | #ifndef Kafka_REQUIRES_SUPER
16 | # if __has_attribute(objc_requires_super)
17 | # define Kafka_REQUIRES_SUPER __attribute__((objc_requires_super))
18 | # else
19 | # define Kafka_REQUIRES_SUPER
20 | # endif
21 | #endif
22 |
23 | #define KafkaColorWithRGBA(r,g,b,a) \
24 | [UIColor colorWithRed:(r)/255. green:(g)/255. blue:(b)/255. alpha:(a)]
25 |
26 | /**
27 | Refresh the control's location
28 | */
29 | typedef NS_ENUM(NSInteger,KafkaRefreshPosition) {
30 | KafkaRefreshPositionHeader = 0,
31 | KafkaRefreshPositionFooter
32 | };
33 |
34 | typedef void(^KafkaRefreshHandler)(void);
35 |
36 | /**
37 | Refresh control base class, developers do not use this class directly
38 | */
39 | NS_CLASS_AVAILABLE_IOS(7_0) @interface KafkaRefreshControl: UIView
40 |
41 | /**
42 | The UIScrollView to which the control is added, developers may not set
43 | */
44 | @property (nonatomic, weak, readonly) __kindof UIScrollView *scrollView;
45 |
46 | /**
47 | Whether it is refreshing
48 | */
49 | @property (nonatomic, readonly, getter=isRefresh) BOOL refresh;
50 |
51 | /**
52 | Judge whether the animation is executed when the refresh is over
53 | */
54 | @property (assign, nonatomic, getter=isAnimating) BOOL animating;
55 |
56 | /**
57 | Control refresh status, developers may not set
58 | */
59 | @property (nonatomic) KafkaRefreshState refreshState;
60 |
61 | /**
62 | When the system automatically or manually adjust contentInset,
63 |
64 | this value will be saved
65 | */
66 | @property (assign, nonatomic) UIEdgeInsets presetContentInsets;
67 |
68 | /**
69 | This value is set to TRUE if the beginRefresh method is called automatically
70 |
71 | developers may not set
72 | */
73 | @property (assign, nonatomic, getter=isTriggeredRefreshByUser) BOOL triggeredRefreshByUser;
74 |
75 | /**
76 | the current position offset of the control as a percentage
77 |
78 | of the offset that triggered the refresh
79 | */
80 | @property (assign, nonatomic) CGFloat progress;
81 |
82 | /**
83 | Block will be called when refreshing
84 | */
85 | @property (copy, nonatomic) KafkaRefreshHandler refreshHandler;
86 |
87 | /**
88 | The threshold for trigger refresh default 1.0 must be set to not less than 1.0,
89 |
90 | default value is 1.3, developers can set the value
91 | */
92 | @property (assign, nonatomic) CGFloat stretchOffsetYAxisThreshold;
93 |
94 | /**
95 | fill colors for points, lines, and faces that appear in this control.
96 | */
97 | @property (strong, nonatomic) UIColor *themeColor;
98 |
99 | /**
100 | The background color of the layer that executes the animation
101 | */
102 | @property (strong, nonatomic) UIColor *animatedBackgroundColor;
103 |
104 | /**
105 | if called method "endRefreshingAndNoLongerRefreshingWithAlertText:" to end refresh,
106 | shouldNoLongerRefresh will set TRUE.
107 | */
108 | @property (assign, nonatomic,readonly, getter=isShouldNoLongerRefresh) BOOL shouldNoLongerRefresh;
109 | /**
110 | scrollview trigger refresh automatically that don't need to scroll to bottom.
111 | default is YES;
112 |
113 | ATTENTION:!!!
114 |
115 | if (no data) {
116 | [tableView.footRefreshControl endRefreshingAndNoLongerRefreshingWithAlertText:@"no more"];
117 | } else {
118 | [tableView.footRefreshControl endRefreshingWithAlertText:@"Did load successfully" completion:nil];
119 | }
120 | */
121 | @property (nonatomic, assign) BOOL autoRefreshOnFoot;
122 |
123 | /**
124 | Set the color of the prompt text after the refresh is completed.
125 |
126 | @param alertTextColor color
127 | */
128 | - (void)setAlertTextColor:(UIColor *)alertTextColor;
129 |
130 | /**
131 | Set the background color of the prompt text after the refresh is completed.
132 |
133 | @param alertBackgroundColor color
134 | */
135 | - (void)setAlertBackgroundColor:(UIColor *)alertBackgroundColor;
136 |
137 | /**
138 | Called right after initialization is completed
139 | */
140 | - (void)setupProperties Kafka_REQUIRES_SUPER;
141 |
142 | /**
143 | Subclasses override this method
144 | */
145 | - (void)privateContentOffsetOfScrollViewDidChange:(CGPoint)contentOffset Kafka_REQUIRES_SUPER;
146 | - (void)setScrollViewToRefreshLocation Kafka_REQUIRES_SUPER;
147 | - (void)setScrollViewToOriginalLocation:(dispatch_block_t)block Kafka_REQUIRES_SUPER;
148 |
149 | #pragma mark - public
150 |
151 | /**
152 | Trigger refresh
153 | */
154 | - (void)beginRefreshing Kafka_REQUIRES_SUPER;
155 |
156 | /**
157 | end refresh
158 | */
159 | - (void)endRefreshing Kafka_REQUIRES_SUPER;
160 |
161 | /**
162 | When this method is called to end the refresh, there will be a 1.5 second
163 | animated display of "text". Please note that the length of text, please
164 | try to be brief, otherwise it will be cut off.
165 |
166 | @param text default is nil, and no animation.
167 | @param completion when text is hidden, this block will be called.
168 | */
169 | - (void)endRefreshingWithAlertText:(NSString *)text completion:(dispatch_block_t)completion Kafka_REQUIRES_SUPER;
170 |
171 | /**
172 | Using this method means you clearly understand that refreshing
173 | is meaningless and refreshing will be disabled.
174 |
175 | @param text If the user continues to drag, it will display the “text”, and will not trigger refresh.
176 | */
177 | - (void)endRefreshingAndNoLongerRefreshingWithAlertText:(NSString *)text Kafka_REQUIRES_SUPER;
178 |
179 | /**
180 | After you call ‘endRefreshingAndNoLongerRefreshingWithAlertText’,
181 | you need to resume refresh available
182 | */
183 | - (void)resumeRefreshAvailable;
184 |
185 | @end
186 |
--------------------------------------------------------------------------------
/KafkaRefresh/Core/KafkaRefreshControl.m:
--------------------------------------------------------------------------------
1 | /*************************************************************
2 | * Copyright (c) 2016-present, K. *
3 | * All rights reserved. *
4 | * *
5 | * e-mail: xorshine@icloud.com *
6 | * github:https://github.com/xorshine *
7 | * *
8 | * This source code is licensed under the MIT license. *
9 | *************************************************************/
10 |
11 | #import "KafkaRefreshControl.h"
12 | #import "KafkaCategories.h"
13 |
14 | @interface KafkaLabel : UILabel
15 | - (void)startAnimating;
16 | @end
17 |
18 | @implementation KafkaLabel{
19 | CAGradientLayer * new;
20 | }
21 |
22 | - (instancetype)init{
23 | self = [super init];
24 | if (self) {
25 | new = [CAGradientLayer new];
26 | new.locations = @[@0.2,@0.5,@0.8];
27 | new.startPoint = CGPointMake(0, 0.5);
28 | new.endPoint = CGPointMake(1, 0.5);
29 | self.layer.masksToBounds = YES;
30 | [self.layer addSublayer:new];
31 | }
32 | return self;
33 | }
34 |
35 | - (void)layoutSubviews{
36 | new.frame = CGRectMake(0, 0, 0, self.kr_height);
37 | new.position = CGPointMake(self.kr_width/2.0, self.kr_height/2.);
38 | }
39 |
40 | - (void)startAnimating{
41 | __weak typeof(self) weakSelf = self;
42 | [UIView animateWithDuration:0.3 animations:^{
43 | weakSelf.alpha = 1.0;
44 | }];
45 | new.colors = @[(id)[self.textColor colorWithAlphaComponent:0.2].CGColor,
46 | (id)[self.textColor colorWithAlphaComponent:0.1].CGColor,
47 | (id)[self.textColor colorWithAlphaComponent:0.2].CGColor];
48 |
49 | CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"bounds.size.width"];
50 | animation.fromValue = @(0);
51 | animation.toValue = @(self.kr_width);
52 | animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
53 | animation.fillMode = kCAFillModeForwards;
54 | animation.duration = 0.3;
55 | animation.removedOnCompletion = NO;
56 | animation.delegate = self;
57 | [new addAnimation:animation forKey:animation.keyPath];
58 | }
59 |
60 | - (void)stopAnimating{
61 | __weak typeof(self) weakSelf = self;
62 | [UIView animateWithDuration:0.3 animations:^{
63 | weakSelf.alpha = 0.0;
64 | }];
65 | [new removeAllAnimations];
66 | }
67 |
68 | @end
69 |
70 | static NSString * const KafkaContentOffset = @"contentOffset";
71 | static NSString * const KafkaContentSize = @"contentSize";
72 | static CGFloat const KafkaRefreshHeight = 45.;
73 | static CGFloat const kStretchOffsetYAxisThreshold = 1.0;
74 |
75 | @interface KafkaRefreshControl()
76 | @property (nonatomic, weak) __kindof UIScrollView *scrollView;
77 | @property (nonatomic, getter=isRefresh) BOOL refresh;
78 | @property (assign, nonatomic,getter=isObservering) BOOL observering;
79 | @property (strong, nonatomic) KafkaLabel *alertLabel;
80 | @property (assign, nonatomic, getter=isShouldNoLongerRefresh) BOOL shouldNoLongerRefresh;
81 | @end
82 |
83 | @implementation KafkaRefreshControl
84 |
85 | - (instancetype)initWithFrame:(CGRect)frame{
86 | self = [super initWithFrame:frame];
87 | if (self) {
88 | [self setupProperties];
89 | }
90 | return self;
91 | }
92 |
93 | - (instancetype)initWithCoder:(NSCoder *)coder{
94 | self = [super initWithCoder:coder];
95 | if (self) {
96 | [self setupProperties];
97 | }
98 | return self;
99 | }
100 |
101 | - (void)setupProperties{
102 | self.backgroundColor = [UIColor clearColor];
103 | self.alpha = 0.;
104 | [self addSubview:self.alertLabel];
105 | _autoRefreshOnFoot = NO;
106 | _refreshState = KafkaRefreshStateNone;
107 | _stretchOffsetYAxisThreshold = kStretchOffsetYAxisThreshold;
108 | _shouldNoLongerRefresh = NO;
109 | _refresh = NO;
110 | if (CGRectEqualToRect(self.frame, CGRectZero)) self.frame = CGRectMake(0, 0, 1, 1);
111 | }
112 |
113 | - (void)setThemeColor:(UIColor *)themeColor{
114 | if (_themeColor != themeColor) {
115 | _themeColor = themeColor;
116 | _alertLabel.textColor = themeColor;
117 | }
118 | }
119 |
120 | - (void)setAlertTextColor:(UIColor *)alertTextColor{
121 | _alertLabel.textColor = alertTextColor;
122 | }
123 |
124 | - (void)setAlertBackgroundColor:(UIColor *)alertBackgroundColor{
125 | _alertLabel.backgroundColor = alertBackgroundColor;
126 | }
127 |
128 |
129 | - (void)setRefreshState:(KafkaRefreshState)refreshState{
130 | if (_refreshState == refreshState) return;
131 | _refreshState = refreshState;
132 | @weakify(self);
133 | switch (refreshState) {
134 | case KafkaRefreshStateNone:{
135 | [self setAnimateBlock:^{@strongify(self); self.alpha = 0.0; }];
136 | break;
137 | }
138 | case KafkaRefreshStateScrolling:{
139 | ////////////////////////////////////////////////////////////////////////////////////
140 | ///when system adjust contentOffset atuomatically,
141 | ///will trigger refresh control's state changed.
142 | if (!self.isTriggeredRefreshByUser && !self.scrollView.isTracking) {
143 | return;
144 | }
145 |
146 | [self setAnimateBlock:^{@strongify(self); self.alpha = 1.0; }];
147 | break;
148 | }
149 | case KafkaRefreshStateReady:{
150 | ////////////////////////////////////////////////////////////////////////////////////
151 | ///because of scrollView contentOffset is not continuous change.
152 | ///need to manually adjust progress
153 | if (self.progress < self.stretchOffsetYAxisThreshold) {
154 | [self kafkaDidScrollWithProgress:self.stretchOffsetYAxisThreshold max:self.stretchOffsetYAxisThreshold];
155 | }
156 | ////////////////////////////////////////////////////////////////////////////////////
157 | [self setAnimateBlock:^{@strongify(self); self.alpha = 1.0; }];
158 | break;
159 | }
160 | case KafkaRefreshStateRefreshing:{
161 | break;
162 | }
163 | case KafkaRefreshStateWillEndRefresh:{
164 | [self setAnimateBlock:^{@strongify(self); self.alpha = 1.0; }];
165 | break;
166 | }
167 | }
168 | if (self.shouldNoLongerRefresh) {
169 | self.alpha = 1.0;
170 | }
171 | [self kafkaRefreshStateDidChange:refreshState];
172 | }
173 |
174 | - (void)setProgress:(CGFloat)progress{
175 | if (_progress == progress) return;
176 | _progress = progress;
177 | [self kafkaDidScrollWithProgress:progress max:self.stretchOffsetYAxisThreshold];
178 | }
179 |
180 | - (void)setStretchOffsetYAxisThreshold:(CGFloat)stretchOffsetYAxisThreshold{
181 | if (_stretchOffsetYAxisThreshold != stretchOffsetYAxisThreshold && stretchOffsetYAxisThreshold > 1.0) {
182 | _stretchOffsetYAxisThreshold = stretchOffsetYAxisThreshold;
183 | }
184 | }
185 |
186 | - (BOOL)isRefresh{
187 | return (_refreshState == KafkaRefreshStateRefreshing);
188 | }
189 |
190 | #pragma mark - layout
191 |
192 | - (void)layoutSubviews{
193 | [super layoutSubviews];
194 |
195 | self.kr_height = (self.kr_height < 45.) ? KafkaRefreshHeight : self.kr_height;
196 | self.frame = CGRectMake(0, 0, self.scrollView.kr_width, self.kr_height);
197 | self.alertLabel.frame = self.bounds;
198 | }
199 |
200 | - (void)willMoveToSuperview:(UIView *)newSuperview {
201 | NSKeyValueObservingOptions options = NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld;
202 | if (self.superview && newSuperview == nil) {
203 | if (_observering) {
204 | [self.superview removeObserver:self forKeyPath:KafkaContentOffset];
205 | [self.superview removeObserver:self forKeyPath:KafkaContentSize];
206 | _observering = NO;
207 | }
208 | }
209 | else if (self.superview == nil && newSuperview) {
210 | if (!_observering) {
211 | _scrollView = (UIScrollView *)newSuperview;
212 | /**
213 | sometimes, this method called before `layoutSubviews`,such as UICollectionViewController
214 | */
215 | [self layoutIfNeeded];
216 | _presetContentInsets = ((UIScrollView *)newSuperview).realContentInset;
217 | [newSuperview addObserver:self forKeyPath:KafkaContentOffset options:options context:nil];
218 | [newSuperview addObserver:self forKeyPath:KafkaContentSize options:options context:nil];
219 | _observering = YES;
220 | }
221 | }
222 | }
223 |
224 | - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{
225 | if ([keyPath isEqualToString:KafkaContentOffset]) {
226 | /**
227 | If you disable the control's refresh feature, set the control to hidden
228 | */
229 | if (self.isHidden || self.shouldNoLongerRefresh) return;
230 |
231 | CGPoint point = [[change valueForKey:NSKeyValueChangeNewKey] CGPointValue];
232 | /**
233 | If you quickly scroll scrollview in an instant, contentoffset changes are not continuous
234 | */
235 | [self privateContentOffsetOfScrollViewDidChange:point];
236 | }
237 | else if([keyPath isEqualToString:KafkaContentSize]) {
238 | [self layoutSubviews];
239 | }
240 | }
241 |
242 | - (void)privateContentOffsetOfScrollViewDidChange:(CGPoint)contentOffset{}
243 |
244 | - (void)beginRefreshing{
245 | if (self.refreshState != KafkaRefreshStateNone || self.isHidden || self.triggeredRefreshByUser) return;
246 | if (self.isShouldNoLongerRefresh) self.alertLabel.hidden = YES;
247 | self.shouldNoLongerRefresh = NO;
248 | self.triggeredRefreshByUser = YES;
249 | [self setScrollViewToRefreshLocation];
250 | }
251 |
252 | - (void)setScrollViewToRefreshLocation{
253 | self.animating = YES;
254 | }
255 |
256 | - (void)endRefreshing{
257 | [self endRefreshingWithAlertText:nil completion:nil];
258 | }
259 |
260 | - (void)endRefreshingWithAlertText:(NSString *)text completion:(dispatch_block_t)completion {
261 | if((!self.isRefresh && !self.isAnimating) || self.isHidden) return;
262 | if (text) {
263 | [self bringSubviewToFront:self.alertLabel];
264 | self.alertLabel.text = text;
265 | [self.alertLabel startAnimating];
266 | @weakify(self);
267 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
268 | @strongify(self);
269 | [self.alertLabel stopAnimating];
270 | [self _endRefresh:completion];
271 | });
272 | } else {
273 | [self _endRefresh:nil];
274 | }
275 | }
276 |
277 | - (void)endRefreshingAndNoLongerRefreshingWithAlertText:(NSString *)text{
278 | if (self.isHidden) return;
279 | if (self.isShouldNoLongerRefresh) return;
280 |
281 | self.shouldNoLongerRefresh = YES;
282 |
283 | @weakify(self);
284 | if (self.alertLabel.alpha == 0.0) {
285 | [UIView animateWithDuration:0.3 animations:^{
286 | @strongify(self);
287 | self.alertLabel.alpha = 1.0;
288 | }];
289 | }
290 | [self bringSubviewToFront:self.alertLabel];
291 | self.alertLabel.text = text;
292 | if (text) {
293 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
294 | @strongify(self);
295 | [self _endRefresh:nil];
296 | });
297 | } else {
298 | [self _endRefresh:nil];
299 | }
300 | }
301 |
302 | - (void)resumeRefreshAvailable{
303 | self.shouldNoLongerRefresh = NO;
304 | self.alertLabel.alpha = 0.0;
305 | }
306 |
307 | - (void)_endRefresh:(dispatch_block_t)completion{
308 | [self kafkaRefreshStateDidChange:KafkaRefreshStateWillEndRefresh];
309 | self.refreshState = KafkaRefreshStateScrolling;
310 | [self setScrollViewToOriginalLocation:completion];
311 | }
312 |
313 | - (void)setScrollViewToOriginalLocation:(dispatch_block_t)completion{}
314 |
315 | #pragma mark -
316 |
317 | #pragma mark - progress callback
318 |
319 | - (void)kafkaRefreshStateDidChange:(KafkaRefreshState)state{}
320 |
321 | - (void)kafkaDidScrollWithProgress:(CGFloat)progress max:(const CGFloat)max{}
322 |
323 | #pragma mark - getter
324 |
325 | - (KafkaLabel *)alertLabel{
326 | if (!_alertLabel) {
327 | _alertLabel = [KafkaLabel new];
328 | _alertLabel.textAlignment = NSTextAlignmentCenter;
329 | _alertLabel.font = [UIFont fontWithName:@"Helvetica" size:15.f];
330 | _alertLabel.textColor = _themeColor;
331 | _alertLabel.alpha = 0.0;
332 | _alertLabel.backgroundColor = [UIColor whiteColor];
333 | }
334 | return _alertLabel;
335 | }
336 |
337 | @end
338 |
--------------------------------------------------------------------------------
/KafkaRefresh/Core/KafkaRefreshProtocol.h:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, K.
3 | * All rights reserved.
4 | *
5 | * e-mail:xiaohuiprivate@gmail.com
6 | * github:https://github.com/xorshine
7 | *
8 | * This source code is licensed under the MIT license.
9 | */
10 |
11 | #ifndef KafkaRefreshProtocol_h
12 | #define KafkaRefreshProtocol_h
13 | #import
14 |
15 | /**
16 | - KafkaRefreshStateNone: Refresh control is not visible, and non-refresh state
17 | - KafkaRefreshStateScrolling: Refresh control visible, UIScrollView sliding
18 | - KafkaRefreshStateReady: release your hand to refresh
19 | - KafkaRefreshStateRefreshing: refreshing
20 | - KafkaRefreshStateWillEndRefresh: the end of the refresh will begin to slide to the original position
21 | */
22 | typedef NS_ENUM(NSInteger,KafkaRefreshState) {
23 | KafkaRefreshStateNone = 1,
24 | KafkaRefreshStateScrolling,
25 | KafkaRefreshStateReady,
26 | KafkaRefreshStateRefreshing,
27 | KafkaRefreshStateWillEndRefresh
28 | };
29 |
30 | @protocol KafkaRefreshControlProtocol
31 |
32 | @end
33 |
34 |
35 | @protocol KafkaRefreshProgressDelegate
36 | @required
37 |
38 | /**
39 | * when the state of the refresh control changes, the method is called
40 | *
41 | * @state KafkaRefreshState
42 | */
43 | - (void)kafkaRefreshStateDidChange:(KafkaRefreshState)state;
44 |
45 | /**
46 | * when the state of the refresh control changes, the method is called
47 | *
48 | * @progress the current position offset of the control as a percentage of the offset that triggered the refresh
49 | *
50 | * @max the offset that triggered the refresh
51 | */
52 | - (void)kafkaDidScrollWithProgress:(CGFloat)progress max:(const CGFloat)max;
53 |
54 | @end
55 |
56 | #endif /* KafkaRefreshProtocol_h */
57 |
--------------------------------------------------------------------------------
/KafkaRefresh/Core/UIScrollView+KafkaRefreshControl.h:
--------------------------------------------------------------------------------
1 | /*************************************************************
2 | * Copyright (c) 2016-present, K. *
3 | * All rights reserved. *
4 | * *
5 | * e-mail: xorshine@icloud.com *
6 | * github:https://github.com/xorshine *
7 | * *
8 | * This source code is licensed under the MIT license. *
9 | *************************************************************/
10 |
11 | #import
12 |
13 | @class KafkaHeadRefreshControl,KafkaFootRefreshControl;
14 |
15 | @interface UIScrollView (KafkaRefreshControl)
16 |
17 | /**
18 | * Bind the UIScrollView to the refresh control, dynamic binding at runtime
19 | */
20 | @property (strong, nonatomic) __kindof KafkaHeadRefreshControl *headRefreshControl;
21 | @property (strong, nonatomic) __kindof KafkaFootRefreshControl *footRefreshControl;
22 |
23 | @end
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/KafkaRefresh/Core/UIScrollView+KafkaRefreshControl.m:
--------------------------------------------------------------------------------
1 | /*************************************************************
2 | * Copyright (c) 2016-present, K. *
3 | * All rights reserved. *
4 | * *
5 | * e-mail: xorshine@icloud.com *
6 | * github:https://github.com/xorshine *
7 | * *
8 | * This source code is licensed under the MIT license. *
9 | *************************************************************/
10 |
11 | #import "UIScrollView+KafkaRefreshControl.h"
12 | #import
13 | #import "KafkaHeadRefreshControl.h"
14 | #import "KafkaFootRefreshControl.h"
15 |
16 | #define KafkaSelfSetValueRetainNonatomicAndKVOChange(keyPath,key,value) \
17 | [self willChangeValueForKey:(keyPath)]; \
18 | objc_setAssociatedObject(self,(key),(value),OBJC_ASSOCIATION_RETAIN_NONATOMIC); \
19 | [self addSubview:value]; \
20 | [self didChangeValueForKey:(keyPath)];
21 |
22 | const void * KafkaHeadRefreshKey = &KafkaHeadRefreshKey;
23 | const void * KafkaFootRefreshKey = &KafkaFootRefreshKey;
24 |
25 | NSString * KafkaHeadKeyPath = @"KafkaHeadKeyPath";
26 | NSString * KafkaFootKeyPath = @"KafkaHeadKeyPath";
27 |
28 | @implementation UIScrollView (KafkaRefreshControl)
29 |
30 | #pragma mark - setter and getter
31 |
32 | - (void)setHeadRefreshControl:(__kindof KafkaHeadRefreshControl *)headRefreshControl{
33 | if (headRefreshControl != self.headRefreshControl) {
34 | if (self.headRefreshControl) [self.headRefreshControl removeFromSuperview];
35 | KafkaSelfSetValueRetainNonatomicAndKVOChange(KafkaHeadKeyPath, KafkaHeadRefreshKey, headRefreshControl)
36 | }
37 | }
38 |
39 | - (KafkaHeadRefreshControl *)headRefreshControl{
40 | return objc_getAssociatedObject(self, KafkaHeadRefreshKey);
41 | }
42 |
43 | - (void)setFootRefreshControl:(__kindof KafkaFootRefreshControl *)footRefreshControl{
44 | if (footRefreshControl != self.footRefreshControl) {
45 | if (self.footRefreshControl) [self.footRefreshControl removeFromSuperview];
46 | KafkaSelfSetValueRetainNonatomicAndKVOChange(KafkaFootKeyPath, KafkaFootRefreshKey, footRefreshControl);
47 | }
48 | }
49 |
50 | - (KafkaFootRefreshControl *)footRefreshControl{
51 | return objc_getAssociatedObject(self, KafkaFootRefreshKey);
52 | }
53 |
54 | @end
55 |
--------------------------------------------------------------------------------
/KafkaRefresh/Default/KafkaRefreshDefaults.h:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, K.
3 | * All rights reserved.
4 | *
5 | * Email:xorshine@icloud.com
6 | *
7 | * This source code is licensed under the MIT license.
8 | */
9 |
10 | #import
11 | #import "KafkaRefreshStyle.h"
12 |
13 | /**
14 | Global configuration class
15 | */
16 | @interface KafkaRefreshDefaults : NSObject
17 |
18 | /**
19 | Return a singleton
20 | */
21 | + (instancetype)standardRefreshDefaults;
22 |
23 | /**
24 | Head refresh control style
25 | */
26 | @property (assign, nonatomic) KafkaRefreshStyle headDefaultStyle;
27 |
28 | /**
29 | Foot refresh control style
30 | */
31 | @property (assign, nonatomic) KafkaRefreshStyle footDefaultStyle;
32 |
33 | /**
34 | default R G B --> 28. 164. 252.
35 | */
36 | @property (strong, nonatomic) UIColor * themeColor;
37 |
38 | /**
39 | refresh control‘s backgroundColor
40 | */
41 | @property (strong, nonatomic) UIColor * backgroundColor;
42 |
43 | /**
44 | Head drag to display the text
45 | */
46 | @property (copy, nonatomic) NSString * headPullingText;
47 |
48 | /**
49 | Foot drag to display the text
50 | */
51 | @property (copy, nonatomic) NSString * footPullingText;
52 |
53 | /**
54 | The text displayed when you release your finger before releasing it
55 | */
56 | @property (copy, nonatomic) NSString * readyText;
57 |
58 | /**
59 | Text displayed on refresh
60 | */
61 | @property (copy, nonatomic) NSString * refreshingText;
62 |
63 | @end
64 |
--------------------------------------------------------------------------------
/KafkaRefresh/Default/KafkaRefreshDefaults.m:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, K.
3 | * All rights reserved.
4 | *
5 | * Email:xorshine@icloud.com
6 | *
7 | * This source code is licensed under the MIT license.
8 | */
9 |
10 | #import "KafkaRefreshDefaults.h"
11 |
12 | #define KafkaColorWithRGBA(r,g,b,a) \
13 | [UIColor colorWithRed:(r)/255. green:(g)/255. blue:(b)/255. alpha:(a)]
14 |
15 | @implementation KafkaRefreshDefaults
16 |
17 | + (instancetype)standardRefreshDefaults{
18 | static KafkaRefreshDefaults *defaults = nil;
19 | static dispatch_once_t onceToken;
20 | dispatch_once(&onceToken, ^{
21 | defaults = [[KafkaRefreshDefaults alloc] init];
22 | });
23 | return defaults;
24 | }
25 |
26 | - (instancetype)init{
27 | if (self = [super init]) {
28 | _headDefaultStyle = KafkaRefreshStyleAnimatableArrow;
29 | _footDefaultStyle = KafkaRefreshStyleNative;
30 |
31 | _themeColor = KafkaColorWithRGBA(28., 164., 252., 1.0);
32 | _backgroundColor = [UIColor whiteColor];
33 |
34 | _headPullingText = @"继续下拉";
35 | _footPullingText = @"继续上拉";
36 | _readyText = @"松开刷新";
37 | _refreshingText = @"正在加载";
38 | }
39 | return self;
40 | }
41 |
42 | @end
43 |
--------------------------------------------------------------------------------
/KafkaRefresh/KafkaRefresh.h:
--------------------------------------------------------------------------------
1 | /*************************************************************
2 | * Copyright (c) 2016-present, K.
3 | * All rights reserved.
4 | *
5 | * e-mail: xorshine@icloud.com
6 | * github:https://github.com/xorshine
7 | *
8 | * This source code is licensed under the MIT license.
9 | *************************************************************/
10 |
11 | #if __has_include()
12 | #import
13 | #import
14 | #import
15 | #import
16 | #import
17 | #import
18 | #import
19 | #else
20 | #import "UIScrollView+KafkaRefreshControl.h"
21 | #import "UIScrollView+KafkaConfiguration.h"
22 | #import "KafkaHeadRefreshControl.h"
23 | #import "KafkaFootRefreshControl.h"
24 | #import "KafkaRefreshDefaults.h"
25 | #import "KafkaRefreshProtocol.h"
26 | #import "KafkaRefreshStyle.h"
27 | #endif
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/KafkaRefresh/Resource/Image.bundle/arrow48.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BeatsKitano/KafkaRefresh/7d3bb0a5ce8d6ab11e976398cff8e52332f7ff63/KafkaRefresh/Resource/Image.bundle/arrow48.png
--------------------------------------------------------------------------------
/KafkaRefresh/Style/KafkaRefreshStyle.h:
--------------------------------------------------------------------------------
1 | /*************************************************************
2 | * Copyright (c) 2016-present, K.
3 | * All rights reserved.
4 | *
5 | * e-mail: xorshine@icloud.com
6 | * github:https://github.com/xorshine
7 | *
8 | * This source code is licensed under the MIT license.
9 | *************************************************************/
10 |
11 |
12 | typedef NS_ENUM(NSInteger,KafkaRefreshStyle) {
13 | //Native
14 | KafkaRefreshStyleNative = 0,
15 |
16 | //Replicator
17 | KafkaRefreshStyleReplicatorWoody,
18 | KafkaRefreshStyleReplicatorAllen,
19 | KafkaRefreshStyleReplicatorCircle,
20 | KafkaRefreshStyleReplicatorDot,
21 | KafkaRefreshStyleReplicatorArc,
22 | KafkaRefreshStyleReplicatorTriangle,
23 |
24 | //Ring
25 | KafkaRefreshStyleAnimatableRing,
26 | KafkaRefreshStyleAnimatableArrow
27 | };
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/KafkaRefresh/UIKit/FootKit/KafkaArrowFooter.h:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, K.
3 | * All rights reserved.
4 | *
5 | * e-mail:xorshine@icloud.com
6 | * github:https://github.com/xorshine
7 | *
8 | * This source code is licensed under the MIT license.
9 | */
10 |
11 | #import "KafkaFootRefreshControl.h"
12 |
13 | @interface KafkaArrowFooter : KafkaFootRefreshControl
14 |
15 | /**
16 | Foot drag to display the text
17 | */
18 | @property (copy, nonatomic) NSString * pullingText;
19 |
20 | /**
21 | The text displayed when you release your finger before releasing it
22 | */
23 | @property (copy, nonatomic) NSString * readyText;
24 |
25 | /**
26 | Text displayed on refresh
27 | */
28 | @property (copy, nonatomic) NSString * refreshingText;
29 |
30 | @end
31 |
--------------------------------------------------------------------------------
/KafkaRefresh/UIKit/FootKit/KafkaArrowFooter.m:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, K.
3 | * All rights reserved.
4 | *
5 | * e-mail:xorshine@icloud.com
6 | * github:https://github.com/xorshine
7 | *
8 | * This source code is licensed under the MIT license.
9 | */
10 |
11 | #import "KafkaArrowFooter.h"
12 | #import "KafkaRefreshDefaults.h"
13 | #import "KafkaCategories.h"
14 |
15 | @interface KafkaArrowFooter()
16 |
17 | @property (strong, nonatomic) UIImageView * arrowImgV;
18 | @property (strong, nonatomic) UILabel * promptlabel;
19 | @property (strong, nonatomic) UIActivityIndicatorView * indicator;
20 |
21 | @end
22 |
23 | @implementation KafkaArrowFooter
24 |
25 | - (void)setupProperties{
26 | [super setupProperties];
27 | [self addSubview:self.arrowImgV];
28 | [self addSubview:self.promptlabel];
29 | [self addSubview:self.indicator];
30 |
31 | _pullingText = [KafkaRefreshDefaults standardRefreshDefaults].footPullingText;
32 | _readyText = [KafkaRefreshDefaults standardRefreshDefaults].readyText;
33 | _refreshingText = [KafkaRefreshDefaults standardRefreshDefaults].refreshingText;
34 | }
35 |
36 | - (void)layoutSubviews{
37 | [super layoutSubviews];
38 |
39 | [self.promptlabel sizeToFit];
40 | self.promptlabel.center = CGPointMake(self.kr_width/2.0, self.kr_height/2.);
41 |
42 | self.arrowImgV.frame = CGRectMake(0, 0, 12, 12);
43 | self.arrowImgV.kr_right = self.promptlabel.kr_left-20.;
44 | self.arrowImgV.kr_top = self.promptlabel.kr_centerY;
45 |
46 | self.indicator.center = self.arrowImgV.center;
47 | }
48 |
49 | - (void)kafkaDidScrollWithProgress:(CGFloat)progress max:(const CGFloat)max{
50 |
51 |
52 | }
53 |
54 | - (void)kafkaRefreshStateDidChange:(KafkaRefreshState)state{
55 | [super kafkaRefreshStateDidChange:state];
56 | __weak typeof(self) weakSelf = self;
57 | switch (state) {
58 | case KafkaRefreshStateNone:{
59 | self.arrowImgV.hidden = NO;
60 | [_indicator stopAnimating];
61 | [UIView animateWithDuration:0.3 animations:^{
62 | weakSelf.arrowImgV.transform = CGAffineTransformMakeRotation(M_PI);
63 | }];
64 | break;
65 | }
66 | case KafkaRefreshStateScrolling:{
67 | [_indicator stopAnimating];
68 | self.promptlabel.text = _pullingText;
69 | [self.promptlabel sizeToFit];
70 | __weak typeof(self) weakSelf = self;
71 | [UIView animateWithDuration:0.3 animations:^{
72 | weakSelf.arrowImgV.transform = CGAffineTransformMakeRotation(M_PI);
73 | }];
74 | break;
75 | }
76 | case KafkaRefreshStateReady:{
77 | [_indicator stopAnimating];
78 | self.promptlabel.text = _readyText;
79 | [UIView animateWithDuration:0.3 animations:^{
80 | weakSelf.arrowImgV.transform = CGAffineTransformMakeRotation(M_PI*2);
81 | }];
82 | break;
83 | }
84 | case KafkaRefreshStateRefreshing:{
85 | self.promptlabel.text = _refreshingText;
86 | self.arrowImgV.hidden = YES;
87 | [_indicator startAnimating];
88 | break;
89 | }
90 | case KafkaRefreshStateWillEndRefresh:{
91 | [_indicator stopAnimating];
92 | break;
93 | }
94 | }
95 | }
96 |
97 | #pragma mark - getter
98 |
99 | - (UIImageView *)arrowImgV{
100 | if (!_arrowImgV) {
101 | NSBundle *b = [NSBundle bundleWithPath:[[NSBundle bundleForClass:[self class]] pathForResource:@"Image" ofType:@"bundle"]];
102 | NSString *path = [b pathForResource:@"arrow48.png" ofType:nil];
103 | UIImage *image = [UIImage imageWithContentsOfFile:path];
104 | _arrowImgV = [[UIImageView alloc] initWithImage:image];
105 | _arrowImgV.layer.anchorPoint = CGPointMake(0.5, 0);
106 | }
107 | return _arrowImgV;
108 | }
109 |
110 | - (UILabel *)promptlabel{
111 | if (!_promptlabel) {
112 | _promptlabel = [UILabel new];
113 | _promptlabel.textAlignment = NSTextAlignmentCenter;
114 | _promptlabel.textColor = KafkaColorWithRGBA(100.,100.,100.,1.0);
115 | if (@available(iOS 8.2, *))
116 | _promptlabel.font = [UIFont systemFontOfSize:11. weight:UIFontWeightThin];
117 | else
118 | _promptlabel.font = [UIFont systemFontOfSize:11.];
119 | }
120 | return _promptlabel;
121 | }
122 |
123 | - (UIActivityIndicatorView *)indicator{
124 | if (!_indicator) {
125 | _indicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
126 | }
127 | return _indicator;
128 | }
129 |
130 | @end
131 |
--------------------------------------------------------------------------------
/KafkaRefresh/UIKit/FootKit/KafkaNativeFooter.h:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, K.
3 | * All rights reserved.
4 | *
5 | * Email:xorshine@icloud.com
6 | *
7 | * This source code is licensed under the MIT license.
8 | */
9 |
10 | #import "KafkaFootRefreshControl.h"
11 |
12 | @interface KafkaNativeFooter : KafkaFootRefreshControl
13 |
14 | @end
15 |
--------------------------------------------------------------------------------
/KafkaRefresh/UIKit/FootKit/KafkaNativeFooter.m:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, K.
3 | * All rights reserved.
4 | *
5 | * Email:xorshine@icloud.com
6 | *
7 | * This source code is licensed under the MIT license.
8 | */
9 |
10 | #import "KafkaNativeFooter.h"
11 | #import "KafkaCategories.h"
12 |
13 | @interface KafkaNativeFooter()
14 |
15 | @property (strong, nonatomic) UIActivityIndicatorView * indicator;
16 |
17 | @end
18 |
19 | @implementation KafkaNativeFooter
20 |
21 | - (instancetype)initWithFrame:(CGRect)frame{
22 | self = [super initWithFrame:frame];
23 | if (self) {
24 | [self addSubview:self.indicator];
25 | }
26 | return self;
27 | }
28 |
29 | - (void)layoutSubviews{
30 | [super layoutSubviews];
31 |
32 | self.indicator.center = CGPointMake(self.kr_width/2., self.kr_height/2.);
33 | }
34 |
35 | - (void)kafkaDidScrollWithProgress:(CGFloat)progress max:(const CGFloat)max{
36 |
37 | }
38 |
39 | - (void)kafkaRefreshStateDidChange:(KafkaRefreshState)state{
40 | [super kafkaRefreshStateDidChange:state];
41 | switch (state) {
42 | case KafkaRefreshStateNone:{
43 | break;
44 | }
45 | case KafkaRefreshStateScrolling:{
46 | break;
47 | }
48 | case KafkaRefreshStateReady:{
49 | break;
50 | }
51 | case KafkaRefreshStateRefreshing:{
52 | [self.indicator startAnimating];
53 | break;
54 | }
55 | case KafkaRefreshStateWillEndRefresh:{
56 | [self.indicator stopAnimating];
57 | break;
58 | }
59 | }
60 | }
61 |
62 | #pragma mark - lazy
63 |
64 | - (UIActivityIndicatorView *)indicator{
65 | if (!_indicator) {
66 | if (@available(iOS 13.0, *)) {
67 | _indicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleMedium];
68 | } else {
69 | _indicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
70 | }
71 | _indicator.hidesWhenStopped = NO;
72 | }
73 | return _indicator;
74 | }
75 |
76 | @end
77 |
--------------------------------------------------------------------------------
/KafkaRefresh/UIKit/FootKit/KafkaReplicatorFooter.h:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, K.
3 | * All rights reserved.
4 | *
5 | * e-mail:xorshine@icloud.com
6 | * github:https://github.com/xorshine
7 | *
8 | * This source code is licensed under the MIT license.
9 | */
10 |
11 | #import "KafkaFootRefreshControl.h"
12 | #import "KafkaReplicatorLayer.h"
13 |
14 | @interface KafkaReplicatorFooter : KafkaFootRefreshControl
15 |
16 | @property (strong, nonatomic) KafkaReplicatorLayer * replicatorLayer;
17 | @property (assign, nonatomic) KafkaReplicatorLayerAnimationStyle animationStyle;
18 |
19 | @end
20 |
--------------------------------------------------------------------------------
/KafkaRefresh/UIKit/FootKit/KafkaReplicatorFooter.m:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, K.
3 | * All rights reserved.
4 | *
5 | * e-mail:xorshine@icloud.com
6 | * github:https://github.com/xorshine
7 | *
8 | * This source code is licensed under the MIT license.
9 | */
10 |
11 | #import "KafkaReplicatorFooter.h"
12 | #import "KafkaCategories.h"
13 |
14 | @implementation KafkaReplicatorFooter
15 |
16 | - (void)setupProperties{
17 | [super setupProperties];
18 | [self.layer addSublayer:self.replicatorLayer];
19 | }
20 |
21 | - (void)layoutSubviews{
22 | [super layoutSubviews];
23 | self.replicatorLayer.frame = CGRectMake(0, 0, self.kr_width, self.kr_height);
24 | }
25 |
26 | - (void)setThemeColor:(UIColor *)themeColor{
27 | [super setThemeColor:themeColor];
28 | self.replicatorLayer.themeColor = themeColor;
29 | }
30 |
31 | - (void)setAnimationStyle:(KafkaReplicatorLayerAnimationStyle)animationStyle{
32 | _animationStyle = animationStyle;
33 | self.replicatorLayer.animationStyle = animationStyle;
34 | }
35 |
36 | - (void)kafkaDidScrollWithProgress:(CGFloat)progress max:(const CGFloat)max{
37 | #define kOffset 0.7
38 | if (progress >= 0.8) {
39 | progress = (progress-kOffset)/(max - kOffset);
40 | }
41 | switch (self.animationStyle) {
42 | case KafkaReplicatorLayerAnimationStyleWoody:{
43 | break;
44 | }
45 | case KafkaReplicatorLayerAnimationStyleAllen:{
46 |
47 | break;
48 | }
49 | case KafkaReplicatorLayerAnimationStyleCircle:{
50 |
51 | break;
52 | }
53 | case KafkaReplicatorLayerAnimationStyleDot:{
54 |
55 | break;
56 | }
57 | case KafkaReplicatorLayerAnimationStyleArc:{
58 | self.replicatorLayer.indicatorShapeLayer.strokeEnd = progress;
59 | break;
60 | }
61 | case KafkaReplicatorLayerAnimationStyleTriangle:{
62 |
63 | break;
64 | }
65 | }
66 | }
67 |
68 | - (void)kafkaRefreshStateDidChange:(KafkaRefreshState)state{
69 | [super kafkaRefreshStateDidChange:state];
70 | switch (state) {
71 | case KafkaRefreshStateNone:{
72 | break;
73 | }
74 | case KafkaRefreshStateScrolling:{
75 | break;
76 | }
77 | case KafkaRefreshStateReady:{
78 | self.replicatorLayer.opacity = 1.;
79 | break;
80 | }
81 | case KafkaRefreshStateRefreshing:{
82 | [self.replicatorLayer startAnimating];
83 | break;
84 | }
85 | case KafkaRefreshStateWillEndRefresh:{
86 | [self.replicatorLayer stopAnimating];
87 | break;
88 | }
89 | }
90 | }
91 |
92 | - (KafkaReplicatorLayer *)replicatorLayer{
93 | if (!_replicatorLayer) {
94 | _replicatorLayer = [KafkaReplicatorLayer layer];
95 | }
96 | return _replicatorLayer;
97 | }
98 |
99 | @end
100 |
--------------------------------------------------------------------------------
/KafkaRefresh/UIKit/FootKit/KafkaRingIndicatorFooter.h:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, K.
3 | * All rights reserved.
4 | *
5 | * e-mail:xorshine@icloud.com
6 | * github:https://github.com/xorshine
7 | *
8 | * This source code is licensed under the MIT license.
9 | */
10 |
11 | #import "KafkaFootRefreshControl.h"
12 | #import "KafkaArcLayer.h"
13 |
14 | @interface KafkaRingIndicatorFooter : KafkaFootRefreshControl
15 |
16 | @property (strong, nonatomic) KafkaArcLayer * arcLayer;
17 | @property (strong, nonatomic) UIActivityIndicatorView * indicator;
18 |
19 | @end
20 |
--------------------------------------------------------------------------------
/KafkaRefresh/UIKit/FootKit/KafkaRingIndicatorFooter.m:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, K.
3 | * All rights reserved.
4 | *
5 | * e-mail:xorshine@icloud.com
6 | * github:https://github.com/xorshine
7 | *
8 | * This source code is licensed under the MIT license.
9 | */
10 |
11 | #import "KafkaRingIndicatorFooter.h"
12 | #import "KafkaCategories.h"
13 |
14 | @implementation KafkaRingIndicatorFooter
15 |
16 | - (void)setupProperties{
17 | [super setupProperties];
18 | [self.layer addSublayer:self.arcLayer];
19 | [self addSubview:self.indicator];
20 | }
21 |
22 | - (void)layoutSubviews{
23 | [super layoutSubviews];
24 | self.arcLayer.frame = CGRectMake(0, 0, self.kr_width, self.kr_height);
25 | self.indicator.center = CGPointMake(self.kr_width/2., self.kr_height/2.);
26 | }
27 |
28 | - (void)setThemeColor:(UIColor *)fillColor{
29 | if (super.themeColor == fillColor) {
30 | return;
31 | }
32 | [super setThemeColor:fillColor];
33 | self.arcLayer.ringFillColor = fillColor;
34 | }
35 |
36 | - (void)setAnimatedBackgroundColor:(UIColor *)animatedBackgroundColor{
37 | if (super.animatedBackgroundColor == animatedBackgroundColor) {
38 | return;
39 | }
40 | [super setAnimatedBackgroundColor:animatedBackgroundColor];
41 | self.arcLayer.ringBackgroundColor = animatedBackgroundColor;
42 | }
43 |
44 | - (void)kafkaDidScrollWithProgress:(CGFloat)progress max:(const CGFloat)max{
45 | #define kOffset 0.3
46 | if (progress >= kOffset) {
47 | progress = (progress-kOffset)/(max - kOffset);
48 | [self.arcLayer setProgress:progress];
49 | }
50 | }
51 |
52 | - (void)kafkaRefreshStateDidChange:(KafkaRefreshState)state{
53 | [super kafkaRefreshStateDidChange:state];
54 | switch (state) {
55 | case KafkaRefreshStateNone:{
56 | break;
57 | }
58 | case KafkaRefreshStateScrolling:{
59 | break;
60 | }
61 | case KafkaRefreshStateReady:{
62 | self.arcLayer.opacity = 1.;
63 | break;
64 | }
65 | case KafkaRefreshStateRefreshing:{
66 | [self.arcLayer startAnimating];
67 | self.arcLayer.hidden = YES;
68 | [self.indicator startAnimating];
69 | break;
70 | }
71 | case KafkaRefreshStateWillEndRefresh:{
72 | [self.indicator stopAnimating];
73 | self.arcLayer.hidden = NO;
74 | [self.arcLayer stopAnimating];
75 | break;
76 | }
77 | }
78 | }
79 |
80 | - (KafkaArcLayer *)arcLayer{
81 | if (!_arcLayer) {
82 | _arcLayer = [KafkaArcLayer layer];
83 | }
84 | return _arcLayer;
85 | }
86 |
87 | - (UIActivityIndicatorView *)indicator{
88 | if (!_indicator) {
89 | _indicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
90 | _indicator.hidesWhenStopped = YES;
91 | }
92 | return _indicator;
93 | }
94 |
95 | @end
96 |
--------------------------------------------------------------------------------
/KafkaRefresh/UIKit/HeadKit/KafkaArrowHeader.h:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, K.
3 | * All rights reserved.
4 | *
5 | * e-mail:xorshine@icloud.com
6 | * github:https://github.com/xorshine
7 | *
8 | * This source code is licensed under the MIT license.
9 | */
10 |
11 | #import "KafkaHeadRefreshControl.h"
12 |
13 | @interface KafkaArrowHeader : KafkaHeadRefreshControl
14 |
15 | /**
16 | Foot drag to display the text
17 | */
18 | @property (copy, nonatomic) NSString * pullingText;
19 |
20 | /**
21 | The text displayed when you release your finger before releasing it
22 | */
23 | @property (copy, nonatomic) NSString * readyText;
24 |
25 | /**
26 | Text displayed on refresh
27 | */
28 | @property (copy, nonatomic) NSString * refreshingText;
29 |
30 | @end
31 |
--------------------------------------------------------------------------------
/KafkaRefresh/UIKit/HeadKit/KafkaArrowHeader.m:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, K.
3 | * All rights reserved.
4 | *
5 | * e-mail:xorshine@icloud.com
6 | * github:https://github.com/xorshine
7 | *
8 | * This source code is licensed under the MIT license.
9 | */
10 |
11 | #import "KafkaArrowHeader.h"
12 | #import "KafkaRefreshDefaults.h"
13 | #import "KafkaCategories.h"
14 |
15 | @interface KafkaArrowHeader()
16 |
17 | @property (strong, nonatomic) UIImageView * arrowImgV;
18 | @property (strong, nonatomic) UILabel * promptlabel;
19 | @property (strong, nonatomic) UIActivityIndicatorView * indicator;
20 |
21 | @end
22 |
23 | @implementation KafkaArrowHeader
24 |
25 | - (void)setupProperties{
26 | [super setupProperties];
27 | [self addSubview:self.arrowImgV];
28 | [self addSubview:self.promptlabel];
29 | [self addSubview:self.indicator];
30 |
31 | _pullingText = [KafkaRefreshDefaults standardRefreshDefaults].headPullingText;
32 | _readyText = [KafkaRefreshDefaults standardRefreshDefaults].readyText;
33 | _refreshingText = [KafkaRefreshDefaults standardRefreshDefaults].refreshingText;
34 | }
35 |
36 | - (void)layoutSubviews{
37 | [super layoutSubviews];
38 |
39 | self.promptlabel.center = CGPointMake(self.kr_width/2.0, self.kr_height/2.);
40 |
41 | self.arrowImgV.frame = CGRectMake(0, 0, 12, 12);
42 | self.arrowImgV.kr_right = self.promptlabel.kr_left-20.;
43 | self.arrowImgV.kr_top = self.promptlabel.kr_centerY;
44 |
45 | self.indicator.center = self.arrowImgV.center;
46 | }
47 |
48 | - (void)kafkaDidScrollWithProgress:(CGFloat)progress max:(const CGFloat)max{
49 |
50 | }
51 |
52 | - (void)kafkaRefreshStateDidChange:(KafkaRefreshState)state{
53 | [super kafkaRefreshStateDidChange:state];
54 | __weak typeof(self) weakSelf = self;
55 | switch (state) {
56 | case KafkaRefreshStateNone:{
57 | self.arrowImgV.hidden = NO;
58 | [_indicator stopAnimating];
59 | [UIView animateWithDuration:0.3 animations:^{
60 | weakSelf.arrowImgV.transform = CGAffineTransformIdentity;
61 | }];
62 | break;
63 | }
64 | case KafkaRefreshStateScrolling:{
65 | self.promptlabel.text = _pullingText;
66 | [self.promptlabel sizeToFit];
67 | [UIView animateWithDuration:0.3 animations:^{
68 | weakSelf.arrowImgV.transform = CGAffineTransformIdentity;
69 | }];
70 | break;
71 | }
72 | case KafkaRefreshStateReady:{
73 | [_indicator stopAnimating];
74 | self.promptlabel.text = _readyText;
75 | [UIView animateWithDuration:0.3 animations:^{
76 | weakSelf.arrowImgV.transform = CGAffineTransformMakeRotation(M_PI);
77 | }];
78 | break;
79 | }
80 | case KafkaRefreshStateRefreshing:{
81 | self.promptlabel.text = _refreshingText;
82 | self.arrowImgV.hidden = YES;
83 | [_indicator startAnimating];
84 | break;
85 | }
86 | case KafkaRefreshStateWillEndRefresh:{
87 | [_indicator stopAnimating];
88 | break;
89 | }
90 | }
91 | }
92 |
93 | #pragma mark - getter
94 |
95 | - (UIImageView *)arrowImgV{
96 | if (!_arrowImgV) {
97 | NSBundle *b = [NSBundle bundleWithPath:[[NSBundle bundleForClass:[self class]] pathForResource:@"Image" ofType:@"bundle"]];
98 | NSString *path = [b pathForResource:@"arrow48.png" ofType:nil];
99 | UIImage *image = [UIImage imageWithContentsOfFile:path];
100 | _arrowImgV = [[UIImageView alloc] initWithImage:image];
101 | _arrowImgV.layer.anchorPoint = CGPointMake(0.5, 0);
102 | }
103 | return _arrowImgV;
104 | }
105 |
106 | - (UILabel *)promptlabel{
107 | if (!_promptlabel) {
108 | _promptlabel = [[UILabel alloc] init];
109 | _promptlabel.textAlignment = NSTextAlignmentCenter;
110 | _promptlabel.textColor = KafkaColorWithRGBA(100.,100.,100.,1.0);
111 | if (@available(iOS 8.2, *))
112 | _promptlabel.font = [UIFont systemFontOfSize:11. weight:UIFontWeightThin];
113 | else
114 | _promptlabel.font = [UIFont systemFontOfSize:11.];
115 | }
116 | return _promptlabel;
117 | }
118 |
119 | - (UIActivityIndicatorView *)indicator{
120 | if (!_indicator) {
121 | _indicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
122 | }
123 | return _indicator;
124 | }
125 |
126 | @end
127 |
--------------------------------------------------------------------------------
/KafkaRefresh/UIKit/HeadKit/KafkaNativeHeader.h:
--------------------------------------------------------------------------------
1 | /*************************************************************
2 | * Copyright (c) 2016-present, K. *
3 | * All rights reserved. *
4 | * *
5 | * e-mail: xorshine@icloud.com *
6 | * github:https://github.com/xorshine *
7 | * *
8 | * This source code is licensed under the MIT license. *
9 | *************************************************************/
10 |
11 |
12 | #import "KafkaHeadRefreshControl.h"
13 |
14 | @interface KafkaNativeHeader : KafkaHeadRefreshControl
15 | @property (strong, nonatomic) UIActivityIndicatorView * indicator;
16 | @end
17 |
--------------------------------------------------------------------------------
/KafkaRefresh/UIKit/HeadKit/KafkaNativeHeader.m:
--------------------------------------------------------------------------------
1 | /*************************************************************
2 | * Copyright (c) 2016-present, K. *
3 | * All rights reserved. *
4 | * *
5 | * e-mail: xorshine@icloud.com *
6 | * github:https://github.com/xorshine *
7 | * *
8 | * This source code is licensed under the MIT license. *
9 | *************************************************************/
10 |
11 | #import "KafkaNativeHeader.h"
12 | #import "KafkaCategories.h"
13 |
14 | @interface KafkaNativeHeader()
15 |
16 |
17 | @end
18 |
19 | @implementation KafkaNativeHeader
20 |
21 | - (void)setupProperties{
22 | [super setupProperties];
23 | [self addSubview:self.indicator];
24 | }
25 |
26 | - (void)layoutSubviews{
27 | [super layoutSubviews];
28 |
29 | self.indicator.center = CGPointMake(self.kr_width/2., self.kr_height/2.);
30 | }
31 |
32 | - (void)kafkaDidScrollWithProgress:(CGFloat)progress max:(const CGFloat)max{
33 |
34 | }
35 |
36 | - (void)kafkaRefreshStateDidChange:(KafkaRefreshState)state{
37 | [super kafkaRefreshStateDidChange:state];
38 | switch (state) {
39 | case KafkaRefreshStateNone:
40 | case KafkaRefreshStateScrolling:
41 | case KafkaRefreshStateReady:
42 | break;
43 | case KafkaRefreshStateRefreshing:{
44 | [self.indicator startAnimating];
45 | break;
46 | }
47 | case KafkaRefreshStateWillEndRefresh:{
48 | [self.indicator stopAnimating];
49 | break;
50 | }
51 | }
52 | }
53 |
54 | #pragma mark - getter
55 |
56 | - (UIActivityIndicatorView *)indicator{
57 | if (!_indicator) {
58 | if (@available(iOS 13.0, *)) {
59 | _indicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleMedium];
60 | } else {
61 | _indicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
62 | }
63 |
64 | _indicator.hidesWhenStopped = NO;
65 | }
66 | return _indicator;
67 | }
68 |
69 | @end
70 |
--------------------------------------------------------------------------------
/KafkaRefresh/UIKit/HeadKit/KafkaReplicatorHeader.h:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, K.
3 | * All rights reserved.
4 | *
5 | * e-mail:xorshine@icloud.com
6 | * github:https://github.com/xorshine
7 | *
8 | * This source code is licensed under the MIT license.
9 | */
10 |
11 | #import "KafkaHeadRefreshControl.h"
12 | #import "KafkaReplicatorLayer.h"
13 |
14 | @interface KafkaReplicatorHeader : KafkaHeadRefreshControl
15 |
16 | @property (strong, nonatomic) KafkaReplicatorLayer * replicatorLayer;
17 | @property (assign, nonatomic) KafkaReplicatorLayerAnimationStyle animationStyle;
18 |
19 | @end
20 |
--------------------------------------------------------------------------------
/KafkaRefresh/UIKit/HeadKit/KafkaReplicatorHeader.m:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, K.
3 | * All rights reserved.
4 | *
5 | * e-mail:xorshine@icloud.com
6 | * github:https://github.com/xorshine
7 | *
8 | * This source code is licensed under the MIT license.
9 | */
10 |
11 | #import "KafkaReplicatorHeader.h"
12 | #import "KafkaCategories.h"
13 |
14 | @implementation KafkaReplicatorHeader
15 |
16 | - (void)setupProperties{
17 | [super setupProperties];
18 | [self.layer addSublayer:self.replicatorLayer];
19 | }
20 |
21 | - (void)layoutSubviews{
22 | [super layoutSubviews];
23 | self.replicatorLayer.frame = CGRectMake(0, 0, self.kr_width, self.kr_height);
24 | }
25 |
26 | - (void)setThemeColor:(UIColor *)themeColor{
27 | [super setThemeColor:themeColor];
28 | self.replicatorLayer.themeColor = themeColor;
29 | }
30 |
31 | - (void)setAnimationStyle:(KafkaReplicatorLayerAnimationStyle)animationStyle{
32 | _animationStyle = animationStyle;
33 | self.replicatorLayer.animationStyle = animationStyle;
34 | }
35 |
36 | - (void)kafkaDidScrollWithProgress:(CGFloat)progress max:(const CGFloat)max{
37 | #define kOffset 0.7
38 | if (progress >= 0.8) {
39 | progress = (progress-kOffset)/(max - kOffset);
40 | }
41 | switch (self.animationStyle) {
42 | case KafkaReplicatorLayerAnimationStyleWoody:{
43 | break;
44 | }
45 | case KafkaReplicatorLayerAnimationStyleAllen:{
46 |
47 | break;
48 | }
49 | case KafkaReplicatorLayerAnimationStyleCircle:{
50 |
51 | break;
52 | }
53 | case KafkaReplicatorLayerAnimationStyleDot:{
54 |
55 | break;
56 | }
57 | case KafkaReplicatorLayerAnimationStyleArc:{
58 | self.replicatorLayer.indicatorShapeLayer.strokeEnd = progress;
59 | break;
60 | }
61 | case KafkaReplicatorLayerAnimationStyleTriangle:{
62 |
63 | break;
64 | }
65 | }
66 | }
67 |
68 | - (void)kafkaRefreshStateDidChange:(KafkaRefreshState)state{
69 | [super kafkaRefreshStateDidChange:state];
70 | switch (state) {
71 | case KafkaRefreshStateNone:
72 | case KafkaRefreshStateScrolling:break;
73 | case KafkaRefreshStateReady:{
74 | self.replicatorLayer.opacity = 1.;
75 | break;
76 | }
77 | case KafkaRefreshStateRefreshing:{
78 | [self.replicatorLayer startAnimating];
79 | break;
80 | }
81 | case KafkaRefreshStateWillEndRefresh:{
82 | [self.replicatorLayer stopAnimating];
83 | break;
84 | }
85 | }
86 | }
87 |
88 | - (KafkaReplicatorLayer *)replicatorLayer{
89 | if (!_replicatorLayer) {
90 | _replicatorLayer = [KafkaReplicatorLayer layer];
91 | }
92 | return _replicatorLayer;
93 | }
94 |
95 | @end
96 |
--------------------------------------------------------------------------------
/KafkaRefresh/UIKit/HeadKit/KafkaRingIndicatorHeader.h:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, K.
3 | * All rights reserved.
4 | *
5 | * e-mail:xorshine@icloud.com
6 | * github:https://github.com/xorshine
7 | *
8 | * This source code is licensed under the MIT license.
9 | */
10 |
11 | #import "KafkaHeadRefreshControl.h"
12 | #import "KafkaArcLayer.h"
13 |
14 | @interface KafkaRingIndicatorHeader : KafkaHeadRefreshControl
15 |
16 | @property (strong, nonatomic) KafkaArcLayer * arcLayer;
17 | @property (strong, nonatomic) UIActivityIndicatorView * indicator;
18 |
19 | @end
20 |
--------------------------------------------------------------------------------
/KafkaRefresh/UIKit/HeadKit/KafkaRingIndicatorHeader.m:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, K.
3 | * All rights reserved.
4 | *
5 | * e-mail:xorshine@icloud.com
6 | * github:https://github.com/xorshine
7 | *
8 | * This source code is licensed under the MIT license.
9 | */
10 |
11 | #import "KafkaRingIndicatorHeader.h"
12 | #import "KafkaCategories.h"
13 |
14 | @implementation KafkaRingIndicatorHeader
15 |
16 | - (void)setupProperties{
17 | [super setupProperties];
18 | [self.layer addSublayer:self.arcLayer];
19 | [self addSubview:self.indicator];
20 | }
21 |
22 | - (void)layoutSubviews{
23 | [super layoutSubviews];
24 | self.arcLayer.frame = CGRectMake(0, 0, self.kr_width, self.kr_height);
25 | self.indicator.center = CGPointMake(self.kr_width/2., self.kr_height/2.);
26 | }
27 |
28 | - (void)setThemeColor:(UIColor *)themeColor{
29 | if (super.themeColor == themeColor) {
30 | return;
31 | }
32 | [super setThemeColor:themeColor];
33 | self.arcLayer.ringFillColor = themeColor;
34 | }
35 |
36 | - (void)setAnimatedBackgroundColor:(UIColor *)animatedBackgroundColor{
37 | if (super.animatedBackgroundColor == animatedBackgroundColor) {
38 | return;
39 | }
40 | [super setAnimatedBackgroundColor:animatedBackgroundColor];
41 | self.arcLayer.ringBackgroundColor = animatedBackgroundColor;
42 | }
43 |
44 | - (void)kafkaDidScrollWithProgress:(CGFloat)progress max:(const CGFloat)max{
45 | #define kOffset 0.7
46 | if (progress >= kOffset) {
47 | progress = (progress-kOffset)/(max - kOffset);
48 | [self.arcLayer setProgress:progress];
49 | }
50 | }
51 |
52 | - (void)kafkaRefreshStateDidChange:(KafkaRefreshState)state{
53 | [super kafkaRefreshStateDidChange:state];
54 | switch (state) {
55 | case KafkaRefreshStateNone:{
56 | [self.arcLayer setProgress:0];
57 | break;
58 | }
59 | case KafkaRefreshStateScrolling:
60 | case KafkaRefreshStateReady:{
61 | break;
62 | }
63 | case KafkaRefreshStateRefreshing:{
64 | [self.arcLayer startAnimating];
65 | self.arcLayer.hidden = YES;
66 | [self.indicator startAnimating];
67 | break;
68 | }
69 | case KafkaRefreshStateWillEndRefresh:{
70 | [self.arcLayer stopAnimating];
71 | [self.indicator stopAnimating];
72 | self.arcLayer.hidden = NO;
73 | break;
74 | }
75 | }
76 | }
77 |
78 | - (KafkaArcLayer *)arcLayer{
79 | if (!_arcLayer) {
80 | _arcLayer = [KafkaArcLayer layer];
81 | }
82 | return _arcLayer;
83 | }
84 |
85 | - (UIActivityIndicatorView *)indicator{
86 | if (!_indicator) {
87 | _indicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
88 | _indicator.hidesWhenStopped = YES;
89 | }
90 | return _indicator;
91 | }
92 |
93 | @end
94 |
--------------------------------------------------------------------------------
/KafkaRefresh/UIKit/LayerKit/KafkaAnimatableProtocol.h:
--------------------------------------------------------------------------------
1 | //
2 | // KafkaAnimatableProtocol.h
3 | // KafkaExample
4 | //
5 | // Created by 向小辉 on 2018/1/3.
6 | // Copyright © 2018年 Kinx. All rights reserved.
7 | //
8 |
9 | #ifndef KafkaAnimatableProtocol_h
10 | #define KafkaAnimatableProtocol_h
11 |
12 | @protocol KafkaAnimatableProtocol
13 |
14 | - (void)startAnimating;
15 |
16 | - (void)stopAnimating;
17 |
18 | @end
19 |
20 | #endif /* KafkaAnimatableProtocol_h */
21 |
--------------------------------------------------------------------------------
/KafkaRefresh/UIKit/LayerKit/KafkaArcLayer.h:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, K.
3 | * All rights reserved.
4 | *
5 | * Email:xorshine@icloud.com
6 | *
7 | * This source code is licensed under the MIT license.
8 | */
9 |
10 | #import
11 | #import
12 | #import "KafkaAnimatableProtocol.h"
13 |
14 | @interface KafkaArcLayer : CALayer
15 |
16 | @property (strong, nonatomic) UIColor * ringBackgroundColor;
17 | @property (strong, nonatomic) UIColor * ringFillColor;
18 |
19 | - (void)setProgress:(CGFloat)progress;
20 |
21 | @end
22 |
--------------------------------------------------------------------------------
/KafkaRefresh/UIKit/LayerKit/KafkaArcLayer.m:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, K.
3 | * All rights reserved.
4 | *
5 | * Email:xorshine@icloud.com
6 | *
7 | * This source code is licensed under the MIT license.
8 | */
9 |
10 | #import "KafkaArcLayer.h"
11 | #import "KafkaCategories.h"
12 |
13 | #define KafkaColorWithRGBA(r,g,b,a) \
14 | [UIColor colorWithRed:(r)/255. green:(g)/255. blue:(b)/255. alpha:(a)]
15 |
16 | @interface KafkaArcLayer()
17 |
18 | @property (strong, nonatomic) CAShapeLayer * ringBackgroundLayer;
19 | @property (strong, nonatomic) CAShapeLayer * ringShapeLayer;
20 | @property (strong, nonatomic) UIBezierPath * bezierPath;
21 |
22 | @end
23 |
24 | @implementation KafkaArcLayer
25 |
26 | - (instancetype)init{
27 | self = [super init];
28 | if (self) {
29 | _ringFillColor = KafkaColorWithRGBA(180., 180., 180., 1.0);
30 | _ringBackgroundColor = KafkaColorWithRGBA(230., 230., 230., 1.0);
31 |
32 | [self addSublayer:self.ringBackgroundLayer];
33 | [self.ringBackgroundLayer addSublayer:self.ringShapeLayer];
34 | }
35 | return self;
36 | }
37 |
38 | - (void)startAnimating{
39 |
40 | }
41 |
42 | - (void)stopAnimating{
43 | self.ringShapeLayer.strokeEnd = 0.0;
44 | [self.ringShapeLayer removeAllAnimations];
45 | }
46 |
47 | - (void)setProgress:(CGFloat)progress{
48 | self.ringShapeLayer.strokeEnd = progress;
49 | }
50 |
51 | - (void)setRingFillColor:(UIColor *)ringFillColor{
52 | _ringFillColor = ringFillColor;
53 | self.ringShapeLayer.strokeColor = ringFillColor.CGColor;
54 | }
55 |
56 | - (void)setRingBackgroundColor:(UIColor *)ringBackgroundColor{
57 | _ringBackgroundColor = ringBackgroundColor;
58 | self.ringBackgroundLayer.strokeColor = ringBackgroundColor.CGColor;
59 | }
60 |
61 | - (void)layoutSublayers{
62 | [super layoutSublayers];
63 |
64 | self.ringBackgroundLayer.frame = CGRectMake(0, 0, 30, 30);
65 | self.ringBackgroundLayer.position = CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds));
66 |
67 | self.ringShapeLayer.frame = self.ringBackgroundLayer.bounds;
68 | self.ringShapeLayer.position = CGPointMake(CGRectGetMidX(self.ringBackgroundLayer.bounds), CGRectGetMidY(self.ringBackgroundLayer.bounds));
69 |
70 | self.bezierPath = [UIBezierPath bezierPathWithRoundedRect:self.ringShapeLayer.bounds cornerRadius:self.ringShapeLayer.kr_width/2.];
71 |
72 | self.ringBackgroundLayer.path = self.bezierPath.CGPath;
73 | self.ringShapeLayer.path = self.bezierPath.CGPath;
74 | }
75 |
76 | - (CAShapeLayer *)ringBackgroundLayer{
77 | if (!_ringBackgroundLayer) {
78 | _ringBackgroundLayer = [CAShapeLayer layer];
79 | _ringBackgroundLayer.lineWidth = 3.;
80 | _ringBackgroundLayer.lineCap = kCALineCapRound;
81 | _ringBackgroundLayer.backgroundColor = [UIColor clearColor].CGColor;
82 | _ringBackgroundLayer.fillColor = [UIColor clearColor].CGColor;
83 | _ringBackgroundLayer.strokeColor = self.ringBackgroundColor.CGColor;
84 | }
85 | return _ringBackgroundLayer;
86 | }
87 |
88 | - (CAShapeLayer *)ringShapeLayer{
89 | if (!_ringShapeLayer) {
90 | _ringShapeLayer = [CAShapeLayer layer];
91 | _ringShapeLayer.lineWidth = 3.;
92 | _ringShapeLayer.lineCap = kCALineCapRound;
93 | _ringShapeLayer.backgroundColor = [UIColor clearColor].CGColor;
94 | _ringShapeLayer.fillColor = [UIColor clearColor].CGColor;
95 | _ringShapeLayer.strokeColor = self.ringFillColor.CGColor;
96 | _ringShapeLayer.strokeEnd = 0;
97 | }
98 | return _ringShapeLayer;
99 | }
100 |
101 | @end
102 |
--------------------------------------------------------------------------------
/KafkaRefresh/UIKit/LayerKit/KafkaReplicatorLayer.h:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, K.
3 | * All rights reserved.
4 | *
5 | * Email:xorshine@icloud.com
6 | *
7 | * This source code is licensed under the MIT license.
8 | */
9 |
10 | #import
11 | #import
12 | #import "KafkaAnimatableProtocol.h"
13 |
14 | typedef NS_ENUM(NSInteger,KafkaReplicatorLayerAnimationStyle) {
15 | KafkaReplicatorLayerAnimationStyleWoody,
16 | KafkaReplicatorLayerAnimationStyleAllen,
17 | KafkaReplicatorLayerAnimationStyleCircle,
18 | KafkaReplicatorLayerAnimationStyleDot,
19 | KafkaReplicatorLayerAnimationStyleArc,
20 | KafkaReplicatorLayerAnimationStyleTriangle
21 | };
22 |
23 | @interface KafkaReplicatorLayer : CALayer
24 |
25 | @property (strong, nonatomic) CAReplicatorLayer *replicatorLayer;
26 | @property (strong, nonatomic) CAShapeLayer *indicatorShapeLayer;
27 |
28 | @property (assign, nonatomic) KafkaReplicatorLayerAnimationStyle animationStyle;
29 | @property (nonatomic, strong) UIColor *themeColor;
30 |
31 | @end
32 |
--------------------------------------------------------------------------------
/KafkaRefresh/UIKit/LayerKit/KafkaReplicatorLayer.m:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, K.
3 | * All rights reserved.
4 | *
5 | * Email:xorshine@icloud.com
6 | *
7 | * This source code is licensed under the MIT license.
8 | */
9 |
10 | #define KafkaColorWithRGBA(r,g,b,a) \
11 | [UIColor colorWithRed:(r)/255. green:(g)/255. blue:(b)/255. alpha:(a)]
12 |
13 | #import "KafkaReplicatorLayer.h"
14 | #import "KafkaCategories.h"
15 | #import "KafkaRefreshDefaults.h"
16 | #import
17 |
18 | static void * KafkaLeftDot = &KafkaLeftDot;
19 | static void * KafkaRightDot = &KafkaRightDot;
20 |
21 | @implementation KafkaReplicatorLayer
22 |
23 | - (instancetype)init{
24 | self = [super init];
25 | if (self) {
26 | [self setupProperties];
27 | }
28 | return self;
29 | }
30 |
31 | - (void)setupProperties{
32 | [self addSublayer:self.replicatorLayer];
33 | [self.replicatorLayer addSublayer:self.indicatorShapeLayer];
34 | self.indicatorShapeLayer.strokeColor = [KafkaRefreshDefaults standardRefreshDefaults].themeColor.CGColor;
35 | self.indicatorShapeLayer.backgroundColor = [KafkaRefreshDefaults standardRefreshDefaults].backgroundColor.CGColor;
36 | }
37 |
38 | - (void)setAnimationStyle:(KafkaReplicatorLayerAnimationStyle)animationStyle{
39 | if (_animationStyle != animationStyle) {
40 | _animationStyle = animationStyle;
41 | [self setNeedsLayout];
42 | }
43 | }
44 |
45 | - (void)setThemeColor:(UIColor *)themeColor{
46 | _themeColor = themeColor;
47 | self.indicatorShapeLayer.strokeColor = themeColor.CGColor;
48 | self.indicatorShapeLayer.backgroundColor = themeColor.CGColor;
49 | }
50 |
51 | - (void)layoutSublayers {
52 | [super layoutSublayers];
53 |
54 | self.replicatorLayer.frame = self.bounds;
55 |
56 | CGFloat padding = 10.;
57 | switch (self.animationStyle) {
58 | case KafkaReplicatorLayerAnimationStyleWoody:
59 | case KafkaReplicatorLayerAnimationStyleAllen:{
60 | CGFloat h = self.kr_height / 3.0;
61 | CGFloat w = 3.0;
62 | CGFloat x = self.kr_width / 2. - (2.5 * w + padding * 2);
63 | CGFloat y = self.kr_height / 2. - h / 2.0;
64 | self.indicatorShapeLayer.frame = CGRectMake(x, y, w, h);
65 | self.indicatorShapeLayer.cornerRadius = 1.;
66 |
67 | self.replicatorLayer.instanceCount = 5;
68 | self.replicatorLayer.instanceDelay = 0.3/5;
69 | self.replicatorLayer.instanceTransform = CATransform3DMakeTranslation(padding+w, 0.0, 0.0);
70 | self.replicatorLayer.instanceBlueOffset = -0.01;
71 | self.replicatorLayer.instanceGreenOffset = -0.01;
72 | break;
73 | }
74 | case KafkaReplicatorLayerAnimationStyleCircle:{
75 | self.indicatorShapeLayer.frame = CGRectMake(self.kr_width/2. - 2., 10, 4., 4.);
76 | self.indicatorShapeLayer.cornerRadius = 2.;
77 | self.indicatorShapeLayer.transform = CATransform3DMakeScale(0.2, 0.2, 0.2);
78 |
79 | self.replicatorLayer.instanceCount = 12;
80 | self.replicatorLayer.instanceDelay = 0.8/12;
81 | CGFloat angle = (2 * M_PI)/self.replicatorLayer.instanceCount;
82 | self.replicatorLayer.instanceTransform = CATransform3DMakeRotation(angle, 0, 0, 0.1);
83 | self.replicatorLayer.instanceBlueOffset = -0.01;
84 | self.replicatorLayer.instanceGreenOffset = -0.01;
85 | break;
86 | }
87 | case KafkaReplicatorLayerAnimationStyleDot:{
88 | CGFloat innerPadding = 30.;
89 | CGFloat h = 8.0;;
90 | CGFloat w = 8.0;
91 | CGFloat x = self.kr_width / 2. - (.5 * w + innerPadding);
92 | CGFloat y = self.kr_height/2. - h/2.0;
93 | self.indicatorShapeLayer.frame = CGRectMake(x, y, w, h);
94 | self.indicatorShapeLayer.cornerRadius = 4.;
95 |
96 | self.replicatorLayer.instanceCount = 3;
97 | self.replicatorLayer.instanceDelay = 0.5/3;
98 | self.replicatorLayer.instanceTransform = CATransform3DMakeTranslation(innerPadding, 0.0, 0.0);
99 | break;
100 | }
101 | case KafkaReplicatorLayerAnimationStyleArc:{
102 | CGFloat h = self.kr_height - 10.;
103 | CGFloat w = h;
104 | CGFloat x = self.kr_width/2. - 0.5 * w;
105 | CGFloat y = self.kr_height/2.- h/2.0;
106 | self.indicatorShapeLayer.frame = CGRectMake(x, y, w, h);
107 | self.indicatorShapeLayer.fillColor = [UIColor clearColor].CGColor;
108 | self.indicatorShapeLayer.lineWidth = 3.;
109 | self.indicatorShapeLayer.backgroundColor = [UIColor clearColor].CGColor;
110 |
111 | UIBezierPath *arcPath = [UIBezierPath bezierPath];
112 | [arcPath addArcWithCenter:CGPointMake(w/2.0, h/2.)
113 | radius:h/2.
114 | startAngle:M_PI/2.3
115 | endAngle:-M_PI/2.3
116 | clockwise:NO];
117 | self.indicatorShapeLayer.path = arcPath.CGPath;
118 | self.indicatorShapeLayer.strokeEnd = 0.1;
119 | self.replicatorLayer.instanceCount = 2;
120 | self.replicatorLayer.instanceTransform = CATransform3DMakeRotation(M_PI, 0, 0, 0.1);
121 | break;
122 | }
123 | case KafkaReplicatorLayerAnimationStyleTriangle:{
124 | CGFloat h = 8.0;
125 | CGFloat w = h;
126 | self.indicatorShapeLayer.frame = CGRectMake(self.replicatorLayer.kr_width/2.-w/2, 5., w, h);
127 | self.indicatorShapeLayer.cornerRadius = self.indicatorShapeLayer.kr_width/2.;
128 | CGPoint topPoint = self.indicatorShapeLayer.position;
129 | CGPoint leftPoint = CGPointMake(topPoint.x-15, topPoint.y+23);
130 | CGPoint rightPoint = CGPointMake(topPoint.x+15, topPoint.y+23);
131 |
132 | CAShapeLayer *leftCircle = [self leftCircle];
133 | CAShapeLayer *rightCircle = [self rightCircle];
134 | if (leftCircle)
135 | [leftCircle removeFromSuperlayer];
136 | if (rightCircle)
137 | [rightCircle removeFromSuperlayer];
138 |
139 | leftCircle.kr_size = self.indicatorShapeLayer.kr_size;
140 | leftCircle.position = leftPoint;
141 | leftCircle.cornerRadius = self.indicatorShapeLayer.cornerRadius;
142 | [self.replicatorLayer addSublayer:leftCircle];
143 |
144 | rightCircle.kr_size = self.indicatorShapeLayer.kr_size;
145 | rightCircle.position = rightPoint;
146 | rightCircle.cornerRadius = self.indicatorShapeLayer.cornerRadius;
147 | [self.replicatorLayer addSublayer:rightCircle];
148 | break;
149 | }
150 | }
151 | }
152 |
153 | - (void)startAnimating{
154 | [self.indicatorShapeLayer removeAllAnimations];
155 | switch (self.animationStyle) {
156 | case KafkaReplicatorLayerAnimationStyleWoody:{
157 | CABasicAnimation *basicAnimation = [self animationKeyPath:@"transform.scale.y"
158 | from:@(1.5)
159 | to:@(0.0)
160 | duration:0.3
161 | repeatTime:INFINITY];
162 | basicAnimation.autoreverses = YES;
163 | [self.indicatorShapeLayer addAnimation:basicAnimation forKey:basicAnimation.keyPath];
164 | break;
165 | }
166 | case KafkaReplicatorLayerAnimationStyleAllen:{
167 | CABasicAnimation *basicAnimation = [self animationKeyPath:@"position.y"
168 | from:@(self.indicatorShapeLayer.position.y+10)
169 | to:@(self.indicatorShapeLayer.position.y-10)
170 | duration:0.3
171 | repeatTime:INFINITY];
172 | basicAnimation.autoreverses = YES;
173 | [self.indicatorShapeLayer addAnimation:basicAnimation forKey:basicAnimation.keyPath];
174 | break;
175 | }
176 | case KafkaReplicatorLayerAnimationStyleCircle:{
177 | CABasicAnimation *basicAnimation = [self animationKeyPath:@"transform.scale"
178 | from:@(1.0)
179 | to:@(0.2)
180 | duration:0.8
181 | repeatTime:INFINITY];
182 | [self.indicatorShapeLayer addAnimation:basicAnimation forKey:basicAnimation.keyPath];
183 | break;
184 | }
185 | case KafkaReplicatorLayerAnimationStyleDot:{
186 | CABasicAnimation *basicAnimation = [self animationKeyPath:@"transform.scale"
187 | from:@(0.3)
188 | to:@(2.5)
189 | duration:0.5
190 | repeatTime:INFINITY];
191 | basicAnimation.autoreverses = YES;
192 | CABasicAnimation * opc = [self animationKeyPath:@"opacity"
193 | from:@(0.1)
194 | to:@(1.0)
195 | duration:0.5
196 | repeatTime:INFINITY];
197 |
198 | opc.autoreverses = YES;
199 | CAAnimationGroup * group = [CAAnimationGroup animation];
200 | group.animations = @[basicAnimation,opc];
201 | group.autoreverses = YES;
202 | group.repeatCount = INFINITY;
203 | group.duration = 0.5;
204 | [self.indicatorShapeLayer addAnimation:group forKey:basicAnimation.keyPath];
205 | break;
206 | }
207 | case KafkaReplicatorLayerAnimationStyleArc:{
208 | CABasicAnimation *basicAnimation = [self animationKeyPath:@"transform.rotation.z"
209 | from:@(0.0)
210 | to:@(2*M_PI)
211 | duration:0.8
212 | repeatTime:INFINITY];
213 | [self.indicatorShapeLayer addAnimation:basicAnimation forKey:basicAnimation.keyPath];
214 | break;
215 | }
216 | case KafkaReplicatorLayerAnimationStyleTriangle:{
217 | CAShapeLayer *leftCircle = objc_getAssociatedObject(self, KafkaLeftDot);
218 | CAShapeLayer *rightCircle = objc_getAssociatedObject(self, KafkaRightDot);
219 |
220 | CGPoint topPoint = self.indicatorShapeLayer.position;
221 | CGPoint leftPoint = leftCircle.position;
222 | CGPoint rightPoint = rightCircle.position;
223 |
224 | NSArray *vertexs = @[[NSValue valueWithCGPoint:topPoint],
225 | [NSValue valueWithCGPoint:leftPoint],
226 | [NSValue valueWithCGPoint:rightPoint]];
227 |
228 | CAKeyframeAnimation *key0 = [self keyFrameAnimationWithPath:[self trianglePathWithStartPoint:topPoint vertexs:vertexs] duration:1.5];
229 | [self.indicatorShapeLayer addAnimation:key0 forKey:key0.keyPath];
230 |
231 | CAKeyframeAnimation *key1 = [self keyFrameAnimationWithPath:[self trianglePathWithStartPoint:leftPoint vertexs:vertexs] duration:1.5];
232 | [rightCircle addAnimation:key1 forKey:key1.keyPath];
233 |
234 | CAKeyframeAnimation *key2 = [self keyFrameAnimationWithPath:[self trianglePathWithStartPoint:rightPoint vertexs:vertexs] duration:1.5];
235 | [leftCircle addAnimation:key2 forKey:key2.keyPath];
236 | break;
237 | }
238 | }
239 | }
240 |
241 | - (void)stopAnimating{
242 | [self.indicatorShapeLayer removeAllAnimations];
243 |
244 | switch (self.animationStyle) {
245 | case KafkaReplicatorLayerAnimationStyleWoody:
246 | case KafkaReplicatorLayerAnimationStyleAllen:
247 | case KafkaReplicatorLayerAnimationStyleCircle:
248 | case KafkaReplicatorLayerAnimationStyleDot:{
249 | break;
250 | }
251 | case KafkaReplicatorLayerAnimationStyleArc:{
252 | self.indicatorShapeLayer.strokeEnd = 0.1;
253 | break;
254 | }
255 | case KafkaReplicatorLayerAnimationStyleTriangle:{
256 | CAShapeLayer *leftCircle = objc_getAssociatedObject(self, KafkaLeftDot);
257 | [leftCircle removeAllAnimations];
258 | CAShapeLayer *rightCircle = objc_getAssociatedObject(self, KafkaRightDot);
259 | [rightCircle removeAllAnimations];
260 | break;
261 | }
262 | }
263 |
264 | }
265 |
266 | - (CABasicAnimation *)animationKeyPath:(NSString *)keyPath
267 | from:(NSNumber *)fromValue
268 | to:(NSNumber *)toValue
269 | duration:(CFTimeInterval)duration
270 | repeatTime:(CGFloat)repeat {
271 | CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:keyPath];
272 | animation.fromValue = fromValue;
273 | animation.toValue = toValue;
274 | animation.duration = duration;
275 | animation.repeatCount = repeat;
276 | animation.removedOnCompletion = NO;
277 | return animation;
278 | }
279 |
280 | - (CAKeyframeAnimation *)keyFrameAnimationWithPath:(UIBezierPath *)path
281 | duration:(NSTimeInterval)duration {
282 | CAKeyframeAnimation *animation = [[CAKeyframeAnimation alloc] init];
283 | animation.keyPath = @"position";
284 | animation.path = path.CGPath;
285 | animation.duration = duration;
286 | animation.repeatCount = INFINITY;
287 | animation.removedOnCompletion = NO;
288 | return animation;
289 | }
290 |
291 | - (UIBezierPath *)trianglePathWithStartPoint:(CGPoint)startPoint vertexs:(NSArray *)vertexs {
292 | CGPoint topPoint = [[vertexs objectAtIndex:0] CGPointValue];
293 | CGPoint leftPoint = [[vertexs objectAtIndex:1] CGPointValue];
294 | CGPoint rightPoint = [[vertexs objectAtIndex:2] CGPointValue];
295 |
296 | UIBezierPath *path = [UIBezierPath bezierPath];
297 |
298 | if (CGPointEqualToPoint(startPoint, topPoint) ) {
299 | [path moveToPoint:startPoint];
300 | [path addLineToPoint:rightPoint];
301 | [path addLineToPoint:leftPoint];
302 | } else if (CGPointEqualToPoint(startPoint, leftPoint)) {
303 | [path moveToPoint:startPoint];
304 | [path addLineToPoint:topPoint];
305 | [path addLineToPoint:rightPoint];
306 | } else {
307 | [path moveToPoint:startPoint];
308 | [path addLineToPoint:leftPoint];
309 | [path addLineToPoint:topPoint];
310 | }
311 |
312 | [path closePath];
313 |
314 | return path;
315 | }
316 |
317 | - (CAShapeLayer *)leftCircle{
318 | CAShapeLayer *leftCircle = objc_getAssociatedObject(self, KafkaLeftDot);
319 | if (!leftCircle) {
320 | leftCircle = [CAShapeLayer layer];
321 | leftCircle.backgroundColor = self.indicatorShapeLayer.backgroundColor;
322 | objc_setAssociatedObject(self, KafkaLeftDot, leftCircle, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
323 | }
324 | return leftCircle;
325 | }
326 |
327 | - (CAShapeLayer *)rightCircle{
328 | CAShapeLayer *rightCircle = objc_getAssociatedObject(self, KafkaRightDot);
329 | if (!rightCircle) {
330 | rightCircle = [CAShapeLayer layer];
331 | rightCircle.backgroundColor = self.indicatorShapeLayer.backgroundColor;
332 | objc_setAssociatedObject(self, KafkaRightDot, rightCircle, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
333 | }
334 | return rightCircle;
335 | }
336 |
337 | - (CAShapeLayer *)indicatorShapeLayer{
338 | if (!_indicatorShapeLayer) {
339 | _indicatorShapeLayer = [CAShapeLayer layer];
340 | _indicatorShapeLayer.contentsScale = [[UIScreen mainScreen] scale];
341 | _indicatorShapeLayer.lineCap = kCALineCapRound;
342 | }
343 | return _indicatorShapeLayer;
344 | }
345 |
346 | - (CAReplicatorLayer *)replicatorLayer{
347 | if (!_replicatorLayer) {
348 | _replicatorLayer = [CAReplicatorLayer layer];
349 | _replicatorLayer.backgroundColor = [UIColor clearColor].CGColor;
350 | _replicatorLayer.shouldRasterize = YES;
351 | _replicatorLayer.rasterizationScale = [[UIScreen mainScreen] scale];
352 | }
353 | return _replicatorLayer;
354 | }
355 |
356 | @end
357 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 H. H. Hsiang
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 H. H. Hsiang
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
KafkaRefresh
7 |
8 | Animated, customizable, and flexible pull-to-refresh framework for faster and easier iOS development.
9 |
10 |
11 | Report bug
12 | ·
13 | Request feature
14 | ·
15 | 中文文档
16 |
17 |
18 |
19 |
20 | ### Status
21 |
23 | [](https://github.com/xorshine/KafkaRefresh/blob/master/LICENSE)
24 | [](https://img.shields.io/cocoapods/v/KafkaRefresh.svg)
25 | 
26 | 
27 | [](mailto:xorshine@icloud.com)
28 |
29 | ****
30 |
31 | ### Screenshots
32 |
33 |
34 | KafkaRefreshStyle |
35 | Top Screenshots |
36 | Bottom Screenshots |
37 |
38 |
39 | Native |
40 |  |
41 |  |
42 |
43 |
44 | ReplicatorWoody |
45 |  |
46 |  |
47 |
48 |
49 | ReplicatorAllen |
50 |  |
51 |  |
52 |
53 |
54 | ReplicatorCircle |
55 |  |
56 |  |
57 |
58 |
59 | ReplicatorDot |
60 |  |
61 |  |
62 |
63 |
64 | ReplicatorArc |
65 |  |
66 |  |
67 |
68 |
69 | ReplicatorTriangle |
70 |  |
71 |  |
72 |
73 |
74 | AnimatableRing |
75 |  |
76 |  |
77 |
78 |
79 | AnimatableArrow |
80 |  |
81 |  |
82 |
83 |
84 |
85 | ### Features
86 |
87 | * **Built-in rich animation style, support self-customization**
88 |
89 |
90 | * **Non-refresh state hidden automatically**
91 |
92 | > To avoid developers manually adjust contentInset refresh the appearance of the control after the impact of the visual experience;
93 | the most common situation, the absence of data, the bottom of the refresh control is not hidden, the use of KafkaRefresh to avoid the problem.
94 |
95 | * **Anti-dithering at the end of the refresh**
96 |
97 | >When the refresh control finishes refreshing, if UIScrollView is in a scrolling state, KafkaRefresh will adjust the contntOffset that controls the UIScrollView at this time according to the refresh control.
98 |
99 | * **Support setting the offset threshold to trigger refresh**
100 |
101 | >Setting the value of `stretchOffsetYAxisThreshold` can control the refresh pull distance.This property is a ratio relative to the height of the control and must be set greater than 1.0.
102 |
103 | * **Support global setting**
104 |
105 | >KafkaRefreshDefaults is a singleton for global settings
106 |
107 | * **Support progress callback**
108 |
109 | >Real-time callback Drag the offset ratio, for the expansion of the interface, according to the progress of adjustment animation.
110 |
111 | * **Adaptive contentInset system adjustment and manual adjustment**
112 |
113 | >Adaptive UINavigationController for UIScrollView's contentInset property adjustment, even if the contentInset automatically set value, then KafkaRefresh can still adapt this adjustment.
114 |
115 | * **Solve the section view floating problem when refreshing**
116 |
117 | * **Support horizontal and vertical screen switching adaptive**
118 |
119 | >No need to consider in the horizontal and vertical screen refresh refresh problem.
120 |
121 | * **iOS 7+**
122 |
123 | Support iOS 7 above system. Including iPhone X.
124 |
125 | * Support auto refresh
126 |
127 | When the user slides the scrollview to the bottom, the refresh will be triggered automatically, without the user having to slide to the bottom and pull up the scrollview. This feature is not enabled by default, because most people will use the function without viewing the document. If it is not used correctly, it will be easy to cause the refresh to stop.
128 |
129 | Use the preload feature, please strictly follow the requirements below:
130 |
131 | * ```self.tableView.footRefreshControl.autoRefreshOnFoot = YES;``` please set autoRefreshOnFoot TRUE;
132 |
133 | * in refreshHandler,Strictly follow the logic below!
134 |
135 | ```objective-c
136 | if ({No data needs to be stitched}) {
137 | [weakSelf.tableView.footRefreshControl endRefreshingAndNoLongerRefreshingWithAlertText:@"no more"];
138 | } else {
139 | [weakSelf.tableView.footRefreshControl endRefreshingWithAlertText:@"did load successfully" completion:nil];
140 | }
141 | ```
142 |
143 | * **Document coverage 100%**
144 |
145 | > You can see the use of all methods and classes in the header file.
146 |
147 |
148 | ### Installation
149 | * CocoaPods
150 | ```ruby
151 | pod 'KafkaRefresh'
152 | ```
153 |
154 | * Carthage
155 |
156 | > If anyone wants to install by *carthage* , please supply a pull request. I'm not using this package manager myself.
157 |
158 | ### Usage
159 |
160 | ```objective-c
161 | #import "KafkaRefresh.h"
162 | ```
163 |
164 | ##### Initialization
165 | * The first way
166 | ```objective-c
167 | #pragma mark - head
168 |
169 | [self.tableView bindHeadRefreshHandler:^{
170 |
171 | } themeColor:MainColor refreshStyle:KafkaRefreshStyleAnimatableArrow];
172 |
173 | #pragma mark - foot
174 |
175 | [self.tableView bindFootRefreshHandler:^{
176 |
177 | } themeColor:MainColor refreshStyle:KafkaRefreshStyleAnimatableArrow];
178 |
179 | #pragma mark - auto refresh
180 |
181 | self.tableView.footRefreshControl.autoRefreshOnFoot = YES;
182 | ```
183 | * The second way
184 | ```objective-c
185 | KafkaArrowHeader * arrow = [[KafkaArrowHeader alloc] init];
186 | arrow.refreshHandler = ^{
187 | //to do something...
188 | };
189 | self.tableView.headRefreshControl = arrow;
190 | ```
191 | * The third way(global configuration)
192 | ```objective-c
193 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
194 | [[KafkaRefreshDefaults standardRefreshDefaults] setHeaderDefaultStyle:KafkaRefreshStyleAnimatableRing];
195 | return YES;
196 | }
197 |
198 | #pragma mark - global
199 |
200 | [self.tableView bindGlobalStyleForFootRefreshHandler:^{
201 |
202 | }];
203 | ```
204 | ##### Trigger Refresh Manually
205 |
206 | ```objective-c
207 | [self.tableView.headRefreshControl beginRefreshing];
208 | [self.tableView.footRefreshControl beginRefreshing];
209 | ```
210 |
211 | ##### End Refresh
212 | > When you finish refreshing and don't need to show any hints, or any animation, call the following method.
213 |
214 | ```objective-c
215 | - (void)endRefreshing;
216 | ```
217 | > When you finish the refresh and need to display the prompt message, call the following method.
218 |
219 | ```objective-c
220 | - (void)endRefreshingWithAlertText:(NSString *)text completion:(dispatch_block_t)completion;
221 | ```
222 |
223 | >When you end the refresh and no longer need to refresh, call the following method.
224 |
225 | ```objective-c
226 | - (void)endRefreshingAndNoLongerRefreshingWithAlertText:(NSString *)text;
227 | ```
228 | ##### Resume Refresh Available
229 | ```objective-c
230 | /**
231 | After you call ‘endRefreshingAndNoLongerRefreshingWithAlertText’,
232 | you need to resume refresh available
233 | */
234 | - (void)resumeRefreshAvailable;
235 | ```
236 |
237 | ### Customize
238 |
239 | Take KafkaheadRefreshControl as an example
240 | ```objective-c
241 | #import "KafkaheadRefreshControl.h"
242 | @interface CustomHeader : KafkafootRefreshControl
243 | @end
244 | ```
245 |
246 | ```objective-c
247 | @implementation CustomHeader
248 |
249 | - (void)kafkaDidScrollWithProgress:(CGFloat)progress max:(const CGFloat)max{
250 | //progress callback
251 | }
252 |
253 | - (void)kafkaRefreshStateDidChange:(KafkaRefreshState)state{
254 | [super kafkaRefreshStateDidChange:state];
255 | }
256 | @end
257 | ```
258 | ### Warnings And Precautions
259 |
260 | * Many people asked me a question that tableView jump after called `insertRowAtIndexPath: withRowAnimation:`,That Is Not Bug Of KafkaRefresh. Setting `tableView.estimatedRowHeight = UITableViewAutomaticDimension;` can solve this question when you initialize tableView;
261 |
262 |
263 | * Please update the latest version!
264 |
265 |
266 | ### Communication
267 | > 1. If you need help,please email .
268 | > 2. If you found a bug,and can provide steps to reliably reproduce it, open an issue.
269 | > 3. Personal energy is limited, Kafka provides callback interface enough to increase the richer UI effect, we welcome you to join together and submit the pull request.
270 |
271 |
272 | ### License
273 | > KafkaRefresh is released under the MIT license. See [LICENSE](https://github.com/xorshine/KafkaRefresh/blob/master/LICENSE) for details.
274 |
275 |
276 | [](https://app.fossa.com/projects/git%2Bgithub.com%2FHHHsiang%2FKafkaRefresh?ref=badge_large)
--------------------------------------------------------------------------------
/_config.yml:
--------------------------------------------------------------------------------
1 | theme: jekyll-theme-time-machine
--------------------------------------------------------------------------------