├── .gitattributes
├── .gitignore
├── .npmignore
├── LICENSE
├── README.md
├── android
├── README.md
├── build.gradle
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── com
│ │ └── lmy
│ │ ├── header
│ │ ├── AnyHeader.java
│ │ ├── AnyHeaderManager.java
│ │ ├── ClassicsHeaderManager.java
│ │ ├── DefaultHeader.java
│ │ ├── DefaultHeaderMananger.java
│ │ ├── MaterialHeaderManager.java
│ │ └── StoreHouseHeaderManager.java
│ │ └── smartrefreshlayout
│ │ ├── Events.java
│ │ ├── HeaderType.java
│ │ ├── RCTSpinnerStyleModule.java
│ │ ├── ReactSmartRefreshLayout.java
│ │ ├── SmartRefreshLayoutManager.java
│ │ ├── SmartRefreshLayoutPackage.java
│ │ └── SpinnerStyleConstants.java
│ └── res
│ └── values
│ ├── ids.xml
│ └── strings.xml
├── index.js
├── ios
├── MJRefresh
│ ├── Base
│ │ ├── MJRefreshAutoFooter.h
│ │ ├── MJRefreshAutoFooter.m
│ │ ├── MJRefreshBackFooter.h
│ │ ├── MJRefreshBackFooter.m
│ │ ├── MJRefreshComponent.h
│ │ ├── MJRefreshComponent.m
│ │ ├── MJRefreshFooter.h
│ │ ├── MJRefreshFooter.m
│ │ ├── MJRefreshHeader.h
│ │ └── MJRefreshHeader.m
│ ├── Custom
│ │ ├── Footer
│ │ │ ├── Auto
│ │ │ │ ├── MJRefreshAutoGifFooter.h
│ │ │ │ ├── MJRefreshAutoGifFooter.m
│ │ │ │ ├── MJRefreshAutoNormalFooter.h
│ │ │ │ ├── MJRefreshAutoNormalFooter.m
│ │ │ │ ├── MJRefreshAutoStateFooter.h
│ │ │ │ └── MJRefreshAutoStateFooter.m
│ │ │ └── Back
│ │ │ │ ├── MJRefreshBackGifFooter.h
│ │ │ │ ├── MJRefreshBackGifFooter.m
│ │ │ │ ├── MJRefreshBackNormalFooter.h
│ │ │ │ ├── MJRefreshBackNormalFooter.m
│ │ │ │ ├── MJRefreshBackStateFooter.h
│ │ │ │ └── MJRefreshBackStateFooter.m
│ │ └── Header
│ │ │ ├── MJRefreshGifHeader.h
│ │ │ ├── MJRefreshGifHeader.m
│ │ │ ├── MJRefreshNormalHeader.h
│ │ │ ├── MJRefreshNormalHeader.m
│ │ │ ├── MJRefreshStateHeader.h
│ │ │ └── MJRefreshStateHeader.m
│ ├── MJRefresh.bundle
│ │ ├── arrow@2x.png
│ │ ├── en.lproj
│ │ │ └── Localizable.strings
│ │ ├── zh-Hans.lproj
│ │ │ └── Localizable.strings
│ │ └── zh-Hant.lproj
│ │ │ └── Localizable.strings
│ ├── MJRefresh.h
│ ├── MJRefreshConst.h
│ ├── MJRefreshConst.m
│ ├── NSBundle+MJRefresh.h
│ ├── NSBundle+MJRefresh.m
│ ├── UIScrollView+MJExtension.h
│ ├── UIScrollView+MJExtension.m
│ ├── UIScrollView+MJRefresh.h
│ ├── UIScrollView+MJRefresh.m
│ ├── UIView+MJExtension.h
│ └── UIView+MJExtension.m
├── RCTMJRefreshHeader
│ ├── RCTMJRefreshHeader.h
│ ├── RCTMJRefreshHeader.m
│ ├── RCTMJRefreshViewManager.m
│ ├── RCTMJScrollContentShadowView.h
│ ├── RCTMJScrollContentShadowView.m
│ ├── RCTMJScrollContentView.h
│ ├── RCTMJScrollContentView.m
│ ├── RCTMJScrollContentViewMananger.m
│ ├── RCTMJScrollView.h
│ ├── RCTMJScrollView.m
│ ├── RCTMJScrollViewManager.h
│ ├── RCTMJScrollViewManager.m
│ └── UIView+Private1.h
├── RefreshControlEnrichment.xcodeproj
│ └── project.pbxproj
└── RefreshControlEnrichment.xcworkspace
│ └── contents.xcworkspacedata
├── package.json
├── react-native-refresh-control-enrichment.podspec
└── src
├── androidJS
├── AnyHeader.js
├── DefaultHeader.js
├── SmartRefreshControl.js
└── Util.js
├── file
├── androidVideo.gif
└── iosVideo.gif
├── fusion.js
├── iosJS
├── MJRefresh.js
└── MJScrollView.js
└── util
└── date.js
/.gitattributes:
--------------------------------------------------------------------------------
1 | *.pbxproj -text
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Built application files
2 | *.apk
3 | *.aar
4 | *.ap_
5 | *.aab
6 |
7 | # Files for the ART/Dalvik VM
8 | *.dex
9 |
10 | # Java class files
11 | *.class
12 |
13 | # Generated files
14 | bin/
15 | gen/
16 | out/
17 | # Uncomment the following line in case you need and you don't have the release build type files in your app
18 | # release/
19 |
20 | # Gradle files
21 | .gradle/
22 | build/
23 |
24 | # Local configuration file (sdk path, etc)
25 | local.properties
26 |
27 | # Proguard folder generated by Eclipse
28 | proguard/
29 |
30 | # Log Files
31 | *.log
32 |
33 | # Android Studio Navigation editor temp files
34 | .navigation/
35 |
36 | # Android Studio captures folder
37 | captures/
38 |
39 | # IntelliJ
40 | *.iml
41 | .idea/workspace.xml
42 | .idea/tasks.xml
43 | .idea/gradle.xml
44 | .idea/assetWizardSettings.xml
45 | .idea/dictionaries
46 | .idea/libraries
47 | # Android Studio 3 in .gitignore file.
48 | .idea/caches
49 | .idea/modules.xml
50 | # Comment next line if keeping position of elements in Navigation Editor is relevant for you
51 | .idea/navEditor.xml
52 |
53 | # Keystore files
54 | # Uncomment the following lines if you do not want to check your keystore files in.
55 | #*.jks
56 | #*.keystore
57 |
58 | # External native build folder generated in Android Studio 2.2 and later
59 | .externalNativeBuild
60 | .cxx/
61 |
62 | # Google Services (e.g. APIs or Firebase)
63 | # google-services.json
64 |
65 | # Freeline
66 | freeline.py
67 | freeline/
68 | freeline_project_description.json
69 |
70 | # fastlane
71 | fastlane/report.xml
72 | fastlane/Preview.html
73 | fastlane/screenshots
74 | fastlane/test_output
75 | fastlane/readme.md
76 |
77 | # Version control
78 | vcs.xml
79 |
80 | # lint
81 | lint/intermediates/
82 | lint/generated/
83 | lint/outputs/
84 | lint/tmp/
85 | # lint/reports/
86 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | # OSX
2 | #
3 | .DS_Store
4 |
5 | # Xcode
6 | #
7 | build/
8 | *.pbxuser
9 | !default.pbxuser
10 | *.mode1v3
11 | !default.mode1v3
12 | *.mode2v3
13 | !default.mode2v3
14 | *.perspectivev3
15 | !default.perspectivev3
16 | xcuserdata
17 | *.xccheckout
18 | *.moved-aside
19 | DerivedData
20 | *.hmap
21 | *.ipa
22 | *.xcuserstate
23 |
24 | # node.js
25 | #
26 | node_modules/
27 | npm-debug.log
28 |
29 | # Don't publish example apps
30 | example/
31 | # .idea
32 |
33 | .idea/
34 | .gradle/
35 | pic
36 | /src/file/
37 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 jszh
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 | ## react-native-refresh-control-enrichment
2 | [](https://badge.fury.io/js/react-native-refresh-control-enrichment)
3 |
4 | [](https://github.com/gitSirzh/react-native-refresh-control-enrichment)
5 |
6 | Native Refresh
7 |
8 | ## Android
9 | 
10 |
11 | ## IOS
12 | 
13 |
14 | ## Using npm
15 |
16 | ```shell
17 | npm install react-native-refresh-control-enrichment --save
18 | ```
19 |
20 | or using yarn:
21 |
22 | ```shell
23 | yarn add react-native-refresh-control-enrichment
24 | ```
25 |
26 | ## Installation
27 |
28 | #### react-native version >= 0.61.5
29 | After that, we need to install the dependencies to use the project on iOS(you can skip this part, if you are using this on Android)
30 | Now run:```cd ios && pod install```
31 |
32 | ## Using
33 | ```javascript
34 | import ZHRefreshControl, {ZHScrollView} from 'react-native-refresh-control-enrichment';
35 |
36 | this._finishRefresh = r}
41 | headerHeight={80} // headerHeight
42 | headerBackgroundColor={'#e6e6e6'} // headerBackgroundColor and headerBackgroundImage, do not combine
43 | onRefresh={()=>{
44 | console.log('======== Refreshing ========');
45 | setTimeout(()=>{
46 | // Refreshing End
47 | this._finishRefresh.finishRefresh();
48 | console.log('======== Refreshing End ========');
49 | },2000)
50 | }}
51 | />
52 | }
53 | >
54 |
55 | ⬇️ down 😊
56 |
57 |
58 | ```
59 | ## Using In FlatList
60 | ```javascript
61 | import {FlatList} from 'react-native';
62 | import ZHRefreshControl, {ZHScrollView} from 'react-native-refresh-control-enrichment';
63 |
64 | (
67 | this._finishRefresh = r}
72 | headerHeight={80} // headerHeight
73 | headerBackgroundColor={'#e6e6e6'} // headerBackgroundColor and headerBackgroundImage, do not combine
74 | onRefresh={()=>{
75 | console.log('======== Refreshing ========');
76 | setTimeout(()=>{
77 | // Refreshing End
78 | this._finishRefresh.finishRefresh();
79 | console.log('======== Refreshing End ========');
80 | },2000)
81 | }}
82 | />
83 | }
84 | {...props}
85 | />
86 | )}
87 | keyExtractor={(item, key) => key.toString()}
88 | renderItem={({item, index}) => (
89 |
90 | )}
91 | />
92 | ```
93 |
94 | ## Documentation
95 |
96 | ## Props
97 |
98 | ### `onRefresh`
99 |
100 | 刷新时触发
101 |
102 | | Type | Required |
103 | | ---- | -------- |
104 | | function | Yes |
105 |
106 | ---
107 |
108 | ### `refreshState`
109 |
110 | 刷新时状态
111 |
112 | | Type | Required |
113 | | ---- | -------- |
114 | | function | Yes |
115 |
116 | ---
117 |
118 | ### `loadingView`
119 |
120 | 加载中View
121 |
122 | | Type | Default |
123 | | ---- | -------- |
124 | | element | Yes |
125 |
126 | ---
127 |
128 | ### `headerHeight`
129 |
130 | 头部高度
131 |
132 | | Type | Default |
133 | | ---- | -------- |
134 | | number | 60 |
135 |
136 | ---
137 |
138 | ### `centerTop`
139 |
140 | 内容距离顶部高度(一般为状态栏高度)
141 |
142 | | Type | Default |
143 | | ---- | -------- |
144 | | number | 0 |
145 |
146 | ---
147 |
148 | ### `headerBackgroundColor`
149 |
150 | 头部背景色
151 |
152 | | Type | Default |
153 | | ---- | -------- |
154 | | string | #ffffff |
155 |
156 | ---
157 |
158 | ### `headerBackgroundImage`
159 |
160 | 头部背景图片
161 |
162 | | Type | Required |
163 | | ---- | -------- |
164 | | url | Yes |
165 |
166 | ---
167 |
168 | ### `showText`
169 |
170 | 展示刷新状态
171 |
172 | | Type | Default |
173 | | ---- | -------- |
174 | | boolean | Yes |
175 |
176 | ---
177 |
178 | ### `headerTitleStyle`
179 |
180 | 刷新状态字体样式
181 |
182 | | Type | Default |
183 | | ---- | -------- |
184 | | style | Yes |
185 |
186 | ---
187 |
188 | ### `showDate`
189 |
190 | 展示刷新时间
191 |
192 | | Type | Default |
193 | | ---- | -------- |
194 | | boolean | Yes |
195 |
196 | ---
197 |
198 | ### `headerDateStyle`
199 |
200 | 更新时间字体样式
201 |
202 | | Type | Default |
203 | | ---- | -------- |
204 | | style | Yes |
205 |
206 | ---
207 |
208 | ### `titleArray`
209 |
210 | 自定义提示状态,注:数量需要为四个
211 |
212 | | Type | Default |
213 | | ---- | -------- |
214 | | array | yes |
215 |
216 | ---
217 |
218 | ### `pullView`
219 |
220 | 下拉 LoadingView
221 |
222 | | Type | Default |
223 | | ---- | -------- |
224 | | element | Yes |
225 |
226 | ---
227 |
228 | ### `releaseView`
229 |
230 | 释放 LoadingView
231 |
232 | | Type | Default |
233 | | ---- | -------- |
234 | | element | Yes |
235 |
236 | ---
237 |
238 | ### `successView`
239 |
240 | 成功 LoadingView
241 |
242 | | Type | Default |
243 | | ---- | -------- |
244 | | element | Yes |
245 |
246 | ---
247 |
248 | ## Reference library:
249 | #### Android:
250 | https://github.com/react-native-studio/react-native-SmartRefreshLayout
251 | #### iOS:
252 | https://github.com/react-native-studio/react-native-MJRefresh
253 |
--------------------------------------------------------------------------------
/android/README.md:
--------------------------------------------------------------------------------
1 | README
2 | ======
3 |
4 | If you want to publish the lib as a maven dependency, follow these steps before publishing a new version to npm:
5 |
6 | 1. Be sure to have the Android [SDK](https://developer.android.com/studio/index.html) and [NDK](https://developer.android.com/ndk/guides/index.html) installed
7 | 2. Be sure to have a `local.properties` file in this folder that points to the Android SDK and NDK
8 | ```
9 | ndk.dir=/Users/{username}/Library/Android/sdk/ndk-bundle
10 | sdk.dir=/Users/{username}/Library/Android/sdk
11 | ```
12 | 3. Delete the `maven` folder
13 | 4. Run `./gradlew installArchives`
14 | 5. Verify that latest set of generated files is in the maven folder with the correct version number
15 |
--------------------------------------------------------------------------------
/android/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | def safeExtGet(prop, fallback) {
4 | rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
5 | }
6 |
7 | android {
8 | compileSdkVersion safeExtGet("compileSdkVersion", 28)
9 | buildToolsVersion safeExtGet("buildToolsVersion", "28.0.1")
10 |
11 | defaultConfig {
12 | minSdkVersion safeExtGet("minSdkVersion", 16)
13 | targetSdkVersion safeExtGet("targetSdkVersion", 28)
14 | versionCode 1
15 | versionName "1.0"
16 | }
17 |
18 | lintOptions {
19 | abortOnError false
20 | }
21 |
22 | }
23 |
24 | dependencies {
25 | implementation "com.scwang.smartrefresh:SmartRefreshLayout:1.0.5.1"
26 | implementation "com.scwang.smartrefresh:SmartRefreshHeader:1.0.5.1"
27 | implementation "com.facebook.react:react-native:+"
28 | }
29 |
--------------------------------------------------------------------------------
/android/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/android/src/main/java/com/lmy/header/AnyHeader.java:
--------------------------------------------------------------------------------
1 | package com.lmy.header;
2 |
3 | import android.content.Context;
4 | import android.graphics.drawable.BitmapDrawable;
5 | import androidx.annotation.ColorInt;
6 | import androidx.annotation.NonNull;
7 | import android.view.View;
8 |
9 | import com.facebook.react.views.view.ReactViewGroup;
10 | import com.scwang.smartrefresh.layout.api.RefreshHeader;
11 | import com.scwang.smartrefresh.layout.api.RefreshKernel;
12 | import com.scwang.smartrefresh.layout.api.RefreshLayout;
13 | import com.scwang.smartrefresh.layout.constant.RefreshState;
14 | import com.scwang.smartrefresh.layout.constant.SpinnerStyle;
15 | import com.scwang.smartrefresh.layout.util.DensityUtil;
16 |
17 | /**anyview
18 | * Created by painter.g on 2018/3/9.
19 | */
20 |
21 | public class AnyHeader extends ReactViewGroup implements RefreshHeader {
22 | private RefreshKernel mRefreshKernel;
23 | private int mBackgroundColor;
24 | private Integer mPrimaryColor;
25 | private SpinnerStyle mSpinnerStyle = SpinnerStyle.Translate;
26 |
27 | public AnyHeader(Context context) {
28 | super(context);
29 | initView(context);
30 | }
31 |
32 | @Override
33 | public void onInitialized(@NonNull RefreshKernel kernel, int height, int extendHeight) {
34 | mRefreshKernel = kernel;
35 | mRefreshKernel.requestDrawBackgroundForHeader(mBackgroundColor);
36 | }
37 |
38 | private void initView(Context context) {
39 | setMinimumHeight(DensityUtil.dp2px(60));
40 | }
41 |
42 | public void setView(View v){
43 | addView(v);
44 | }
45 |
46 | @NonNull
47 | public View getView() {
48 | return this;//真实的视图就是自己,不能返回null
49 | }
50 |
51 | @Override
52 | public SpinnerStyle getSpinnerStyle() {
53 | return this.mSpinnerStyle;//指定为平移,不能null
54 | }
55 |
56 | /**
57 | * 设置主题色
58 | * @param colors
59 | */
60 | @Override
61 | public void setPrimaryColors(int... colors) {
62 | if(colors.length>0) {
63 | if (!(getBackground() instanceof BitmapDrawable) && mPrimaryColor == null) {
64 | setPrimaryColor(colors[0]);
65 | mPrimaryColor = null;
66 | }
67 | }
68 | }
69 |
70 | public AnyHeader setPrimaryColor(@ColorInt int primaryColor) {
71 | mBackgroundColor = mPrimaryColor =primaryColor;
72 | if (mRefreshKernel != null) {
73 | mRefreshKernel.requestDrawBackgroundForHeader(mPrimaryColor);
74 | }
75 | return this;
76 | }
77 |
78 | public AnyHeader setSpinnerStyle(SpinnerStyle style){
79 | this.mSpinnerStyle = style;
80 | return this;
81 | }
82 |
83 | @Override
84 | public void onPulling(float percent, int offset, int height, int extendHeight) {
85 |
86 | }
87 |
88 | @Override
89 | public void onReleasing(float percent, int offset, int height, int extendHeight) {
90 |
91 | }
92 |
93 | @Override
94 | public void onReleased(RefreshLayout refreshLayout, int height, int extendHeight) {
95 |
96 | }
97 |
98 | @Override
99 | public void onStartAnimator(@NonNull RefreshLayout refreshLayout, int height, int extendHeight) {
100 |
101 | }
102 |
103 | @Override
104 | public int onFinish(@NonNull RefreshLayout refreshLayout, boolean success) {
105 | return 200;//延迟200毫秒之后再弹回
106 | }
107 |
108 | @Override
109 | public void onHorizontalDrag(float percentX, int offsetX, int offsetMax) {
110 |
111 | }
112 |
113 | @Override
114 | public boolean isSupportHorizontalDrag() {
115 | return false;
116 | }
117 |
118 | @Override
119 | public void onStateChanged(RefreshLayout refreshLayout, RefreshState oldState, RefreshState newState) {
120 |
121 | }
122 |
123 | /*@Override
124 | protected void onFinishInflate() {
125 | int childCount = getChildCount();
126 | for(int i=0;i {
19 | @Override
20 | public String getName() {
21 | return "RCTAnyHeader";
22 | }
23 |
24 | @Override
25 | protected AnyHeader createViewInstance(ThemedReactContext reactContext) {
26 | return new AnyHeader(reactContext);
27 | }
28 |
29 | /**
30 | * 设置主调色
31 | * @param view
32 | * @param primaryColor
33 | */
34 | @ReactProp(name = "primaryColor")
35 | public void setPrimaryColor(AnyHeader view, String primaryColor){
36 | if(primaryColor!=null && !"".equals(primaryColor)){
37 | view.setPrimaryColor(Color.parseColor(primaryColor));
38 | }
39 | }
40 |
41 | /**
42 | * 设置spinnerStyle
43 | * @param view
44 | * @param spinnerStyle
45 | */
46 | @ReactProp(name = "spinnerStyle")
47 | public void setSpinnerStyle(AnyHeader view,String spinnerStyle){
48 | view.setSpinnerStyle(SpinnerStyleConstants.SpinnerStyleMap.get(spinnerStyle));
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/android/src/main/java/com/lmy/header/ClassicsHeaderManager.java:
--------------------------------------------------------------------------------
1 | package com.lmy.header;
2 |
3 | import android.graphics.Color;
4 |
5 | import com.facebook.react.uimanager.SimpleViewManager;
6 | import com.facebook.react.uimanager.ThemedReactContext;
7 | import com.facebook.react.uimanager.annotations.ReactProp;
8 | import com.scwang.smartrefresh.layout.header.ClassicsHeader;
9 |
10 | /**
11 | * Created by painter.g on 2018/3/7.
12 | */
13 |
14 | public class ClassicsHeaderManager extends SimpleViewManager {
15 | @Override
16 | public String getName() {
17 | return "RCTClassicsHeader";
18 | }
19 |
20 | @Override
21 | protected ClassicsHeader createViewInstance(ThemedReactContext reactContext) {
22 | return new ClassicsHeader(reactContext);
23 | }
24 |
25 | /**
26 | * 设置主题颜色
27 | * @param view
28 | * @param primaryColor
29 | */
30 | @ReactProp(name = "primaryColor")
31 | public void setPrimaryColor(ClassicsHeader view,String primaryColor){
32 | view.setPrimaryColor(Color.parseColor(primaryColor));
33 | }
34 |
35 | /**
36 | * 设置强调颜色
37 | * @param view
38 | * @param accentColor
39 | */
40 | @ReactProp(name = "accentColor")
41 | public void setAccentColor(ClassicsHeader view,String accentColor){
42 | view.setAccentColor(Color.parseColor(accentColor));
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/android/src/main/java/com/lmy/header/DefaultHeader.java:
--------------------------------------------------------------------------------
1 | package com.lmy.header;
2 |
3 | import android.content.Context;
4 | import androidx.annotation.ColorInt;
5 | import androidx.annotation.NonNull;
6 | import androidx.annotation.Nullable;
7 | import android.util.AttributeSet;
8 | import android.view.Gravity;
9 | import android.view.View;
10 | import android.view.ViewGroup;
11 | import android.widget.ImageView;
12 | import android.widget.LinearLayout;
13 | import android.widget.RelativeLayout;
14 | import android.widget.TextView;
15 |
16 | import com.facebook.drawee.components.DeferredReleaser;
17 | import com.lmy.smartrefreshlayout.R;
18 | import com.scwang.smartrefresh.layout.api.RefreshHeader;
19 | import com.scwang.smartrefresh.layout.api.RefreshKernel;
20 | import com.scwang.smartrefresh.layout.api.RefreshLayout;
21 | import com.scwang.smartrefresh.layout.constant.RefreshState;
22 | import com.scwang.smartrefresh.layout.constant.SpinnerStyle;
23 | import com.scwang.smartrefresh.layout.internal.ProgressDrawable;
24 | import com.scwang.smartrefresh.layout.internal.pathview.PathsView;
25 | import com.scwang.smartrefresh.layout.util.DensityUtil;
26 |
27 | /**
28 | * Created by painter.g on 2018/3/12.
29 | */
30 |
31 | public class DefaultHeader extends RelativeLayout implements RefreshHeader {
32 | private TextView mHeaderText;//标题文本
33 | private PathsView mArrowView;//下拉箭头
34 | private ImageView mProgressView;//刷新动画视图
35 | private ProgressDrawable mProgressDrawable;//刷新动画
36 | protected RefreshKernel mRefreshKernel;
37 | protected int mBackgroundColor;
38 | protected int mAccentColor;
39 |
40 | public DefaultHeader(Context context) {
41 | super(context);
42 | this.initView(context);
43 | }
44 |
45 | public DefaultHeader(Context context, @Nullable AttributeSet attrs) {
46 | super(context, attrs);
47 | this.initView(context);
48 | }
49 |
50 | public DefaultHeader(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
51 | super(context, attrs, defStyleAttr);
52 | this.initView(context);
53 | }
54 |
55 |
56 | private void initView(Context context) {
57 | RelativeLayout parent = new RelativeLayout(context);
58 | RelativeLayout.LayoutParams rlParent = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
59 | rlParent.addRule(RelativeLayout.CENTER_IN_PARENT,RelativeLayout.TRUE);
60 |
61 |
62 | RelativeLayout.LayoutParams rlArrowView = new RelativeLayout.LayoutParams(DensityUtil.dp2px(20),DensityUtil.dp2px(20));
63 | mArrowView = new PathsView(context);
64 | mArrowView.setId(R.id.arrow_view);
65 | mArrowView.parserColors(0xff666666);
66 | mArrowView.parserPaths("M20,12l-1.41,-1.41L13,16.17V4h-2v12.17l-5.58,-5.59L4,12l8,8 8,-8z");
67 | parent.addView(mArrowView,rlArrowView);
68 |
69 |
70 | RelativeLayout.LayoutParams rlHeaderText= new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
71 | rlHeaderText.addRule(RelativeLayout.RIGHT_OF,mArrowView.getId());
72 | rlHeaderText.leftMargin = DensityUtil.dp2px(20);
73 | mHeaderText = new TextView(context);
74 | mHeaderText.setText("下拉开始刷新");
75 | parent.addView(mHeaderText,rlHeaderText);
76 |
77 | RelativeLayout.LayoutParams rlProgressView = new RelativeLayout.LayoutParams(DensityUtil.dp2px(20),DensityUtil.dp2px(20));
78 | rlProgressView.addRule(RelativeLayout.ALIGN_RIGHT,mArrowView.getId());
79 | mProgressDrawable = new ProgressDrawable();
80 | mProgressView = new ImageView(context);
81 | mProgressView.setImageDrawable(mProgressDrawable);
82 | parent.addView(mProgressView,rlProgressView);
83 |
84 |
85 | addView(parent,rlParent);
86 | //addView(mProgressView, DensityUtil.dp2px(20), DensityUtil.dp2px(20));
87 | //addView(mArrowView, DensityUtil.dp2px(20), DensityUtil.dp2px(20));
88 | //addView(new View(context), DensityUtil.dp2px(20), DensityUtil.dp2px(20));
89 | //addView(mHeaderText, LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
90 | setMinimumHeight(DensityUtil.dp2px(60));
91 | }
92 |
93 | @NonNull
94 | public View getView() {
95 | return this;//真实的视图就是自己,不能返回null
96 | }
97 |
98 | @Override
99 | public SpinnerStyle getSpinnerStyle() {
100 | return SpinnerStyle.Translate;//指定为平移,不能null
101 | }
102 |
103 | @Override
104 | public void onStartAnimator(RefreshLayout layout, int headHeight, int extendHeight) {
105 | mProgressDrawable.start();//开始动画
106 | }
107 |
108 | @Override
109 | public int onFinish(RefreshLayout layout, boolean success) {
110 | mProgressDrawable.stop();//停止动画
111 | if (success) {
112 | mHeaderText.setText("刷新完成");
113 | } else {
114 | mHeaderText.setText("刷新失败");
115 | }
116 | return 500;//延迟500毫秒之后再弹回
117 | }
118 |
119 | @Override
120 | public void onStateChanged(RefreshLayout refreshLayout, RefreshState oldState, RefreshState newState) {
121 | switch (newState) {
122 | case None:
123 | case PullDownToRefresh:
124 | mHeaderText.setText("下拉开始刷新");
125 | mArrowView.setVisibility(VISIBLE);//显示下拉箭头
126 | mProgressView.setVisibility(GONE);//隐藏动画
127 | mArrowView.animate().rotation(0);//还原箭头方向
128 | break;
129 | case Refreshing:
130 | mHeaderText.setText("正在刷新");
131 | mProgressView.setVisibility(VISIBLE);//显示加载动画
132 | mArrowView.setVisibility(GONE);//隐藏箭头
133 | break;
134 | case ReleaseToRefresh:
135 | mHeaderText.setText("释放立即刷新");
136 | mArrowView.animate().rotation(180);//显示箭头改为朝上
137 | break;
138 | }
139 | }
140 |
141 | @Override
142 | public boolean isSupportHorizontalDrag() {
143 | return false;
144 | }
145 |
146 | @Override
147 | public void onInitialized(RefreshKernel kernel, int height, int extendHeight) {
148 | mRefreshKernel = kernel;
149 | mRefreshKernel.requestDrawBackgroundForHeader(mBackgroundColor);
150 |
151 | }
152 |
153 | @Override
154 | public void onHorizontalDrag(float percentX, int offsetX, int offsetMax) {
155 | }
156 |
157 | @Override
158 | public void onPulling(float percent, int offset, int headHeight, int extendHeight) {
159 | }
160 |
161 | @Override
162 | public void onReleasing(float percent, int offset, int headHeight, int extendHeight) {
163 | }
164 |
165 | @Override
166 | public void onReleased(RefreshLayout refreshLayout, int height, int extendHeight) {
167 |
168 | }
169 |
170 | @Override
171 | public void setPrimaryColors(@ColorInt int... colors) {
172 | }
173 |
174 | public DefaultHeader setPrimaryColor(@ColorInt int primaryColor) {
175 | mBackgroundColor = primaryColor;
176 | if (mRefreshKernel != null) {
177 | mRefreshKernel.requestDrawBackgroundForHeader(primaryColor);
178 | }
179 | return this;
180 | }
181 |
182 | public DefaultHeader setAccentColor(int accentColor){
183 | mAccentColor=accentColor;
184 | if(mArrowView!=null){
185 | mArrowView.parserColors(accentColor);
186 | }
187 | if(mProgressDrawable!=null) {
188 | mProgressDrawable.setColor(accentColor);
189 | }
190 | mHeaderText.setTextColor(accentColor);
191 | return this;
192 | }
193 |
194 | }
195 |
--------------------------------------------------------------------------------
/android/src/main/java/com/lmy/header/DefaultHeaderMananger.java:
--------------------------------------------------------------------------------
1 | package com.lmy.header;
2 |
3 | import android.graphics.Color;
4 |
5 | import com.facebook.react.uimanager.SimpleViewManager;
6 | import com.facebook.react.uimanager.ThemedReactContext;
7 | import com.facebook.react.uimanager.annotations.ReactProp;
8 |
9 | /**
10 | * Created by painter.g on 2018/3/12.
11 | */
12 |
13 | public class DefaultHeaderMananger extends SimpleViewManager {
14 | @Override
15 | public String getName() {
16 | return "RCTDefaultHeader";
17 | }
18 |
19 | @Override
20 | protected DefaultHeader createViewInstance(ThemedReactContext reactContext) {
21 | return new DefaultHeader(reactContext);
22 | }
23 | /**
24 | * 设置主题颜色
25 | * @param view
26 | * @param primaryColor
27 | */
28 | @ReactProp(name = "primaryColor")
29 | public void setPrimaryColor(DefaultHeader view,String primaryColor){
30 | view.setPrimaryColor(Color.parseColor(primaryColor));
31 | }
32 |
33 | /**
34 | * 设置强调颜色
35 | * @param view
36 | * @param accentColor
37 | */
38 | @ReactProp(name = "accentColor")
39 | public void setAccentColor(DefaultHeader view,String accentColor){
40 | view.setAccentColor(Color.parseColor(accentColor));
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/android/src/main/java/com/lmy/header/MaterialHeaderManager.java:
--------------------------------------------------------------------------------
1 | package com.lmy.header;
2 |
3 | import com.facebook.react.uimanager.ThemedReactContext;
4 | import com.facebook.react.uimanager.ViewGroupManager;
5 | import com.scwang.smartrefresh.header.MaterialHeader;
6 |
7 | /**
8 | * Created by painter.g on 2018/3/8.
9 | */
10 |
11 | public class MaterialHeaderManager extends ViewGroupManager {
12 | @Override
13 | public String getName() {
14 | return "RCTMaterialHeader";
15 | }
16 |
17 | @Override
18 | protected MaterialHeader createViewInstance(ThemedReactContext reactContext) {
19 | return new MaterialHeader(reactContext);
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/android/src/main/java/com/lmy/header/StoreHouseHeaderManager.java:
--------------------------------------------------------------------------------
1 | package com.lmy.header;
2 |
3 | import android.graphics.Color;
4 |
5 | import com.facebook.react.uimanager.SimpleViewManager;
6 | import com.facebook.react.uimanager.ThemedReactContext;
7 | import com.facebook.react.uimanager.annotations.ReactProp;
8 | import com.scwang.smartrefresh.header.StoreHouseHeader;
9 |
10 | /**
11 | * Created by painter.g on 2018/3/7.
12 | */
13 |
14 | public class StoreHouseHeaderManager extends SimpleViewManager {
15 |
16 | private String textStr="STOREHOUSE";
17 | @Override
18 | public String getName() {
19 | return "RCTStoreHouseHeader";
20 | }
21 |
22 | @Override
23 | protected StoreHouseHeader createViewInstance(ThemedReactContext reactContext) {
24 | return new StoreHouseHeader(reactContext);
25 | }
26 |
27 | /**
28 | * 设置字体颜色
29 | * @param view
30 | * @param textColor
31 | */
32 | @ReactProp(name = "textColor")
33 | public void setTextColor(StoreHouseHeader view,String textColor){
34 | view.setTextColor(Color.parseColor(textColor));
35 | }
36 |
37 | /**
38 | * 设置文字
39 | * @param view
40 | * @param text
41 | */
42 | @ReactProp(name="text")
43 | public void setText(StoreHouseHeader view,String text){
44 | textStr=text;
45 | view.initWithString(text);
46 | }
47 |
48 | /**
49 | * 设置字体尺寸
50 | * TODO:似乎还没有作用
51 | * @param view
52 | * @param fontSize
53 | */
54 | @ReactProp(name="fontSize",defaultInt = 25)
55 | public void setFontSize(StoreHouseHeader view,int fontSize){
56 | view.initWithString(textStr,fontSize);
57 | }
58 |
59 | /**
60 | * 设置线宽
61 | * @param view
62 | * @param lineWidth
63 | */
64 | @ReactProp(name = "lineWidth")
65 | public void setLineWidth(StoreHouseHeader view,int lineWidth){
66 | view.setLineWidth(lineWidth);
67 | }
68 |
69 | /**
70 | * 设置dropHeight
71 | * TODO:似乎还没有作用
72 | * @param view
73 | * @param dropHeight
74 | */
75 | @ReactProp(name="dropHeight")
76 | public void setDropHeight(StoreHouseHeader view,int dropHeight){
77 | view.setDropHeight(dropHeight);
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/android/src/main/java/com/lmy/smartrefreshlayout/Events.java:
--------------------------------------------------------------------------------
1 | package com.lmy.smartrefreshlayout;
2 |
3 | /**
4 | * Created by lmy2534290808 on 2017/12/2.
5 | */
6 |
7 | public enum Events {
8 | REFRESH("onSmartRefresh"),//刷新触发
9 | LOAD_MORE("onLoadMore"),//加载更多触发
10 | HEADER_PULLING("onHeaderPulling"),//header下拉触发
11 | HEADER_RELEASING("onHeaderReleasing"),//header刷新完成后触发
12 | PULL_DOWN_TO_REFRESH("onPullDownToRefresh"),//下拉开始刷新
13 | RELEASE_TO_REFRESH("onReleaseToRefresh"),//释放刷新
14 | HEADER_RELEASED("onHeaderReleased"),//释放时进行刷新
15 | FOOTER_MOVING("onFooterMoving");//footer移动时触发
16 |
17 | private final String mName;
18 |
19 | Events(final String name) {
20 | mName = name;
21 | }
22 |
23 | @Override
24 | public String toString() {
25 | return mName;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/android/src/main/java/com/lmy/smartrefreshlayout/HeaderType.java:
--------------------------------------------------------------------------------
1 | package com.lmy.smartrefreshlayout;
2 |
3 | /**
4 | * Created by painter.g on 2018/3/7.
5 | */
6 |
7 | public class HeaderType {
8 | public static final String CLASSIC="Classic";
9 | }
10 |
--------------------------------------------------------------------------------
/android/src/main/java/com/lmy/smartrefreshlayout/RCTSpinnerStyleModule.java:
--------------------------------------------------------------------------------
1 | package com.lmy.smartrefreshlayout;
2 |
3 | import com.facebook.react.bridge.ReactApplicationContext;
4 | import com.facebook.react.bridge.ReactContextBaseJavaModule;
5 | import com.scwang.smartrefresh.layout.constant.SpinnerStyle;
6 |
7 | import java.util.Collections;
8 | import java.util.HashMap;
9 | import java.util.Map;
10 |
11 | import javax.annotation.Nullable;
12 |
13 | /**
14 | * Created by macbook on 2018/6/13.
15 | */
16 |
17 | public class RCTSpinnerStyleModule extends ReactContextBaseJavaModule {
18 | private static final String MODULE_NAME = "RCTSpinnerStyleModule";
19 |
20 | public RCTSpinnerStyleModule(ReactApplicationContext reactContext) {
21 | super(reactContext);
22 | }
23 |
24 | @Override
25 | public String getName() {
26 | return MODULE_NAME;
27 | }
28 |
29 | /**
30 | *
31 | *Translate,//平行移动 特点: HeaderView高度不会改变,
32 | *Scale,//拉伸形变 特点:在下拉和上弹(HeaderView高度改变)时候,会自动触发OnDraw事件
33 | *FixedBehind,//固定在背后 特点:HeaderView高度不会改变,
34 | *FixedFront,//固定在前面 特点:HeaderView高度不会改变,
35 | *MatchLayout//填满布局
36 | *
37 | * @return
38 | */
39 | @Nullable
40 | @Override
41 | public Map getConstants() {
42 | return Collections.unmodifiableMap(new HashMap(){{
43 | put("translate", SpinnerStyleConstants.TRANSLATE);
44 | put("fixBehind",SpinnerStyleConstants.FIX_BEHIND);
45 | put("fixFront",SpinnerStyleConstants.FIX_FRONT);
46 | put("scale",SpinnerStyleConstants.SCALE);
47 | put("matchLayout",SpinnerStyleConstants.MATCH_LAYOUT);
48 | }});
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/android/src/main/java/com/lmy/smartrefreshlayout/ReactSmartRefreshLayout.java:
--------------------------------------------------------------------------------
1 | package com.lmy.smartrefreshlayout;
2 |
3 | import android.content.Context;
4 | import android.view.MotionEvent;
5 |
6 | import com.facebook.react.uimanager.events.NativeGestureUtil;
7 | import com.scwang.smartrefresh.layout.SmartRefreshLayout;
8 |
9 | /**
10 | * Created by painter.g on 2018/3/7.
11 | */
12 |
13 | public class ReactSmartRefreshLayout extends SmartRefreshLayout {
14 | private static final float DEFAULT_CIRCLE_TARGET = 64;
15 |
16 | private boolean mDidLayout = false;
17 | private boolean mRefreshing = false;
18 | private float mProgressViewOffset = 0;
19 | private int mTouchSlop;
20 | private float mPrevTouchX;
21 | private boolean mIntercepted;
22 | public ReactSmartRefreshLayout(Context context) {
23 | super(context);
24 | }
25 | @Override
26 | public void onLayout(boolean changed, int left, int top, int right, int bottom) {
27 | super.onLayout(changed, left, top, right, bottom);
28 |
29 | if (!mDidLayout) {
30 | mDidLayout = true;
31 |
32 | // Update values that must be set after initial layout.
33 | // setProgressViewOffset(mProgressViewOffset);
34 | // setRefreshing(mRefreshing);
35 | }
36 | }
37 |
38 |
39 | @Override
40 | public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
41 | if (getParent() != null) {
42 | getParent().requestDisallowInterceptTouchEvent(disallowIntercept);
43 | }
44 | }
45 |
46 | @Override
47 | public boolean onInterceptTouchEvent(MotionEvent ev) {
48 | if (shouldInterceptTouchEvent(ev) && super.onInterceptTouchEvent(ev)) {
49 | NativeGestureUtil.notifyNativeGestureStarted(this, ev);
50 | return true;
51 | }
52 | return false;
53 | }
54 |
55 |
56 | private boolean shouldInterceptTouchEvent(MotionEvent ev) {
57 | switch (ev.getAction()) {
58 | case MotionEvent.ACTION_DOWN:
59 | mPrevTouchX = ev.getX();
60 | mIntercepted = false;
61 | break;
62 |
63 | case MotionEvent.ACTION_MOVE:
64 | final float eventX = ev.getX();
65 | final float xDiff = Math.abs(eventX - mPrevTouchX);
66 |
67 | if (mIntercepted || xDiff > mTouchSlop) {
68 | mIntercepted = true;
69 | return false;
70 | }
71 | }
72 | return true;
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/android/src/main/java/com/lmy/smartrefreshlayout/SmartRefreshLayoutManager.java:
--------------------------------------------------------------------------------
1 | package com.lmy.smartrefreshlayout;
2 |
3 | import android.graphics.Color;
4 | import android.view.View;
5 |
6 | import com.facebook.react.bridge.Arguments;
7 | import com.facebook.react.bridge.ReadableArray;
8 | import com.facebook.react.bridge.ReadableMap;
9 | import com.facebook.react.bridge.WritableMap;
10 | import com.facebook.react.common.MapBuilder;
11 | import com.facebook.react.uimanager.ThemedReactContext;
12 | import com.facebook.react.uimanager.ViewGroupManager;
13 | import com.facebook.react.uimanager.annotations.ReactProp;
14 | import com.facebook.react.uimanager.events.RCTEventEmitter;
15 | import com.lmy.header.AnyHeader;
16 | import com.scwang.smartrefresh.layout.api.RefreshFooter;
17 | import com.scwang.smartrefresh.layout.api.RefreshHeader;
18 | import com.scwang.smartrefresh.layout.api.RefreshLayout;
19 | import com.scwang.smartrefresh.layout.constant.RefreshState;
20 | import com.scwang.smartrefresh.layout.listener.OnRefreshListener;
21 | import com.scwang.smartrefresh.layout.listener.SimpleMultiPurposeListener;
22 | import com.scwang.smartrefresh.layout.util.DensityUtil;
23 |
24 | import java.util.List;
25 | import java.util.Map;
26 |
27 | import javax.annotation.Nullable;
28 |
29 | /**
30 | * Created by painter.g on 2018/3/6.
31 | * SmartRefreshLayout插件的封装
32 | * https://github.com/scwang90/SmartRefreshLayout
33 | */
34 |
35 | public class SmartRefreshLayoutManager extends ViewGroupManager{
36 | //返回给rn的组件名
37 | protected static final String REACT_CLASS="SmartRefreshLayout";
38 |
39 | private ReactSmartRefreshLayout smartRefreshLayout;
40 | private RCTEventEmitter mEventEmitter;
41 | private ThemedReactContext themedReactContext;
42 |
43 | private static final String COMMAND_FINISH_REFRESH_NAME="finishRefresh";
44 | private static final int COMMAND_FINISH_REFRESH_ID=0;
45 |
46 | @Override
47 | public String getName() {
48 | return REACT_CLASS;
49 | }
50 |
51 | @Override
52 | protected ReactSmartRefreshLayout createViewInstance(ThemedReactContext reactContext) {
53 | smartRefreshLayout=new ReactSmartRefreshLayout(reactContext);
54 | smartRefreshLayout.setEnableLoadMore(false);//暂时禁止上拉加载
55 | themedReactContext=reactContext;
56 | mEventEmitter=reactContext.getJSModule(RCTEventEmitter.class);
57 | return smartRefreshLayout;
58 | }
59 |
60 | @Override
61 | public Map getExportedCustomDirectEventTypeConstants() {
62 | MapBuilder.Builder builder = MapBuilder.builder();
63 | for (Events event : Events.values()) {
64 | builder.put(event.toString(), MapBuilder.of("registrationName", event.toString()));
65 | }
66 | return builder.build();
67 | }
68 |
69 | @Nullable
70 | @Override
71 | public Map getCommandsMap() {
72 | return MapBuilder.of(
73 | COMMAND_FINISH_REFRESH_NAME,COMMAND_FINISH_REFRESH_ID
74 | );
75 | }
76 | /**
77 | * 最大显示下拉高度/Header标准高度
78 | * @param view
79 | * @param maxDragRate
80 | */
81 | @ReactProp(name="maxDragRate",defaultFloat = 2.0f)
82 | public void setMaxDragRate(ReactSmartRefreshLayout view,float maxDragRate){
83 | view.setHeaderMaxDragRate(maxDragRate);
84 | }
85 | /**
86 | * 显示下拉高度/手指真实下拉高度=阻尼效果
87 | * @param view
88 | * @param dragRate
89 | */
90 | @ReactProp(name = "dragRate",defaultFloat = 0.5f)
91 | public void setDragRate(ReactSmartRefreshLayout view,float dragRate){
92 | view.setDragRate(dragRate);
93 | }
94 | /**
95 | * 是否使用越界拖动
96 | * @param view
97 | * @param overScrollDrag
98 | */
99 | @ReactProp(name="overScrollDrag",defaultBoolean = true)
100 | public void setOverScrollDrag(ReactSmartRefreshLayout view,boolean overScrollDrag){
101 | view.setEnableOverScrollDrag(overScrollDrag);
102 | }
103 | /**
104 | * 是否启用越界回弹
105 | * @param view
106 | * @param overScrollBounce
107 | */
108 | @ReactProp(name = "overScrollBounce",defaultBoolean = true)
109 | public void setOverScrollBounce(ReactSmartRefreshLayout view,boolean overScrollBounce){
110 | view.setEnableOverScrollBounce(overScrollBounce);
111 | }
112 | /**
113 | * 设置为纯滚动
114 | * @param view
115 | * @param pureScroll
116 | */
117 | @ReactProp(name = "pureScroll",defaultBoolean = false)
118 | public void setPureScroll(ReactSmartRefreshLayout view,boolean pureScroll){
119 | view.setEnablePureScrollMode(pureScroll);
120 | }
121 | /**
122 | * 通过RefreshLayout设置主题色
123 | * @param view
124 | * @param primaryColor
125 | */
126 | @ReactProp(name = "primaryColor",defaultInt = Color.TRANSPARENT)
127 | public void setPrimaryColor(ReactSmartRefreshLayout view, int primaryColor){
128 | view.setPrimaryColors(primaryColor);
129 | }
130 | /**
131 | * 设置headerHeight
132 | * @param view
133 | * @param headerHeight
134 | */
135 | @ReactProp(name = "headerHeight")
136 | public void setHeaderHeight(ReactSmartRefreshLayout view,float headerHeight){
137 | if(headerHeight != 0.0f) {
138 | view.setHeaderHeight(headerHeight);
139 |
140 | }
141 | }
142 | /**
143 | * 是否启用下拉刷新功能
144 | * @param view
145 | * @param enableRefresh
146 | */
147 | @ReactProp(name="enableRefresh",defaultBoolean = true)
148 | public void setEnableRefresh(ReactSmartRefreshLayout view,boolean enableRefresh){
149 | view.setEnableRefresh(enableRefresh);
150 | }
151 |
152 | /**
153 | * 是否启用自动刷新
154 | * @param view
155 | * @param autoRefresh
156 | */
157 | @ReactProp(name = "autoRefresh",defaultBoolean = false)
158 | public void setAutoRefresh(ReactSmartRefreshLayout view, ReadableMap autoRefresh){
159 | boolean isAutoRefresh=false;Integer time=null;
160 | if(autoRefresh.hasKey("refresh")){
161 | isAutoRefresh=autoRefresh.getBoolean("refresh");
162 | }
163 | if(autoRefresh.hasKey("time")){
164 | time=autoRefresh.getInt("time");
165 | }
166 | if(isAutoRefresh==true){
167 | if(time!=null && time>0){
168 | view.autoRefresh(time);
169 | }else{
170 | view.autoRefresh();
171 | }
172 | }
173 | }
174 | @Override
175 | public void receiveCommand(ReactSmartRefreshLayout root, int commandId, @Nullable ReadableArray args) {
176 | switch (commandId){
177 | case COMMAND_FINISH_REFRESH_ID:
178 | int delayed=args.getInt(0);
179 | boolean success=args.getBoolean(1);
180 | if(delayed>=0){
181 | root.finishRefresh(delayed,success);
182 | }else{
183 | root.finishRefresh(success);
184 | }
185 | break;
186 | default:break;
187 | }
188 | }
189 |
190 | @Override
191 | public void addView(ReactSmartRefreshLayout parent, View child, int index) {
192 | switch (index){
193 | case 0:
194 | RefreshHeader header;
195 | if(child instanceof RefreshHeader){
196 | header=(RefreshHeader)child;
197 | }else{
198 | header=new AnyHeader(themedReactContext);
199 | ((AnyHeader)header).setView(child);
200 | }
201 | parent.setRefreshHeader(header);
202 | //parent.setRefreshHeader(new MaterialHeader(themedReactContext).setShowBezierWave(true));
203 | break;
204 | case 1:
205 | parent.setRefreshContent(child);
206 | break;
207 | case 2:
208 | //RefreshFooter footer=(RefreshFooter)child;
209 | //parent.setRefreshFooter(footer);
210 | break;
211 | default:break;
212 |
213 | }
214 | }
215 |
216 | @Override
217 | public void addViews(ReactSmartRefreshLayout parent, List views) {
218 | super.addViews(parent, views);
219 | }
220 |
221 | @Override
222 | protected void addEventEmitters(ThemedReactContext reactContext,final ReactSmartRefreshLayout view) {
223 |
224 |
225 | /**
226 | * 必须设置OnRefreshListener,如果没有设置,
227 | * 则会自动触发finishRefresh
228 | *
229 | * OnRefreshListener和OnSimpleMultiPurposeListener
230 | * 中的onRefresh都会触发刷新,只需写一个即可
231 | */
232 | view.setOnRefreshListener(new OnRefreshListener() {
233 | @Override
234 | public void onRefresh(RefreshLayout refreshLayout) {
235 |
236 | }
237 | });
238 | view.setOnMultiPurposeListener(new SimpleMultiPurposeListener() {
239 | private int getTargetId(){
240 | return view.getId();
241 | }
242 |
243 | @Override
244 | public void onHeaderPulling(RefreshHeader header, float percent, int offset, int headerHeight, int extendHeight) {
245 | WritableMap writableMap = Arguments.createMap();
246 | writableMap.putDouble("percent",percent);
247 | writableMap.putDouble("offset",DensityUtil.px2dp(offset));
248 | writableMap.putDouble("headerHeight",DensityUtil.px2dp(headerHeight));
249 | mEventEmitter.receiveEvent(getTargetId(),Events.HEADER_PULLING.toString(),writableMap);
250 | }
251 |
252 | @Override
253 | public void onHeaderReleased(RefreshHeader header, int headerHeight, int extendHeight) {
254 | WritableMap writableMap = Arguments.createMap();
255 | writableMap.putDouble("headerHeight",DensityUtil.px2dp(headerHeight));
256 | mEventEmitter.receiveEvent(getTargetId(),Events.HEADER_RELEASED.toString(),writableMap);
257 | }
258 |
259 | @Override
260 | public void onHeaderReleasing(RefreshHeader header, float percent, int offset, int headerHeight, int extendHeight) {
261 | WritableMap writableMap = Arguments.createMap();
262 | writableMap.putDouble("percent",percent);
263 | writableMap.putDouble("offset",DensityUtil.px2dp(offset));
264 | writableMap.putDouble("headerHeight",DensityUtil.px2dp(headerHeight));
265 | mEventEmitter.receiveEvent(getTargetId(),Events.HEADER_RELEASING.toString(),writableMap);
266 | }
267 |
268 | @Override
269 | public void onFooterPulling(RefreshFooter footer, float percent, int offset, int footerHeight, int extendHeight) {
270 | WritableMap writableMap = Arguments.createMap();
271 | writableMap.putDouble("percent",percent);
272 | writableMap.putDouble("offset",DensityUtil.px2dp(offset));
273 | writableMap.putDouble("footerHeight",DensityUtil.px2dp(footerHeight));
274 | mEventEmitter.receiveEvent(getTargetId(),Events.FOOTER_MOVING.toString(),writableMap);
275 | }
276 |
277 | @Override
278 | public void onFooterReleasing(RefreshFooter footer, float percent, int offset, int footerHeight, int extendHeight) {
279 | WritableMap writableMap = Arguments.createMap();
280 | writableMap.putDouble("percent",percent);
281 | writableMap.putDouble("offset",DensityUtil.px2dp(offset));
282 | writableMap.putDouble("footerHeight",DensityUtil.px2dp(footerHeight));
283 | mEventEmitter.receiveEvent(getTargetId(),Events.FOOTER_MOVING.toString(),writableMap);
284 | }
285 |
286 | @Override
287 | public void onHeaderStartAnimator(RefreshHeader header, int headerHeight, int extendHeight) {
288 |
289 | }
290 |
291 | @Override
292 | public void onHeaderFinish(RefreshHeader header, boolean success) {
293 |
294 | }
295 | @Override
296 | public void onLoadMore(RefreshLayout refreshLayout) {
297 | mEventEmitter.receiveEvent(getTargetId(),Events.LOAD_MORE.toString(),null);
298 | }
299 |
300 | @Override
301 | public void onRefresh(RefreshLayout refreshLayout) {
302 | mEventEmitter.receiveEvent(getTargetId(),Events.REFRESH.toString(),null);
303 | }
304 |
305 | @Override
306 | public void onStateChanged(RefreshLayout refreshLayout, RefreshState oldState, RefreshState newState) {
307 | switch (newState) {
308 | case None:
309 | case PullDownToRefresh:
310 | mEventEmitter.receiveEvent(getTargetId(),Events.PULL_DOWN_TO_REFRESH.toString(),null);
311 | break;
312 | case Refreshing:
313 |
314 | break;
315 | case ReleaseToRefresh:
316 | mEventEmitter.receiveEvent(getTargetId(),Events.RELEASE_TO_REFRESH.toString(),null);
317 | break;
318 | }
319 |
320 | }
321 | });
322 | }
323 | private int getTargetId(){
324 | return smartRefreshLayout.getId();
325 | }
326 | }
327 |
--------------------------------------------------------------------------------
/android/src/main/java/com/lmy/smartrefreshlayout/SmartRefreshLayoutPackage.java:
--------------------------------------------------------------------------------
1 | package com.lmy.smartrefreshlayout;
2 |
3 | import com.facebook.react.ReactPackage;
4 | import com.facebook.react.bridge.JavaScriptModule;
5 | import com.facebook.react.bridge.NativeModule;
6 | import com.facebook.react.bridge.ReactApplicationContext;
7 | import com.facebook.react.uimanager.ViewManager;
8 | import com.lmy.header.AnyHeaderManager;
9 | import com.lmy.header.ClassicsHeaderManager;
10 | import com.lmy.header.DefaultHeaderMananger;
11 | import com.lmy.header.MaterialHeaderManager;
12 | import com.lmy.header.StoreHouseHeaderManager;
13 |
14 | import java.util.Arrays;
15 | import java.util.Collections;
16 | import java.util.List;
17 |
18 | /**
19 | * Created by painter.g on 2018/3/6.
20 | */
21 |
22 | public class SmartRefreshLayoutPackage implements ReactPackage {
23 | @Override
24 | public List createNativeModules(ReactApplicationContext reactContext) {
25 | return Arrays.asList(
26 | new RCTSpinnerStyleModule(reactContext)
27 | );
28 | }
29 | //@Override >=0.47已经过期
30 | public List> createJSModules() {
31 | return Collections.emptyList();
32 | }
33 | @Override
34 | public List createViewManagers(ReactApplicationContext reactContext) {
35 | return Arrays.asList(
36 | new SmartRefreshLayoutManager(),
37 | new ClassicsHeaderManager(),
38 | new StoreHouseHeaderManager(),
39 | new MaterialHeaderManager(),
40 | new AnyHeaderManager(),
41 | new DefaultHeaderMananger()
42 | );
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/android/src/main/java/com/lmy/smartrefreshlayout/SpinnerStyleConstants.java:
--------------------------------------------------------------------------------
1 | package com.lmy.smartrefreshlayout;
2 |
3 | import com.scwang.smartrefresh.layout.constant.SpinnerStyle;
4 |
5 | import java.util.HashMap;
6 |
7 | /**
8 | * Created by macbook on 2018/6/13.
9 | */
10 |
11 | public class SpinnerStyleConstants {
12 | public static final String TRANSLATE = "translate";
13 | public static final String FIX_BEHIND = "fixBehind";
14 | public static final String SCALE = "scale";
15 | public static final String FIX_FRONT = "fixFront";
16 | public static final String MATCH_LAYOUT = "matchLayout";
17 | public static final HashMap SpinnerStyleMap = new HashMap(){{
18 | put(TRANSLATE,SpinnerStyle.Translate);
19 | put(FIX_BEHIND,SpinnerStyle.FixedBehind);
20 | put(SCALE,SpinnerStyle.Scale);
21 | put(MATCH_LAYOUT,SpinnerStyle.MatchLayout);
22 | put(FIX_FRONT,SpinnerStyle.FixedFront);
23 | }};
24 | }
25 |
--------------------------------------------------------------------------------
/android/src/main/res/values/ids.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/android/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | SmartRefreshLayout
3 |
4 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by jszh on 2020/5/12.
3 | */
4 |
5 | import ZHRefreshControl from './src/fusion'
6 | import {ZHScrollView} from './src/iosJS/MJRefresh';
7 |
8 | export {ZHScrollView}
9 | export default ZHRefreshControl;
10 |
--------------------------------------------------------------------------------
/ios/MJRefresh/Base/MJRefreshAutoFooter.h:
--------------------------------------------------------------------------------
1 | //
2 | // MJRefreshAutoFooter.h
3 | // MJRefreshExample
4 | //
5 | // Created by MJ Lee on 15/4/24.
6 | // Copyright (c) 2015年 小码哥. All rights reserved.
7 | //
8 |
9 | #import "MJRefreshFooter.h"
10 |
11 | @interface MJRefreshAutoFooter : MJRefreshFooter
12 | /** 是否自动刷新(默认为YES) */
13 | @property (assign, nonatomic, getter=isAutomaticallyRefresh) BOOL automaticallyRefresh;
14 |
15 | /** 当底部控件出现多少时就自动刷新(默认为1.0,也就是底部控件完全出现时,才会自动刷新) */
16 | @property (assign, nonatomic) CGFloat appearencePercentTriggerAutoRefresh MJRefreshDeprecated("请使用triggerAutomaticallyRefreshPercent属性");
17 |
18 | /** 当底部控件出现多少时就自动刷新(默认为1.0,也就是底部控件完全出现时,才会自动刷新) */
19 | @property (assign, nonatomic) CGFloat triggerAutomaticallyRefreshPercent;
20 |
21 | /** 是否每一次拖拽只发一次请求 */
22 | @property (assign, nonatomic, getter=isOnlyRefreshPerDrag) BOOL onlyRefreshPerDrag;
23 | @end
24 |
--------------------------------------------------------------------------------
/ios/MJRefresh/Base/MJRefreshAutoFooter.m:
--------------------------------------------------------------------------------
1 | //
2 | // MJRefreshAutoFooter.m
3 | // MJRefreshExample
4 | //
5 | // Created by MJ Lee on 15/4/24.
6 | // Copyright (c) 2015年 小码哥. All rights reserved.
7 | //
8 |
9 | #import "MJRefreshAutoFooter.h"
10 |
11 | @interface MJRefreshAutoFooter()
12 | /** 一个新的拖拽 */
13 | @property (assign, nonatomic, getter=isOneNewPan) BOOL oneNewPan;
14 | @end
15 |
16 | @implementation MJRefreshAutoFooter
17 |
18 | #pragma mark - 初始化
19 | - (void)willMoveToSuperview:(UIView *)newSuperview
20 | {
21 | [super willMoveToSuperview:newSuperview];
22 |
23 | if (newSuperview) { // 新的父控件
24 | if (self.hidden == NO) {
25 | self.scrollView.mj_insetB += self.mj_h;
26 | }
27 |
28 | // 设置位置
29 | self.mj_y = _scrollView.mj_contentH;
30 | } else { // 被移除了
31 | if (self.hidden == NO) {
32 | self.scrollView.mj_insetB -= self.mj_h;
33 | }
34 | }
35 | }
36 |
37 | #pragma mark - 过期方法
38 | - (void)setAppearencePercentTriggerAutoRefresh:(CGFloat)appearencePercentTriggerAutoRefresh
39 | {
40 | self.triggerAutomaticallyRefreshPercent = appearencePercentTriggerAutoRefresh;
41 | }
42 |
43 | - (CGFloat)appearencePercentTriggerAutoRefresh
44 | {
45 | return self.triggerAutomaticallyRefreshPercent;
46 | }
47 |
48 | #pragma mark - 实现父类的方法
49 | - (void)prepare
50 | {
51 | [super prepare];
52 |
53 | // 默认底部控件100%出现时才会自动刷新
54 | self.triggerAutomaticallyRefreshPercent = 1.0;
55 |
56 | // 设置为默认状态
57 | self.automaticallyRefresh = YES;
58 |
59 | // 默认是当offset达到条件就发送请求(可连续)
60 | self.onlyRefreshPerDrag = NO;
61 | }
62 |
63 | - (void)scrollViewContentSizeDidChange:(NSDictionary *)change
64 | {
65 | [super scrollViewContentSizeDidChange:change];
66 |
67 | // 设置位置
68 | self.mj_y = self.scrollView.mj_contentH;
69 | }
70 |
71 | - (void)scrollViewContentOffsetDidChange:(NSDictionary *)change
72 | {
73 | [super scrollViewContentOffsetDidChange:change];
74 |
75 | if (self.state != MJRefreshStateIdle || !self.automaticallyRefresh || self.mj_y == 0) return;
76 |
77 | if (_scrollView.mj_insetT + _scrollView.mj_contentH > _scrollView.mj_h) { // 内容超过一个屏幕
78 | // 这里的_scrollView.mj_contentH替换掉self.mj_y更为合理
79 | if (_scrollView.mj_offsetY >= _scrollView.mj_contentH - _scrollView.mj_h + self.mj_h * self.triggerAutomaticallyRefreshPercent + _scrollView.mj_insetB - self.mj_h) {
80 | // 防止手松开时连续调用
81 | CGPoint old = [change[@"old"] CGPointValue];
82 | CGPoint new = [change[@"new"] CGPointValue];
83 | if (new.y <= old.y) return;
84 |
85 | // 当底部刷新控件完全出现时,才刷新
86 | [self beginRefreshing];
87 | }
88 | }
89 | }
90 |
91 | - (void)scrollViewPanStateDidChange:(NSDictionary *)change
92 | {
93 | [super scrollViewPanStateDidChange:change];
94 |
95 | if (self.state != MJRefreshStateIdle) return;
96 |
97 | UIGestureRecognizerState panState = _scrollView.panGestureRecognizer.state;
98 | if (panState == UIGestureRecognizerStateEnded) {// 手松开
99 | if (_scrollView.mj_insetT + _scrollView.mj_contentH <= _scrollView.mj_h) { // 不够一个屏幕
100 | if (_scrollView.mj_offsetY >= - _scrollView.mj_insetT) { // 向上拽
101 | [self beginRefreshing];
102 | }
103 | } else { // 超出一个屏幕
104 | if (_scrollView.mj_offsetY >= _scrollView.mj_contentH + _scrollView.mj_insetB - _scrollView.mj_h) {
105 | [self beginRefreshing];
106 | }
107 | }
108 | } else if (panState == UIGestureRecognizerStateBegan) {
109 | self.oneNewPan = YES;
110 | }
111 | }
112 |
113 | - (void)beginRefreshing
114 | {
115 | if (!self.isOneNewPan && self.isOnlyRefreshPerDrag) return;
116 |
117 | [super beginRefreshing];
118 |
119 | self.oneNewPan = NO;
120 | }
121 |
122 | - (void)setState:(MJRefreshState)state
123 | {
124 | MJRefreshCheckState
125 |
126 | if (state == MJRefreshStateRefreshing) {
127 | [self executeRefreshingCallback];
128 | } else if (state == MJRefreshStateNoMoreData || state == MJRefreshStateIdle) {
129 | if (MJRefreshStateRefreshing == oldState) {
130 | if (self.endRefreshingCompletionBlock) {
131 | self.endRefreshingCompletionBlock();
132 | }
133 | }
134 | }
135 | }
136 |
137 | - (void)setHidden:(BOOL)hidden
138 | {
139 | BOOL lastHidden = self.isHidden;
140 |
141 | [super setHidden:hidden];
142 |
143 | if (!lastHidden && hidden) {
144 | self.state = MJRefreshStateIdle;
145 |
146 | self.scrollView.mj_insetB -= self.mj_h;
147 | } else if (lastHidden && !hidden) {
148 | self.scrollView.mj_insetB += self.mj_h;
149 |
150 | // 设置位置
151 | self.mj_y = _scrollView.mj_contentH;
152 | }
153 | }
154 | @end
155 |
--------------------------------------------------------------------------------
/ios/MJRefresh/Base/MJRefreshBackFooter.h:
--------------------------------------------------------------------------------
1 | //
2 | // MJRefreshBackFooter.h
3 | // MJRefreshExample
4 | //
5 | // Created by MJ Lee on 15/4/24.
6 | // Copyright (c) 2015年 小码哥. All rights reserved.
7 | //
8 |
9 | #import "MJRefreshFooter.h"
10 |
11 | @interface MJRefreshBackFooter : MJRefreshFooter
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/ios/MJRefresh/Base/MJRefreshBackFooter.m:
--------------------------------------------------------------------------------
1 | //
2 | // MJRefreshBackFooter.m
3 | // MJRefreshExample
4 | //
5 | // Created by MJ Lee on 15/4/24.
6 | // Copyright (c) 2015年 小码哥. All rights reserved.
7 | //
8 |
9 | #import "MJRefreshBackFooter.h"
10 |
11 | @interface MJRefreshBackFooter()
12 | @property (assign, nonatomic) NSInteger lastRefreshCount;
13 | @property (assign, nonatomic) CGFloat lastBottomDelta;
14 | @end
15 |
16 | @implementation MJRefreshBackFooter
17 |
18 | #pragma mark - 初始化
19 | - (void)willMoveToSuperview:(UIView *)newSuperview
20 | {
21 | [super willMoveToSuperview:newSuperview];
22 |
23 | [self scrollViewContentSizeDidChange:nil];
24 | }
25 |
26 | #pragma mark - 实现父类的方法
27 | - (void)scrollViewContentOffsetDidChange:(NSDictionary *)change
28 | {
29 | [super scrollViewContentOffsetDidChange:change];
30 |
31 | // 如果正在刷新,直接返回
32 | if (self.state == MJRefreshStateRefreshing) return;
33 |
34 | _scrollViewOriginalInset = self.scrollView.mj_inset;
35 |
36 | // 当前的contentOffset
37 | CGFloat currentOffsetY = self.scrollView.mj_offsetY;
38 | // 尾部控件刚好出现的offsetY
39 | CGFloat happenOffsetY = [self happenOffsetY];
40 | // 如果是向下滚动到看不见尾部控件,直接返回
41 | if (currentOffsetY <= happenOffsetY) return;
42 |
43 | CGFloat pullingPercent = (currentOffsetY - happenOffsetY) / self.mj_h;
44 |
45 | // 如果已全部加载,仅设置pullingPercent,然后返回
46 | if (self.state == MJRefreshStateNoMoreData) {
47 | self.pullingPercent = pullingPercent;
48 | return;
49 | }
50 |
51 | if (self.scrollView.isDragging) {
52 | self.pullingPercent = pullingPercent;
53 | // 普通 和 即将刷新 的临界点
54 | CGFloat normal2pullingOffsetY = happenOffsetY + self.mj_h;
55 |
56 | if (self.state == MJRefreshStateIdle && currentOffsetY > normal2pullingOffsetY) {
57 | // 转为即将刷新状态
58 | self.state = MJRefreshStatePulling;
59 | } else if (self.state == MJRefreshStatePulling && currentOffsetY <= normal2pullingOffsetY) {
60 | // 转为普通状态
61 | self.state = MJRefreshStateIdle;
62 | }
63 | } else if (self.state == MJRefreshStatePulling) {// 即将刷新 && 手松开
64 | // 开始刷新
65 | [self beginRefreshing];
66 | } else if (pullingPercent < 1) {
67 | self.pullingPercent = pullingPercent;
68 | }
69 | }
70 |
71 | - (void)scrollViewContentSizeDidChange:(NSDictionary *)change
72 | {
73 | [super scrollViewContentSizeDidChange:change];
74 |
75 | // 内容的高度
76 | CGFloat contentHeight = self.scrollView.mj_contentH + self.ignoredScrollViewContentInsetBottom;
77 | // 表格的高度
78 | CGFloat scrollHeight = self.scrollView.mj_h - self.scrollViewOriginalInset.top - self.scrollViewOriginalInset.bottom + self.ignoredScrollViewContentInsetBottom;
79 | // 设置位置和尺寸
80 | self.mj_y = MAX(contentHeight, scrollHeight);
81 | }
82 |
83 | - (void)setState:(MJRefreshState)state
84 | {
85 | MJRefreshCheckState
86 |
87 | // 根据状态来设置属性
88 | if (state == MJRefreshStateNoMoreData || state == MJRefreshStateIdle) {
89 | // 刷新完毕
90 | if (MJRefreshStateRefreshing == oldState) {
91 | [UIView animateWithDuration:MJRefreshSlowAnimationDuration animations:^{
92 | self.scrollView.mj_insetB -= self.lastBottomDelta;
93 |
94 | // 自动调整透明度
95 | if (self.isAutomaticallyChangeAlpha) self.alpha = 0.0;
96 | } completion:^(BOOL finished) {
97 | self.pullingPercent = 0.0;
98 |
99 | if (self.endRefreshingCompletionBlock) {
100 | self.endRefreshingCompletionBlock();
101 | }
102 | }];
103 | }
104 |
105 | CGFloat deltaH = [self heightForContentBreakView];
106 | // 刚刷新完毕
107 | if (MJRefreshStateRefreshing == oldState && deltaH > 0 && self.scrollView.mj_totalDataCount != self.lastRefreshCount) {
108 | self.scrollView.mj_offsetY = self.scrollView.mj_offsetY;
109 | }
110 | } else if (state == MJRefreshStateRefreshing) {
111 | // 记录刷新前的数量
112 | self.lastRefreshCount = self.scrollView.mj_totalDataCount;
113 |
114 | [UIView animateWithDuration:MJRefreshFastAnimationDuration animations:^{
115 | CGFloat bottom = self.mj_h + self.scrollViewOriginalInset.bottom;
116 | CGFloat deltaH = [self heightForContentBreakView];
117 | if (deltaH < 0) { // 如果内容高度小于view的高度
118 | bottom -= deltaH;
119 | }
120 | self.lastBottomDelta = bottom - self.scrollView.mj_insetB;
121 | self.scrollView.mj_insetB = bottom;
122 | self.scrollView.mj_offsetY = [self happenOffsetY] + self.mj_h;
123 | } completion:^(BOOL finished) {
124 | [self executeRefreshingCallback];
125 | }];
126 | }
127 | }
128 | #pragma mark - 私有方法
129 | #pragma mark 获得scrollView的内容 超出 view 的高度
130 | - (CGFloat)heightForContentBreakView
131 | {
132 | CGFloat h = self.scrollView.frame.size.height - self.scrollViewOriginalInset.bottom - self.scrollViewOriginalInset.top;
133 | return self.scrollView.contentSize.height - h;
134 | }
135 |
136 | #pragma mark 刚好看到上拉刷新控件时的contentOffset.y
137 | - (CGFloat)happenOffsetY
138 | {
139 | CGFloat deltaH = [self heightForContentBreakView];
140 | if (deltaH > 0) {
141 | return deltaH - self.scrollViewOriginalInset.top;
142 | } else {
143 | return - self.scrollViewOriginalInset.top;
144 | }
145 | }
146 | @end
147 |
--------------------------------------------------------------------------------
/ios/MJRefresh/Base/MJRefreshComponent.h:
--------------------------------------------------------------------------------
1 | // 代码地址: https://github.com/CoderMJLee/MJRefresh
2 | // 代码地址: http://code4app.com/ios/%E5%BF%AB%E9%80%9F%E9%9B%86%E6%88%90%E4%B8%8B%E6%8B%89%E4%B8%8A%E6%8B%89%E5%88%B7%E6%96%B0/52326ce26803fabc46000000
3 | // MJRefreshComponent.h
4 | // MJRefreshExample
5 | //
6 | // Created by MJ Lee on 15/3/4.
7 | // Copyright (c) 2015年 小码哥. All rights reserved.
8 | // 刷新控件的基类
9 |
10 | #import
11 | #import "MJRefreshConst.h"
12 | #import "UIView+MJExtension.h"
13 | #import "UIScrollView+MJExtension.h"
14 | #import "UIScrollView+MJRefresh.h"
15 | #import "NSBundle+MJRefresh.h"
16 | #import
17 | #import
18 |
19 | /** 刷新控件的状态 */
20 | typedef NS_ENUM(NSInteger, MJRefreshState) {
21 | /** 普通闲置状态 */
22 | MJRefreshStateIdle = 1,
23 | /** 松开就可以进行刷新的状态 */
24 | MJRefreshStatePulling,
25 | /** 正在刷新中的状态 */
26 | MJRefreshStateRefreshing,
27 | /** 即将刷新的状态 */
28 | MJRefreshStateWillRefresh,
29 | /** 所有数据加载完毕,没有更多的数据了 */
30 | MJRefreshStateNoMoreData
31 | };
32 |
33 | /** 进入刷新状态的回调 */
34 | typedef void (^MJRefreshComponentRefreshingBlock)(void);
35 | /** 开始刷新后的回调(进入刷新状态后的回调) */
36 | typedef void (^MJRefreshComponentbeginRefreshingCompletionBlock)(void);
37 | /** 结束刷新后的回调 */
38 | typedef void (^MJRefreshComponentEndRefreshingCompletionBlock)(void);
39 |
40 | /** 刷新控件的基类 */
41 | @interface MJRefreshComponent : UIView
42 | {
43 | /** 记录scrollView刚开始的inset */
44 | UIEdgeInsets _scrollViewOriginalInset;
45 | /** 父控件 */
46 | __weak UIScrollView *_scrollView;
47 | }
48 | #pragma mark - 刷新回调
49 | /** 正在刷新的回调 */
50 | @property (copy, nonatomic) MJRefreshComponentRefreshingBlock refreshingBlock;
51 | /** 设置回调对象和回调方法 */
52 | - (void)setRefreshingTarget:(id)target refreshingAction:(SEL)action;
53 |
54 | /** 回调对象 */
55 | @property (weak, nonatomic) id refreshingTarget;
56 | /** 回调方法 */
57 | @property (assign, nonatomic) SEL refreshingAction;
58 | /** 触发回调(交给子类去调用) */
59 | - (void)executeRefreshingCallback;
60 |
61 | #pragma mark - 刷新状态控制
62 | /** 进入刷新状态 */
63 | - (void)beginRefreshing;
64 | - (void)beginRefreshingWithCompletionBlock:(void (^)(void))completionBlock;
65 | /** 开始刷新后的回调(进入刷新状态后的回调) */
66 | @property (copy, nonatomic) MJRefreshComponentbeginRefreshingCompletionBlock beginRefreshingCompletionBlock;
67 | /** 结束刷新的回调 */
68 | @property (copy, nonatomic) MJRefreshComponentEndRefreshingCompletionBlock endRefreshingCompletionBlock;
69 | /** 结束刷新状态 */
70 | - (void)endRefreshing;
71 | - (void)endRefreshingWithCompletionBlock:(void (^)(void))completionBlock;
72 | /** 是否正在刷新 */
73 | @property (assign, nonatomic, readonly, getter=isRefreshing) BOOL refreshing;
74 | //- (BOOL)isRefreshing;
75 | /** 刷新状态 一般交给子类内部实现 */
76 | @property (assign, nonatomic) MJRefreshState state;
77 |
78 | #pragma mark - 交给子类去访问
79 | /** 记录scrollView刚开始的inset */
80 | @property (assign, nonatomic, readonly) UIEdgeInsets scrollViewOriginalInset;
81 | /** 父控件 */
82 | @property (weak, nonatomic, readonly) UIScrollView *scrollView;
83 |
84 | #pragma mark - 交给子类们去实现
85 | /** 初始化 */
86 | - (void)prepare NS_REQUIRES_SUPER;
87 | /** 摆放子控件frame */
88 | - (void)placeSubviews NS_REQUIRES_SUPER;
89 | /** 当scrollView的contentOffset发生改变的时候调用 */
90 | - (void)scrollViewContentOffsetDidChange:(NSDictionary *)change NS_REQUIRES_SUPER;
91 | /** 当scrollView的contentSize发生改变的时候调用 */
92 | - (void)scrollViewContentSizeDidChange:(NSDictionary *)change NS_REQUIRES_SUPER;
93 | /** 当scrollView的拖拽状态发生改变的时候调用 */
94 | - (void)scrollViewPanStateDidChange:(NSDictionary *)change NS_REQUIRES_SUPER;
95 |
96 |
97 | #pragma mark - 其他
98 | /** 拉拽的百分比(交给子类重写) */
99 | @property (assign, nonatomic) CGFloat pullingPercent;
100 | /** 根据拖拽比例自动切换透明度 */
101 | @property (assign, nonatomic, getter=isAutoChangeAlpha) BOOL autoChangeAlpha MJRefreshDeprecated("请使用automaticallyChangeAlpha属性");
102 | /** 根据拖拽比例自动切换透明度 */
103 | @property (assign, nonatomic, getter=isAutomaticallyChangeAlpha) BOOL automaticallyChangeAlpha;
104 | @end
105 |
106 | @interface UILabel(MJRefresh)
107 | + (instancetype)mj_label;
108 | - (CGFloat)mj_textWith;
109 | @end
110 |
--------------------------------------------------------------------------------
/ios/MJRefresh/Base/MJRefreshComponent.m:
--------------------------------------------------------------------------------
1 | // 代码地址: https://github.com/CoderMJLee/MJRefresh
2 | // 代码地址: http://code4app.com/ios/%E5%BF%AB%E9%80%9F%E9%9B%86%E6%88%90%E4%B8%8B%E6%8B%89%E4%B8%8A%E6%8B%89%E5%88%B7%E6%96%B0/52326ce26803fabc46000000
3 | // MJRefreshComponent.m
4 | // MJRefreshExample
5 | //
6 | // Created by MJ Lee on 15/3/4.
7 | // Copyright (c) 2015年 小码哥. All rights reserved.
8 | //
9 |
10 | #import "MJRefreshComponent.h"
11 | #import "MJRefreshConst.h"
12 |
13 | @interface MJRefreshComponent()
14 | @property (strong, nonatomic) UIPanGestureRecognizer *pan;
15 | @end
16 |
17 | @implementation MJRefreshComponent
18 | #pragma mark - 初始化
19 | - (instancetype)initWithFrame:(CGRect)frame
20 | {
21 | if (self = [super initWithFrame:frame]) {
22 | // 准备工作
23 | [self prepare];
24 |
25 | // 默认是普通状态
26 | self.state = MJRefreshStateIdle;
27 | }
28 | return self;
29 | }
30 |
31 | - (void)prepare
32 | {
33 | // 基本属性
34 | self.autoresizingMask = UIViewAutoresizingFlexibleWidth;
35 | self.backgroundColor = [UIColor clearColor];
36 | }
37 |
38 | - (void)layoutSubviews
39 | {
40 | [self placeSubviews];
41 |
42 | [super layoutSubviews];
43 | }
44 |
45 | - (void)placeSubviews{}
46 |
47 | - (void)willMoveToSuperview:(UIView *)newSuperview
48 | {
49 | [super willMoveToSuperview:newSuperview];
50 |
51 | // 如果不是UIScrollView,不做任何事情
52 | if (newSuperview && ![newSuperview isKindOfClass:[UIScrollView class]]) return;
53 |
54 | // 旧的父控件移除监听
55 | [self removeObservers];
56 |
57 | if (newSuperview) { // 新的父控件
58 | // 设置宽度
59 | self.mj_w = newSuperview.mj_w;
60 | // 设置位置
61 | self.mj_x = -_scrollView.mj_insetL;
62 |
63 | // 记录UIScrollView
64 | _scrollView = (UIScrollView *)newSuperview;
65 | // 设置永远支持垂直弹簧效果
66 | _scrollView.alwaysBounceVertical = YES;
67 | // 记录UIScrollView最开始的contentInset
68 | _scrollViewOriginalInset = _scrollView.mj_inset;
69 |
70 | // 添加监听
71 | [self addObservers];
72 | }
73 | }
74 |
75 | - (void)drawRect:(CGRect)rect
76 | {
77 | [super drawRect:rect];
78 |
79 | if (self.state == MJRefreshStateWillRefresh) {
80 | // 预防view还没显示出来就调用了beginRefreshing
81 | self.state = MJRefreshStateRefreshing;
82 | }
83 | }
84 |
85 | #pragma mark - KVO监听
86 | - (void)addObservers
87 | {
88 | NSKeyValueObservingOptions options = NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld;
89 | [self.scrollView addObserver:self forKeyPath:MJRefreshKeyPathContentOffset options:options context:nil];
90 | [self.scrollView addObserver:self forKeyPath:MJRefreshKeyPathContentSize options:options context:nil];
91 | self.pan = self.scrollView.panGestureRecognizer;
92 | [self.pan addObserver:self forKeyPath:MJRefreshKeyPathPanState options:options context:nil];
93 | }
94 |
95 | - (void)removeObservers
96 | {
97 | [self.superview removeObserver:self forKeyPath:MJRefreshKeyPathContentOffset];
98 | [self.superview removeObserver:self forKeyPath:MJRefreshKeyPathContentSize];
99 | [self.pan removeObserver:self forKeyPath:MJRefreshKeyPathPanState];
100 | self.pan = nil;
101 | }
102 |
103 | - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
104 | {
105 | // 遇到这些情况就直接返回
106 | if (!self.userInteractionEnabled) return;
107 |
108 | // 这个就算看不见也需要处理
109 | if ([keyPath isEqualToString:MJRefreshKeyPathContentSize]) {
110 | [self scrollViewContentSizeDidChange:change];
111 | }
112 |
113 | // 看不见
114 | if (self.hidden) return;
115 | if ([keyPath isEqualToString:MJRefreshKeyPathContentOffset]) {
116 | [self scrollViewContentOffsetDidChange:change];
117 | } else if ([keyPath isEqualToString:MJRefreshKeyPathPanState]) {
118 | [self scrollViewPanStateDidChange:change];
119 | }
120 | }
121 |
122 | - (void)scrollViewContentOffsetDidChange:(NSDictionary *)change{}
123 | - (void)scrollViewContentSizeDidChange:(NSDictionary *)change{}
124 | - (void)scrollViewPanStateDidChange:(NSDictionary *)change{}
125 |
126 | #pragma mark - 公共方法
127 | #pragma mark 设置回调对象和回调方法
128 | - (void)setRefreshingTarget:(id)target refreshingAction:(SEL)action
129 | {
130 | self.refreshingTarget = target;
131 | self.refreshingAction = action;
132 | }
133 |
134 | - (void)setState:(MJRefreshState)state
135 | {
136 | _state = state;
137 |
138 | // 加入主队列的目的是等setState:方法调用完毕、设置完文字后再去布局子控件
139 | dispatch_async(dispatch_get_main_queue(), ^{
140 | [self setNeedsLayout];
141 | });
142 | }
143 |
144 | #pragma mark 进入刷新状态
145 | - (void)beginRefreshing
146 | {
147 | [UIView animateWithDuration:MJRefreshFastAnimationDuration animations:^{
148 | self.alpha = 1.0;
149 | }];
150 | self.pullingPercent = 1.0;
151 | // 只要正在刷新,就完全显示
152 | if (self.window) {
153 | self.state = MJRefreshStateRefreshing;
154 | } else {
155 | // 预防正在刷新中时,调用本方法使得header inset回置失败
156 | if (self.state != MJRefreshStateRefreshing) {
157 | self.state = MJRefreshStateWillRefresh;
158 | // 刷新(预防从另一个控制器回到这个控制器的情况,回来要重新刷新一下)
159 | [self setNeedsDisplay];
160 | }
161 | }
162 | }
163 |
164 | - (void)beginRefreshingWithCompletionBlock:(void (^)(void))completionBlock
165 | {
166 | self.beginRefreshingCompletionBlock = completionBlock;
167 |
168 | [self beginRefreshing];
169 | }
170 |
171 | #pragma mark 结束刷新状态
172 | - (void)endRefreshing
173 | {
174 | dispatch_async(dispatch_get_main_queue(), ^{
175 | self.state = MJRefreshStateIdle;
176 | });
177 | }
178 |
179 | - (void)endRefreshingWithCompletionBlock:(void (^)(void))completionBlock
180 | {
181 | self.endRefreshingCompletionBlock = completionBlock;
182 |
183 | [self endRefreshing];
184 | }
185 |
186 | #pragma mark 是否正在刷新
187 | - (BOOL)isRefreshing
188 | {
189 | return self.state == MJRefreshStateRefreshing || self.state == MJRefreshStateWillRefresh;
190 | }
191 |
192 | #pragma mark 自动切换透明度
193 | - (void)setAutoChangeAlpha:(BOOL)autoChangeAlpha
194 | {
195 | self.automaticallyChangeAlpha = autoChangeAlpha;
196 | }
197 |
198 | - (BOOL)isAutoChangeAlpha
199 | {
200 | return self.isAutomaticallyChangeAlpha;
201 | }
202 |
203 | - (void)setAutomaticallyChangeAlpha:(BOOL)automaticallyChangeAlpha
204 | {
205 | _automaticallyChangeAlpha = automaticallyChangeAlpha;
206 |
207 | if (self.isRefreshing) return;
208 |
209 | if (automaticallyChangeAlpha) {
210 | self.alpha = self.pullingPercent;
211 | } else {
212 | self.alpha = 1.0;
213 | }
214 | }
215 |
216 | #pragma mark 根据拖拽进度设置透明度
217 | - (void)setPullingPercent:(CGFloat)pullingPercent
218 | {
219 | _pullingPercent = pullingPercent;
220 |
221 | if (self.isRefreshing) return;
222 |
223 | if (self.isAutomaticallyChangeAlpha) {
224 | self.alpha = pullingPercent;
225 | }
226 | }
227 |
228 | #pragma mark - 内部方法
229 | - (void)executeRefreshingCallback
230 | {
231 | dispatch_async(dispatch_get_main_queue(), ^{
232 | if (self.refreshingBlock) {
233 | self.refreshingBlock();
234 | }
235 | if ([self.refreshingTarget respondsToSelector:self.refreshingAction]) {
236 | MJRefreshMsgSend(MJRefreshMsgTarget(self.refreshingTarget), self.refreshingAction, self);
237 | }
238 | if (self.beginRefreshingCompletionBlock) {
239 | self.beginRefreshingCompletionBlock();
240 | }
241 | });
242 | }
243 | @end
244 |
245 | @implementation UILabel(MJRefresh)
246 | + (instancetype)mj_label
247 | {
248 | UILabel *label = [[self alloc] init];
249 | label.font = MJRefreshLabelFont;
250 | label.textColor = MJRefreshLabelTextColor;
251 | label.autoresizingMask = UIViewAutoresizingFlexibleWidth;
252 | label.textAlignment = NSTextAlignmentCenter;
253 | label.backgroundColor = [UIColor clearColor];
254 | return label;
255 | }
256 |
257 | - (CGFloat)mj_textWith {
258 | CGFloat stringWidth = 0;
259 | CGSize size = CGSizeMake(MAXFLOAT, MAXFLOAT);
260 | if (self.text.length > 0) {
261 | #if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000
262 | stringWidth =[self.text
263 | boundingRectWithSize:size
264 | options:NSStringDrawingUsesLineFragmentOrigin
265 | attributes:@{NSFontAttributeName:self.font}
266 | context:nil].size.width;
267 | #else
268 |
269 | stringWidth = [self.text sizeWithFont:self.font
270 | constrainedToSize:size
271 | lineBreakMode:NSLineBreakByCharWrapping].width;
272 | #endif
273 | }
274 | return stringWidth;
275 | }
276 | @end
277 |
--------------------------------------------------------------------------------
/ios/MJRefresh/Base/MJRefreshFooter.h:
--------------------------------------------------------------------------------
1 | // 代码地址: https://github.com/CoderMJLee/MJRefresh
2 | // 代码地址: http://code4app.com/ios/%E5%BF%AB%E9%80%9F%E9%9B%86%E6%88%90%E4%B8%8B%E6%8B%89%E4%B8%8A%E6%8B%89%E5%88%B7%E6%96%B0/52326ce26803fabc46000000
3 | // MJRefreshFooter.h
4 | // MJRefreshExample
5 | //
6 | // Created by MJ Lee on 15/3/5.
7 | // Copyright (c) 2015年 小码哥. All rights reserved.
8 | // 上拉刷新控件
9 |
10 | #import "MJRefreshComponent.h"
11 |
12 | @interface MJRefreshFooter : MJRefreshComponent
13 | /** 创建footer */
14 | + (instancetype)footerWithRefreshingBlock:(MJRefreshComponentRefreshingBlock)refreshingBlock;
15 | /** 创建footer */
16 | + (instancetype)footerWithRefreshingTarget:(id)target refreshingAction:(SEL)action;
17 |
18 | /** 提示没有更多的数据 */
19 | - (void)endRefreshingWithNoMoreData;
20 | - (void)noticeNoMoreData MJRefreshDeprecated("使用endRefreshingWithNoMoreData");
21 |
22 | /** 重置没有更多的数据(消除没有更多数据的状态) */
23 | - (void)resetNoMoreData;
24 |
25 | /** 忽略多少scrollView的contentInset的bottom */
26 | @property (assign, nonatomic) CGFloat ignoredScrollViewContentInsetBottom;
27 |
28 | /** 自动根据有无数据来显示和隐藏(有数据就显示,没有数据隐藏。默认是NO) */
29 | @property (assign, nonatomic, getter=isAutomaticallyHidden) BOOL automaticallyHidden MJRefreshDeprecated("不建议使用此属性,开发者请自行控制footer的显示和隐藏。基于安全考虑,在未来的某些版本此属性可能作废");
30 | @end
31 |
--------------------------------------------------------------------------------
/ios/MJRefresh/Base/MJRefreshFooter.m:
--------------------------------------------------------------------------------
1 | // 代码地址: https://github.com/CoderMJLee/MJRefresh
2 | // 代码地址: http://code4app.com/ios/%E5%BF%AB%E9%80%9F%E9%9B%86%E6%88%90%E4%B8%8B%E6%8B%89%E4%B8%8A%E6%8B%89%E5%88%B7%E6%96%B0/52326ce26803fabc46000000
3 | // MJRefreshFooter.m
4 | // MJRefreshExample
5 | //
6 | // Created by MJ Lee on 15/3/5.
7 | // Copyright (c) 2015年 小码哥. All rights reserved.
8 | //
9 |
10 | #import "MJRefreshFooter.h"
11 | #include "UIScrollView+MJRefresh.h"
12 |
13 | @interface MJRefreshFooter()
14 |
15 | @end
16 |
17 | @implementation MJRefreshFooter
18 | #pragma mark - 构造方法
19 | + (instancetype)footerWithRefreshingBlock:(MJRefreshComponentRefreshingBlock)refreshingBlock
20 | {
21 | MJRefreshFooter *cmp = [[self alloc] init];
22 | cmp.refreshingBlock = refreshingBlock;
23 | return cmp;
24 | }
25 | + (instancetype)footerWithRefreshingTarget:(id)target refreshingAction:(SEL)action
26 | {
27 | MJRefreshFooter *cmp = [[self alloc] init];
28 | [cmp setRefreshingTarget:target refreshingAction:action];
29 | return cmp;
30 | }
31 |
32 | #pragma mark - 重写父类的方法
33 | - (void)prepare
34 | {
35 | [super prepare];
36 |
37 | // 设置自己的高度
38 | self.mj_h = MJRefreshFooterHeight;
39 |
40 | // 默认不会自动隐藏
41 | self.automaticallyHidden = NO;
42 | }
43 |
44 | - (void)willMoveToSuperview:(UIView *)newSuperview
45 | {
46 | [super willMoveToSuperview:newSuperview];
47 |
48 | if (newSuperview) {
49 | // 监听scrollView数据的变化
50 | if ([self.scrollView isKindOfClass:[UITableView class]] || [self.scrollView isKindOfClass:[UICollectionView class]]) {
51 | [self.scrollView setMj_reloadDataBlock:^(NSInteger totalDataCount) {
52 | if (self.isAutomaticallyHidden) {
53 | self.hidden = (totalDataCount == 0);
54 | }
55 | }];
56 | }
57 | }
58 | }
59 |
60 | #pragma mark - 公共方法
61 | - (void)endRefreshingWithNoMoreData
62 | {
63 | dispatch_async(dispatch_get_main_queue(), ^{
64 | self.state = MJRefreshStateNoMoreData;
65 | });
66 | }
67 |
68 | - (void)noticeNoMoreData
69 | {
70 | [self endRefreshingWithNoMoreData];
71 | }
72 |
73 | - (void)resetNoMoreData
74 | {
75 | dispatch_async(dispatch_get_main_queue(), ^{
76 | self.state = MJRefreshStateIdle;
77 | });
78 | }
79 |
80 | - (void)setAutomaticallyHidden:(BOOL)automaticallyHidden
81 | {
82 | _automaticallyHidden = automaticallyHidden;
83 | }
84 | @end
85 |
--------------------------------------------------------------------------------
/ios/MJRefresh/Base/MJRefreshHeader.h:
--------------------------------------------------------------------------------
1 | // 代码地址: https://github.com/CoderMJLee/MJRefresh
2 | // 代码地址: http://code4app.com/ios/%E5%BF%AB%E9%80%9F%E9%9B%86%E6%88%90%E4%B8%8B%E6%8B%89%E4%B8%8A%E6%8B%89%E5%88%B7%E6%96%B0/52326ce26803fabc46000000
3 | // MJRefreshHeader.h
4 | // MJRefreshExample
5 | //
6 | // Created by MJ Lee on 15/3/4.
7 | // Copyright (c) 2015年 小码哥. All rights reserved.
8 | // 下拉刷新控件:负责监控用户下拉的状态
9 |
10 | #import "MJRefreshComponent.h"
11 |
12 | @interface MJRefreshHeader : MJRefreshComponent
13 | /** 创建header */
14 | + (instancetype)headerWithRefreshingBlock:(MJRefreshComponentRefreshingBlock)refreshingBlock;
15 | /** 创建header */
16 | + (instancetype)headerWithRefreshingTarget:(id)target refreshingAction:(SEL)action;
17 |
18 | /** 这个key用来存储上一次下拉刷新成功的时间 */
19 | @property (copy, nonatomic) NSString *lastUpdatedTimeKey;
20 | /** 上一次下拉刷新成功的时间 */
21 | @property (strong, nonatomic, readonly) NSDate *lastUpdatedTime;
22 |
23 | /** 忽略多少scrollView的contentInset的top */
24 | @property (assign, nonatomic) CGFloat ignoredScrollViewContentInsetTop;
25 | @end
26 |
--------------------------------------------------------------------------------
/ios/MJRefresh/Base/MJRefreshHeader.m:
--------------------------------------------------------------------------------
1 | // 代码地址: https://github.com/CoderMJLee/MJRefresh
2 | // 代码地址: http://code4app.com/ios/%E5%BF%AB%E9%80%9F%E9%9B%86%E6%88%90%E4%B8%8B%E6%8B%89%E4%B8%8A%E6%8B%89%E5%88%B7%E6%96%B0/52326ce26803fabc46000000
3 | // MJRefreshHeader.m
4 | // MJRefreshExample
5 | //
6 | // Created by MJ Lee on 15/3/4.
7 | // Copyright (c) 2015年 小码哥. All rights reserved.
8 | //
9 |
10 | #import "MJRefreshHeader.h"
11 |
12 | @interface MJRefreshHeader()
13 | @property (assign, nonatomic) CGFloat insetTDelta;
14 | @end
15 |
16 | @implementation MJRefreshHeader
17 | #pragma mark - 构造方法
18 | + (instancetype)headerWithRefreshingBlock:(MJRefreshComponentRefreshingBlock)refreshingBlock
19 | {
20 | MJRefreshHeader *cmp = [[self alloc] init];
21 | cmp.refreshingBlock = refreshingBlock;
22 | return cmp;
23 | }
24 | + (instancetype)headerWithRefreshingTarget:(id)target refreshingAction:(SEL)action
25 | {
26 | MJRefreshHeader *cmp = [[self alloc] init];
27 | [cmp setRefreshingTarget:target refreshingAction:action];
28 | return cmp;
29 | }
30 |
31 | #pragma mark - 覆盖父类的方法
32 | - (void)prepare
33 | {
34 | [super prepare];
35 |
36 | // 设置key
37 | self.lastUpdatedTimeKey = MJRefreshHeaderLastUpdatedTimeKey;
38 |
39 | // 设置高度
40 | self.mj_h = MJRefreshHeaderHeight;
41 | }
42 |
43 | - (void)placeSubviews
44 | {
45 | [super placeSubviews];
46 |
47 | // 设置y值(当自己的高度发生改变了,肯定要重新调整Y值,所以放到placeSubviews方法中设置y值)
48 | self.mj_y = - self.mj_h- self.ignoredScrollViewContentInsetTop;
49 | }
50 |
51 | - (void)scrollViewContentOffsetDidChange:(NSDictionary *)change
52 | {
53 | [super scrollViewContentOffsetDidChange:change];
54 |
55 | // 在刷新的refreshing状态
56 | if (self.state == MJRefreshStateRefreshing) {
57 | // 暂时保留
58 | if (self.window == nil) return;
59 |
60 | // sectionheader停留解决
61 | CGFloat insetT = - self.scrollView.mj_offsetY > _scrollViewOriginalInset.top ? - self.scrollView.mj_offsetY : _scrollViewOriginalInset.top;
62 | insetT = insetT > self.mj_h + _scrollViewOriginalInset.top ? self.mj_h + _scrollViewOriginalInset.top : insetT;
63 | self.scrollView.mj_insetT = insetT;
64 |
65 | self.insetTDelta = _scrollViewOriginalInset.top - insetT;
66 | return;
67 | }
68 |
69 | // 跳转到下一个控制器时,contentInset可能会变
70 | _scrollViewOriginalInset = self.scrollView.mj_inset;
71 |
72 | // 当前的contentOffset
73 | CGFloat offsetY = self.scrollView.mj_offsetY;
74 | // 头部控件刚好出现的offsetY
75 | CGFloat happenOffsetY = - self.scrollViewOriginalInset.top;
76 |
77 | // 如果是向上滚动到看不见头部控件,直接返回
78 | // >= -> >
79 | if (offsetY > happenOffsetY) return;
80 |
81 | // 普通 和 即将刷新 的临界点
82 | CGFloat normal2pullingOffsetY = happenOffsetY - self.mj_h;
83 | CGFloat pullingPercent = (happenOffsetY - offsetY) / self.mj_h;
84 |
85 | if (self.scrollView.isDragging) { // 如果正在拖拽
86 | self.pullingPercent = pullingPercent;
87 | if (self.state == MJRefreshStateIdle && offsetY < normal2pullingOffsetY) {
88 | // 转为即将刷新状态
89 | self.state = MJRefreshStatePulling;
90 | } else if (self.state == MJRefreshStatePulling && offsetY >= normal2pullingOffsetY) {
91 | // 转为普通状态
92 | self.state = MJRefreshStateIdle;
93 | }
94 | } else if (self.state == MJRefreshStatePulling) {// 即将刷新 && 手松开
95 | // 开始刷新
96 | [self beginRefreshing];
97 | } else if (pullingPercent < 1) {
98 | self.pullingPercent = pullingPercent;
99 | }
100 | }
101 |
102 | - (void)setState:(MJRefreshState)state
103 | {
104 | MJRefreshCheckState
105 |
106 | // 根据状态做事情
107 | if (state == MJRefreshStateIdle) {
108 | if (oldState != MJRefreshStateRefreshing) return;
109 |
110 | // 保存刷新时间
111 | [[NSUserDefaults standardUserDefaults] setObject:[NSDate date] forKey:self.lastUpdatedTimeKey];
112 | [[NSUserDefaults standardUserDefaults] synchronize];
113 |
114 | // 恢复inset和offset
115 | [UIView animateWithDuration:MJRefreshSlowAnimationDuration animations:^{
116 | self.scrollView.mj_insetT += self.insetTDelta;
117 |
118 | // 自动调整透明度
119 | if (self.isAutomaticallyChangeAlpha) self.alpha = 0.0;
120 | } completion:^(BOOL finished) {
121 | self.pullingPercent = 0.0;
122 |
123 | if (self.endRefreshingCompletionBlock) {
124 | self.endRefreshingCompletionBlock();
125 | }
126 | }];
127 | } else if (state == MJRefreshStateRefreshing) {
128 | dispatch_async(dispatch_get_main_queue(), ^{
129 | [UIView animateWithDuration:MJRefreshFastAnimationDuration animations:^{
130 | CGFloat top = self.scrollViewOriginalInset.top + self.mj_h;
131 | // 增加滚动区域top
132 | self.scrollView.mj_insetT = top;
133 | // 设置滚动位置
134 | CGPoint offset = self.scrollView.contentOffset;
135 | offset.y = -top;
136 | [self.scrollView setContentOffset:offset animated:NO];
137 | } completion:^(BOOL finished) {
138 | [self executeRefreshingCallback];
139 | }];
140 | });
141 | }
142 | }
143 |
144 | #pragma mark - 公共方法
145 | - (NSDate *)lastUpdatedTime
146 | {
147 | return [[NSUserDefaults standardUserDefaults] objectForKey:self.lastUpdatedTimeKey];
148 | }
149 |
150 | - (void)setIgnoredScrollViewContentInsetTop:(CGFloat)ignoredScrollViewContentInsetTop {
151 | _ignoredScrollViewContentInsetTop = ignoredScrollViewContentInsetTop;
152 |
153 | self.mj_y = - self.mj_h - _ignoredScrollViewContentInsetTop;
154 | }
155 |
156 | @end
157 |
--------------------------------------------------------------------------------
/ios/MJRefresh/Custom/Footer/Auto/MJRefreshAutoGifFooter.h:
--------------------------------------------------------------------------------
1 | //
2 | // MJRefreshAutoGifFooter.h
3 | // MJRefreshExample
4 | //
5 | // Created by MJ Lee on 15/4/24.
6 | // Copyright (c) 2015年 小码哥. All rights reserved.
7 | //
8 |
9 | #import "MJRefreshAutoStateFooter.h"
10 |
11 | @interface MJRefreshAutoGifFooter : MJRefreshAutoStateFooter
12 | @property (weak, nonatomic, readonly) UIImageView *gifView;
13 |
14 | /** 设置state状态下的动画图片images 动画持续时间duration*/
15 | - (void)setImages:(NSArray *)images duration:(NSTimeInterval)duration forState:(MJRefreshState)state;
16 | - (void)setImages:(NSArray *)images forState:(MJRefreshState)state;
17 | @end
18 |
--------------------------------------------------------------------------------
/ios/MJRefresh/Custom/Footer/Auto/MJRefreshAutoGifFooter.m:
--------------------------------------------------------------------------------
1 | //
2 | // MJRefreshAutoGifFooter.m
3 | // MJRefreshExample
4 | //
5 | // Created by MJ Lee on 15/4/24.
6 | // Copyright (c) 2015年 小码哥. All rights reserved.
7 | //
8 |
9 | #import "MJRefreshAutoGifFooter.h"
10 |
11 | @interface MJRefreshAutoGifFooter()
12 | {
13 | __unsafe_unretained UIImageView *_gifView;
14 | }
15 | /** 所有状态对应的动画图片 */
16 | @property (strong, nonatomic) NSMutableDictionary *stateImages;
17 | /** 所有状态对应的动画时间 */
18 | @property (strong, nonatomic) NSMutableDictionary *stateDurations;
19 | @end
20 |
21 | @implementation MJRefreshAutoGifFooter
22 | #pragma mark - 懒加载
23 | - (UIImageView *)gifView
24 | {
25 | if (!_gifView) {
26 | UIImageView *gifView = [[UIImageView alloc] init];
27 | [self addSubview:_gifView = gifView];
28 | }
29 | return _gifView;
30 | }
31 |
32 | - (NSMutableDictionary *)stateImages
33 | {
34 | if (!_stateImages) {
35 | self.stateImages = [NSMutableDictionary dictionary];
36 | }
37 | return _stateImages;
38 | }
39 |
40 | - (NSMutableDictionary *)stateDurations
41 | {
42 | if (!_stateDurations) {
43 | self.stateDurations = [NSMutableDictionary dictionary];
44 | }
45 | return _stateDurations;
46 | }
47 |
48 | #pragma mark - 公共方法
49 | - (void)setImages:(NSArray *)images duration:(NSTimeInterval)duration forState:(MJRefreshState)state
50 | {
51 | if (images == nil) return;
52 |
53 | self.stateImages[@(state)] = images;
54 | self.stateDurations[@(state)] = @(duration);
55 |
56 | /* 根据图片设置控件的高度 */
57 | UIImage *image = [images firstObject];
58 | if (image.size.height > self.mj_h) {
59 | self.mj_h = image.size.height;
60 | }
61 | }
62 |
63 | - (void)setImages:(NSArray *)images forState:(MJRefreshState)state
64 | {
65 | [self setImages:images duration:images.count * 0.1 forState:state];
66 | }
67 |
68 | #pragma mark - 实现父类的方法
69 | - (void)prepare
70 | {
71 | [super prepare];
72 |
73 | // 初始化间距
74 | self.labelLeftInset = 20;
75 | }
76 |
77 | - (void)placeSubviews
78 | {
79 | [super placeSubviews];
80 |
81 | if (self.gifView.constraints.count) return;
82 |
83 | self.gifView.frame = self.bounds;
84 | if (self.isRefreshingTitleHidden) {
85 | self.gifView.contentMode = UIViewContentModeCenter;
86 | } else {
87 | self.gifView.contentMode = UIViewContentModeRight;
88 | self.gifView.mj_w = self.mj_w * 0.5 - self.labelLeftInset - self.stateLabel.mj_textWith * 0.5;
89 | }
90 | }
91 |
92 | - (void)setState:(MJRefreshState)state
93 | {
94 | MJRefreshCheckState
95 |
96 | // 根据状态做事情
97 | if (state == MJRefreshStateRefreshing) {
98 | NSArray *images = self.stateImages[@(state)];
99 | if (images.count == 0) return;
100 | [self.gifView stopAnimating];
101 |
102 | self.gifView.hidden = NO;
103 | if (images.count == 1) { // 单张图片
104 | self.gifView.image = [images lastObject];
105 | } else { // 多张图片
106 | self.gifView.animationImages = images;
107 | self.gifView.animationDuration = [self.stateDurations[@(state)] doubleValue];
108 | [self.gifView startAnimating];
109 | }
110 | } else if (state == MJRefreshStateNoMoreData || state == MJRefreshStateIdle) {
111 | [self.gifView stopAnimating];
112 | self.gifView.hidden = YES;
113 | }
114 | }
115 | @end
116 |
117 |
--------------------------------------------------------------------------------
/ios/MJRefresh/Custom/Footer/Auto/MJRefreshAutoNormalFooter.h:
--------------------------------------------------------------------------------
1 | //
2 | // MJRefreshAutoNormalFooter.h
3 | // MJRefreshExample
4 | //
5 | // Created by MJ Lee on 15/4/24.
6 | // Copyright (c) 2015年 小码哥. All rights reserved.
7 | //
8 |
9 | #import "MJRefreshAutoStateFooter.h"
10 |
11 | @interface MJRefreshAutoNormalFooter : MJRefreshAutoStateFooter
12 | /** 菊花的样式 */
13 | @property (assign, nonatomic) UIActivityIndicatorViewStyle activityIndicatorViewStyle;
14 | @end
15 |
--------------------------------------------------------------------------------
/ios/MJRefresh/Custom/Footer/Auto/MJRefreshAutoNormalFooter.m:
--------------------------------------------------------------------------------
1 | //
2 | // MJRefreshAutoNormalFooter.m
3 | // MJRefreshExample
4 | //
5 | // Created by MJ Lee on 15/4/24.
6 | // Copyright (c) 2015年 小码哥. All rights reserved.
7 | //
8 |
9 | #import "MJRefreshAutoNormalFooter.h"
10 |
11 | @interface MJRefreshAutoNormalFooter()
12 | @property (weak, nonatomic) UIActivityIndicatorView *loadingView;
13 | @end
14 |
15 | @implementation MJRefreshAutoNormalFooter
16 | #pragma mark - 懒加载子控件
17 | - (UIActivityIndicatorView *)loadingView
18 | {
19 | if (!_loadingView) {
20 | UIActivityIndicatorView *loadingView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:self.activityIndicatorViewStyle];
21 | loadingView.hidesWhenStopped = YES;
22 | [self addSubview:_loadingView = loadingView];
23 | }
24 | return _loadingView;
25 | }
26 |
27 | - (void)setActivityIndicatorViewStyle:(UIActivityIndicatorViewStyle)activityIndicatorViewStyle
28 | {
29 | _activityIndicatorViewStyle = activityIndicatorViewStyle;
30 |
31 | self.loadingView = nil;
32 | [self setNeedsLayout];
33 | }
34 | #pragma mark - 重写父类的方法
35 | - (void)prepare
36 | {
37 | [super prepare];
38 |
39 | self.activityIndicatorViewStyle = UIActivityIndicatorViewStyleGray;
40 | }
41 |
42 | - (void)placeSubviews
43 | {
44 | [super placeSubviews];
45 |
46 | if (self.loadingView.constraints.count) return;
47 |
48 | // 圈圈
49 | CGFloat loadingCenterX = self.mj_w * 0.5;
50 | if (!self.isRefreshingTitleHidden) {
51 | loadingCenterX -= self.stateLabel.mj_textWith * 0.5 + self.labelLeftInset;
52 | }
53 | CGFloat loadingCenterY = self.mj_h * 0.5;
54 | self.loadingView.center = CGPointMake(loadingCenterX, loadingCenterY);
55 | }
56 |
57 | - (void)setState:(MJRefreshState)state
58 | {
59 | MJRefreshCheckState
60 |
61 | // 根据状态做事情
62 | if (state == MJRefreshStateNoMoreData || state == MJRefreshStateIdle) {
63 | [self.loadingView stopAnimating];
64 | } else if (state == MJRefreshStateRefreshing) {
65 | [self.loadingView startAnimating];
66 | }
67 | }
68 |
69 | @end
70 |
--------------------------------------------------------------------------------
/ios/MJRefresh/Custom/Footer/Auto/MJRefreshAutoStateFooter.h:
--------------------------------------------------------------------------------
1 | //
2 | // MJRefreshAutoStateFooter.h
3 | // MJRefreshExample
4 | //
5 | // Created by MJ Lee on 15/6/13.
6 | // Copyright © 2015年 小码哥. All rights reserved.
7 | //
8 |
9 | #import "MJRefreshAutoFooter.h"
10 |
11 | @interface MJRefreshAutoStateFooter : MJRefreshAutoFooter
12 | /** 文字距离圈圈、箭头的距离 */
13 | @property (assign, nonatomic) CGFloat labelLeftInset;
14 | /** 显示刷新状态的label */
15 | @property (weak, nonatomic, readonly) UILabel *stateLabel;
16 |
17 | /** 设置state状态下的文字 */
18 | - (void)setTitle:(NSString *)title forState:(MJRefreshState)state;
19 |
20 | /** 隐藏刷新状态的文字 */
21 | @property (assign, nonatomic, getter=isRefreshingTitleHidden) BOOL refreshingTitleHidden;
22 | @end
23 |
--------------------------------------------------------------------------------
/ios/MJRefresh/Custom/Footer/Auto/MJRefreshAutoStateFooter.m:
--------------------------------------------------------------------------------
1 | //
2 | // MJRefreshAutoStateFooter.m
3 | // MJRefreshExample
4 | //
5 | // Created by MJ Lee on 15/6/13.
6 | // Copyright © 2015年 小码哥. All rights reserved.
7 | //
8 |
9 | #import "MJRefreshAutoStateFooter.h"
10 |
11 | @interface MJRefreshAutoStateFooter()
12 | {
13 | /** 显示刷新状态的label */
14 | __unsafe_unretained UILabel *_stateLabel;
15 | }
16 | /** 所有状态对应的文字 */
17 | @property (strong, nonatomic) NSMutableDictionary *stateTitles;
18 | @end
19 |
20 | @implementation MJRefreshAutoStateFooter
21 | #pragma mark - 懒加载
22 | - (NSMutableDictionary *)stateTitles
23 | {
24 | if (!_stateTitles) {
25 | self.stateTitles = [NSMutableDictionary dictionary];
26 | }
27 | return _stateTitles;
28 | }
29 |
30 | - (UILabel *)stateLabel
31 | {
32 | if (!_stateLabel) {
33 | [self addSubview:_stateLabel = [UILabel mj_label]];
34 | }
35 | return _stateLabel;
36 | }
37 |
38 | #pragma mark - 公共方法
39 | - (void)setTitle:(NSString *)title forState:(MJRefreshState)state
40 | {
41 | if (title == nil) return;
42 | self.stateTitles[@(state)] = title;
43 | self.stateLabel.text = self.stateTitles[@(self.state)];
44 | }
45 |
46 | #pragma mark - 私有方法
47 | - (void)stateLabelClick
48 | {
49 | if (self.state == MJRefreshStateIdle) {
50 | [self beginRefreshing];
51 | }
52 | }
53 |
54 | #pragma mark - 重写父类的方法
55 | - (void)prepare
56 | {
57 | [super prepare];
58 |
59 | // 初始化间距
60 | self.labelLeftInset = MJRefreshLabelLeftInset;
61 |
62 | // 初始化文字
63 | [self setTitle:[NSBundle mj_localizedStringForKey:MJRefreshAutoFooterIdleText] forState:MJRefreshStateIdle];
64 | [self setTitle:[NSBundle mj_localizedStringForKey:MJRefreshAutoFooterRefreshingText] forState:MJRefreshStateRefreshing];
65 | [self setTitle:[NSBundle mj_localizedStringForKey:MJRefreshAutoFooterNoMoreDataText] forState:MJRefreshStateNoMoreData];
66 |
67 | // 监听label
68 | self.stateLabel.userInteractionEnabled = YES;
69 | [self.stateLabel addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(stateLabelClick)]];
70 | }
71 |
72 | - (void)placeSubviews
73 | {
74 | [super placeSubviews];
75 |
76 | if (self.stateLabel.constraints.count) return;
77 |
78 | // 状态标签
79 | self.stateLabel.frame = self.bounds;
80 | }
81 |
82 | - (void)setState:(MJRefreshState)state
83 | {
84 | MJRefreshCheckState
85 |
86 | if (self.isRefreshingTitleHidden && state == MJRefreshStateRefreshing) {
87 | self.stateLabel.text = nil;
88 | } else {
89 | self.stateLabel.text = self.stateTitles[@(state)];
90 | }
91 | }
92 | @end
--------------------------------------------------------------------------------
/ios/MJRefresh/Custom/Footer/Back/MJRefreshBackGifFooter.h:
--------------------------------------------------------------------------------
1 | //
2 | // MJRefreshBackGifFooter.h
3 | // MJRefreshExample
4 | //
5 | // Created by MJ Lee on 15/4/24.
6 | // Copyright (c) 2015年 小码哥. All rights reserved.
7 | //
8 |
9 | #import "MJRefreshBackStateFooter.h"
10 |
11 | @interface MJRefreshBackGifFooter : MJRefreshBackStateFooter
12 | @property (weak, nonatomic, readonly) UIImageView *gifView;
13 |
14 | /** 设置state状态下的动画图片images 动画持续时间duration*/
15 | - (void)setImages:(NSArray *)images duration:(NSTimeInterval)duration forState:(MJRefreshState)state;
16 | - (void)setImages:(NSArray *)images forState:(MJRefreshState)state;
17 | @end
18 |
--------------------------------------------------------------------------------
/ios/MJRefresh/Custom/Footer/Back/MJRefreshBackGifFooter.m:
--------------------------------------------------------------------------------
1 | //
2 | // MJRefreshBackGifFooter.m
3 | // MJRefreshExample
4 | //
5 | // Created by MJ Lee on 15/4/24.
6 | // Copyright (c) 2015年 小码哥. All rights reserved.
7 | //
8 |
9 | #import "MJRefreshBackGifFooter.h"
10 |
11 | @interface MJRefreshBackGifFooter()
12 | {
13 | __unsafe_unretained UIImageView *_gifView;
14 | }
15 | /** 所有状态对应的动画图片 */
16 | @property (strong, nonatomic) NSMutableDictionary *stateImages;
17 | /** 所有状态对应的动画时间 */
18 | @property (strong, nonatomic) NSMutableDictionary *stateDurations;
19 | @end
20 |
21 | @implementation MJRefreshBackGifFooter
22 | #pragma mark - 懒加载
23 | - (UIImageView *)gifView
24 | {
25 | if (!_gifView) {
26 | UIImageView *gifView = [[UIImageView alloc] init];
27 | [self addSubview:_gifView = gifView];
28 | }
29 | return _gifView;
30 | }
31 |
32 | - (NSMutableDictionary *)stateImages
33 | {
34 | if (!_stateImages) {
35 | self.stateImages = [NSMutableDictionary dictionary];
36 | }
37 | return _stateImages;
38 | }
39 |
40 | - (NSMutableDictionary *)stateDurations
41 | {
42 | if (!_stateDurations) {
43 | self.stateDurations = [NSMutableDictionary dictionary];
44 | }
45 | return _stateDurations;
46 | }
47 |
48 | #pragma mark - 公共方法
49 | - (void)setImages:(NSArray *)images duration:(NSTimeInterval)duration forState:(MJRefreshState)state
50 | {
51 | if (images == nil) return;
52 |
53 | self.stateImages[@(state)] = images;
54 | self.stateDurations[@(state)] = @(duration);
55 |
56 | /* 根据图片设置控件的高度 */
57 | UIImage *image = [images firstObject];
58 | if (image.size.height > self.mj_h) {
59 | self.mj_h = image.size.height;
60 | }
61 | }
62 |
63 | - (void)setImages:(NSArray *)images forState:(MJRefreshState)state
64 | {
65 | [self setImages:images duration:images.count * 0.1 forState:state];
66 | }
67 |
68 | #pragma mark - 实现父类的方法
69 | - (void)prepare
70 | {
71 | [super prepare];
72 |
73 | // 初始化间距
74 | self.labelLeftInset = 20;
75 | }
76 |
77 | - (void)setPullingPercent:(CGFloat)pullingPercent
78 | {
79 | [super setPullingPercent:pullingPercent];
80 | NSArray *images = self.stateImages[@(MJRefreshStateIdle)];
81 | if (self.state != MJRefreshStateIdle || images.count == 0) return;
82 | [self.gifView stopAnimating];
83 | NSUInteger index = images.count * pullingPercent;
84 | if (index >= images.count) index = images.count - 1;
85 | self.gifView.image = images[index];
86 | }
87 |
88 | - (void)placeSubviews
89 | {
90 | [super placeSubviews];
91 |
92 | if (self.gifView.constraints.count) return;
93 |
94 | self.gifView.frame = self.bounds;
95 | if (self.stateLabel.hidden) {
96 | self.gifView.contentMode = UIViewContentModeCenter;
97 | } else {
98 | self.gifView.contentMode = UIViewContentModeRight;
99 | self.gifView.mj_w = self.mj_w * 0.5 - self.labelLeftInset - self.stateLabel.mj_textWith * 0.5;
100 | }
101 | }
102 |
103 | - (void)setState:(MJRefreshState)state
104 | {
105 | MJRefreshCheckState
106 |
107 | // 根据状态做事情
108 | if (state == MJRefreshStatePulling || state == MJRefreshStateRefreshing) {
109 | NSArray *images = self.stateImages[@(state)];
110 | if (images.count == 0) return;
111 |
112 | self.gifView.hidden = NO;
113 | [self.gifView stopAnimating];
114 | if (images.count == 1) { // 单张图片
115 | self.gifView.image = [images lastObject];
116 | } else { // 多张图片
117 | self.gifView.animationImages = images;
118 | self.gifView.animationDuration = [self.stateDurations[@(state)] doubleValue];
119 | [self.gifView startAnimating];
120 | }
121 | } else if (state == MJRefreshStateIdle) {
122 | self.gifView.hidden = NO;
123 | } else if (state == MJRefreshStateNoMoreData) {
124 | self.gifView.hidden = YES;
125 | }
126 | }
127 | @end
128 |
--------------------------------------------------------------------------------
/ios/MJRefresh/Custom/Footer/Back/MJRefreshBackNormalFooter.h:
--------------------------------------------------------------------------------
1 | //
2 | // MJRefreshBackNormalFooter.h
3 | // MJRefreshExample
4 | //
5 | // Created by MJ Lee on 15/4/24.
6 | // Copyright (c) 2015年 小码哥. All rights reserved.
7 | //
8 |
9 | #import "MJRefreshBackStateFooter.h"
10 |
11 | @interface MJRefreshBackNormalFooter : MJRefreshBackStateFooter
12 | @property (weak, nonatomic, readonly) UIImageView *arrowView;
13 | /** 菊花的样式 */
14 | @property (assign, nonatomic) UIActivityIndicatorViewStyle activityIndicatorViewStyle;
15 | @end
16 |
--------------------------------------------------------------------------------
/ios/MJRefresh/Custom/Footer/Back/MJRefreshBackNormalFooter.m:
--------------------------------------------------------------------------------
1 | //
2 | // MJRefreshBackNormalFooter.m
3 | // MJRefreshExample
4 | //
5 | // Created by MJ Lee on 15/4/24.
6 | // Copyright (c) 2015年 小码哥. All rights reserved.
7 | //
8 |
9 | #import "MJRefreshBackNormalFooter.h"
10 | #import "NSBundle+MJRefresh.h"
11 |
12 | @interface MJRefreshBackNormalFooter()
13 | {
14 | __unsafe_unretained UIImageView *_arrowView;
15 | }
16 | @property (weak, nonatomic) UIActivityIndicatorView *loadingView;
17 | @end
18 |
19 | @implementation MJRefreshBackNormalFooter
20 | #pragma mark - 懒加载子控件
21 | - (UIImageView *)arrowView
22 | {
23 | if (!_arrowView) {
24 | UIImageView *arrowView = [[UIImageView alloc] initWithImage:[NSBundle mj_arrowImage]];
25 | [self addSubview:_arrowView = arrowView];
26 | }
27 | return _arrowView;
28 | }
29 |
30 |
31 | - (UIActivityIndicatorView *)loadingView
32 | {
33 | if (!_loadingView) {
34 | UIActivityIndicatorView *loadingView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:self.activityIndicatorViewStyle];
35 | loadingView.hidesWhenStopped = YES;
36 | [self addSubview:_loadingView = loadingView];
37 | }
38 | return _loadingView;
39 | }
40 |
41 | - (void)setActivityIndicatorViewStyle:(UIActivityIndicatorViewStyle)activityIndicatorViewStyle
42 | {
43 | _activityIndicatorViewStyle = activityIndicatorViewStyle;
44 |
45 | self.loadingView = nil;
46 | [self setNeedsLayout];
47 | }
48 | #pragma mark - 重写父类的方法
49 | - (void)prepare
50 | {
51 | [super prepare];
52 |
53 | self.activityIndicatorViewStyle = UIActivityIndicatorViewStyleGray;
54 | }
55 |
56 | - (void)placeSubviews
57 | {
58 | [super placeSubviews];
59 |
60 | // 箭头的中心点
61 | CGFloat arrowCenterX = self.mj_w * 0.5;
62 | if (!self.stateLabel.hidden) {
63 | arrowCenterX -= self.labelLeftInset + self.stateLabel.mj_textWith * 0.5;
64 | }
65 | CGFloat arrowCenterY = self.mj_h * 0.5;
66 | CGPoint arrowCenter = CGPointMake(arrowCenterX, arrowCenterY);
67 |
68 | // 箭头
69 | if (self.arrowView.constraints.count == 0) {
70 | self.arrowView.mj_size = self.arrowView.image.size;
71 | self.arrowView.center = arrowCenter;
72 | }
73 |
74 | // 圈圈
75 | if (self.loadingView.constraints.count == 0) {
76 | self.loadingView.center = arrowCenter;
77 | }
78 |
79 | self.arrowView.tintColor = self.stateLabel.textColor;
80 | }
81 |
82 | - (void)setState:(MJRefreshState)state
83 | {
84 | MJRefreshCheckState
85 |
86 | // 根据状态做事情
87 | if (state == MJRefreshStateIdle) {
88 | if (oldState == MJRefreshStateRefreshing) {
89 | self.arrowView.transform = CGAffineTransformMakeRotation(0.000001 - M_PI);
90 | [UIView animateWithDuration:MJRefreshSlowAnimationDuration animations:^{
91 | self.loadingView.alpha = 0.0;
92 | } completion:^(BOOL finished) {
93 | self.loadingView.alpha = 1.0;
94 | [self.loadingView stopAnimating];
95 |
96 | self.arrowView.hidden = NO;
97 | }];
98 | } else {
99 | self.arrowView.hidden = NO;
100 | [self.loadingView stopAnimating];
101 | [UIView animateWithDuration:MJRefreshFastAnimationDuration animations:^{
102 | self.arrowView.transform = CGAffineTransformMakeRotation(0.000001 - M_PI);
103 | }];
104 | }
105 | } else if (state == MJRefreshStatePulling) {
106 | self.arrowView.hidden = NO;
107 | [self.loadingView stopAnimating];
108 | [UIView animateWithDuration:MJRefreshFastAnimationDuration animations:^{
109 | self.arrowView.transform = CGAffineTransformIdentity;
110 | }];
111 | } else if (state == MJRefreshStateRefreshing) {
112 | self.arrowView.hidden = YES;
113 | [self.loadingView startAnimating];
114 | } else if (state == MJRefreshStateNoMoreData) {
115 | self.arrowView.hidden = YES;
116 | [self.loadingView stopAnimating];
117 | }
118 | }
119 |
120 | @end
121 |
--------------------------------------------------------------------------------
/ios/MJRefresh/Custom/Footer/Back/MJRefreshBackStateFooter.h:
--------------------------------------------------------------------------------
1 | //
2 | // MJRefreshBackStateFooter.h
3 | // MJRefreshExample
4 | //
5 | // Created by MJ Lee on 15/6/13.
6 | // Copyright © 2015年 小码哥. All rights reserved.
7 | //
8 |
9 | #import "MJRefreshBackFooter.h"
10 |
11 | @interface MJRefreshBackStateFooter : MJRefreshBackFooter
12 | /** 文字距离圈圈、箭头的距离 */
13 | @property (assign, nonatomic) CGFloat labelLeftInset;
14 | /** 显示刷新状态的label */
15 | @property (weak, nonatomic, readonly) UILabel *stateLabel;
16 | /** 设置state状态下的文字 */
17 | - (void)setTitle:(NSString *)title forState:(MJRefreshState)state;
18 |
19 | /** 获取state状态下的title */
20 | - (NSString *)titleForState:(MJRefreshState)state;
21 | @end
22 |
--------------------------------------------------------------------------------
/ios/MJRefresh/Custom/Footer/Back/MJRefreshBackStateFooter.m:
--------------------------------------------------------------------------------
1 | //
2 | // MJRefreshBackStateFooter.m
3 | // MJRefreshExample
4 | //
5 | // Created by MJ Lee on 15/6/13.
6 | // Copyright © 2015年 小码哥. All rights reserved.
7 | //
8 |
9 | #import "MJRefreshBackStateFooter.h"
10 |
11 | @interface MJRefreshBackStateFooter()
12 | {
13 | /** 显示刷新状态的label */
14 | __unsafe_unretained UILabel *_stateLabel;
15 | }
16 | /** 所有状态对应的文字 */
17 | @property (strong, nonatomic) NSMutableDictionary *stateTitles;
18 | @end
19 |
20 | @implementation MJRefreshBackStateFooter
21 | #pragma mark - 懒加载
22 | - (NSMutableDictionary *)stateTitles
23 | {
24 | if (!_stateTitles) {
25 | self.stateTitles = [NSMutableDictionary dictionary];
26 | }
27 | return _stateTitles;
28 | }
29 |
30 | - (UILabel *)stateLabel
31 | {
32 | if (!_stateLabel) {
33 | [self addSubview:_stateLabel = [UILabel mj_label]];
34 | }
35 | return _stateLabel;
36 | }
37 |
38 | #pragma mark - 公共方法
39 | - (void)setTitle:(NSString *)title forState:(MJRefreshState)state
40 | {
41 | if (title == nil) return;
42 | self.stateTitles[@(state)] = title;
43 | self.stateLabel.text = self.stateTitles[@(self.state)];
44 | }
45 |
46 | - (NSString *)titleForState:(MJRefreshState)state {
47 | return self.stateTitles[@(state)];
48 | }
49 |
50 | #pragma mark - 重写父类的方法
51 | - (void)prepare
52 | {
53 | [super prepare];
54 |
55 | // 初始化间距
56 | self.labelLeftInset = MJRefreshLabelLeftInset;
57 |
58 | // 初始化文字
59 | [self setTitle:[NSBundle mj_localizedStringForKey:MJRefreshBackFooterIdleText] forState:MJRefreshStateIdle];
60 | [self setTitle:[NSBundle mj_localizedStringForKey:MJRefreshBackFooterPullingText] forState:MJRefreshStatePulling];
61 | [self setTitle:[NSBundle mj_localizedStringForKey:MJRefreshBackFooterRefreshingText] forState:MJRefreshStateRefreshing];
62 | [self setTitle:[NSBundle mj_localizedStringForKey:MJRefreshBackFooterNoMoreDataText] forState:MJRefreshStateNoMoreData];
63 | }
64 |
65 | - (void)placeSubviews
66 | {
67 | [super placeSubviews];
68 |
69 | if (self.stateLabel.constraints.count) return;
70 |
71 | // 状态标签
72 | self.stateLabel.frame = self.bounds;
73 | }
74 |
75 | - (void)setState:(MJRefreshState)state
76 | {
77 | MJRefreshCheckState
78 |
79 | // 设置状态文字
80 | self.stateLabel.text = self.stateTitles[@(state)];
81 | }
82 | @end
83 |
--------------------------------------------------------------------------------
/ios/MJRefresh/Custom/Header/MJRefreshGifHeader.h:
--------------------------------------------------------------------------------
1 | //
2 | // MJRefreshGifHeader.h
3 | // MJRefreshExample
4 | //
5 | // Created by MJ Lee on 15/4/24.
6 | // Copyright (c) 2015年 小码哥. All rights reserved.
7 | //
8 |
9 | #import "MJRefreshStateHeader.h"
10 |
11 | @interface MJRefreshGifHeader : MJRefreshStateHeader
12 | @property (weak, nonatomic, readonly) UIImageView *gifView;
13 |
14 | /** 设置state状态下的动画图片images 动画持续时间duration*/
15 | - (void)setImages:(NSArray *)images duration:(NSTimeInterval)duration forState:(MJRefreshState)state;
16 | - (void)setImages:(NSArray *)images forState:(MJRefreshState)state;
17 | @end
18 |
--------------------------------------------------------------------------------
/ios/MJRefresh/Custom/Header/MJRefreshGifHeader.m:
--------------------------------------------------------------------------------
1 | //
2 | // MJRefreshGifHeader.m
3 | // MJRefreshExample
4 | //
5 | // Created by MJ Lee on 15/4/24.
6 | // Copyright (c) 2015年 小码哥. All rights reserved.
7 | //
8 |
9 | #import "MJRefreshGifHeader.h"
10 |
11 | @interface MJRefreshGifHeader()
12 | {
13 | __unsafe_unretained UIImageView *_gifView;
14 | }
15 | /** 所有状态对应的动画图片 */
16 | @property (strong, nonatomic) NSMutableDictionary *stateImages;
17 | /** 所有状态对应的动画时间 */
18 | @property (strong, nonatomic) NSMutableDictionary *stateDurations;
19 | @end
20 |
21 | @implementation MJRefreshGifHeader
22 | #pragma mark - 懒加载
23 | - (UIImageView *)gifView
24 | {
25 | if (!_gifView) {
26 | UIImageView *gifView = [[UIImageView alloc] init];
27 | [self addSubview:_gifView = gifView];
28 | }
29 | return _gifView;
30 | }
31 |
32 | - (NSMutableDictionary *)stateImages
33 | {
34 | if (!_stateImages) {
35 | self.stateImages = [NSMutableDictionary dictionary];
36 | }
37 | return _stateImages;
38 | }
39 |
40 | - (NSMutableDictionary *)stateDurations
41 | {
42 | if (!_stateDurations) {
43 | self.stateDurations = [NSMutableDictionary dictionary];
44 | }
45 | return _stateDurations;
46 | }
47 |
48 | #pragma mark - 公共方法
49 | - (void)setImages:(NSArray *)images duration:(NSTimeInterval)duration forState:(MJRefreshState)state
50 | {
51 | if (images == nil) return;
52 |
53 | self.stateImages[@(state)] = images;
54 | self.stateDurations[@(state)] = @(duration);
55 |
56 | /* 根据图片设置控件的高度 */
57 | UIImage *image = [images firstObject];
58 | if (image.size.height > self.mj_h) {
59 | self.mj_h = image.size.height;
60 | }
61 | }
62 |
63 | - (void)setImages:(NSArray *)images forState:(MJRefreshState)state
64 | {
65 | [self setImages:images duration:images.count * 0.1 forState:state];
66 | }
67 |
68 | #pragma mark - 实现父类的方法
69 | - (void)prepare
70 | {
71 | [super prepare];
72 |
73 | // 初始化间距
74 | self.labelLeftInset = 20;
75 | }
76 |
77 | - (void)setPullingPercent:(CGFloat)pullingPercent
78 | {
79 | [super setPullingPercent:pullingPercent];
80 | NSArray *images = self.stateImages[@(MJRefreshStateIdle)];
81 | if (self.state != MJRefreshStateIdle || images.count == 0) return;
82 | // 停止动画
83 | [self.gifView stopAnimating];
84 | // 设置当前需要显示的图片
85 | NSUInteger index = images.count * pullingPercent;
86 | if (index >= images.count) index = images.count - 1;
87 | self.gifView.image = images[index];
88 | }
89 |
90 | - (void)placeSubviews
91 | {
92 | [super placeSubviews];
93 |
94 | if (self.gifView.constraints.count) return;
95 |
96 | self.gifView.frame = self.bounds;
97 | if (self.stateLabel.hidden && self.lastUpdatedTimeLabel.hidden) {
98 | self.gifView.contentMode = UIViewContentModeCenter;
99 | } else {
100 | self.gifView.contentMode = UIViewContentModeRight;
101 |
102 | CGFloat stateWidth = self.stateLabel.mj_textWith;
103 | CGFloat timeWidth = 0.0;
104 | if (!self.lastUpdatedTimeLabel.hidden) {
105 | timeWidth = self.lastUpdatedTimeLabel.mj_textWith;
106 | }
107 | CGFloat textWidth = MAX(stateWidth, timeWidth);
108 | self.gifView.mj_w = self.mj_w * 0.5 - textWidth * 0.5 - self.labelLeftInset;
109 | }
110 | }
111 |
112 | - (void)setState:(MJRefreshState)state
113 | {
114 | MJRefreshCheckState
115 |
116 | // 根据状态做事情
117 | if (state == MJRefreshStatePulling || state == MJRefreshStateRefreshing) {
118 | NSArray *images = self.stateImages[@(state)];
119 | if (images.count == 0) return;
120 |
121 | [self.gifView stopAnimating];
122 | if (images.count == 1) { // 单张图片
123 | self.gifView.image = [images lastObject];
124 | } else { // 多张图片
125 | self.gifView.animationImages = images;
126 | self.gifView.animationDuration = [self.stateDurations[@(state)] doubleValue];
127 | [self.gifView startAnimating];
128 | }
129 | } else if (state == MJRefreshStateIdle) {
130 | [self.gifView stopAnimating];
131 | }
132 | }
133 | @end
134 |
--------------------------------------------------------------------------------
/ios/MJRefresh/Custom/Header/MJRefreshNormalHeader.h:
--------------------------------------------------------------------------------
1 | //
2 | // MJRefreshNormalHeader.h
3 | // MJRefreshExample
4 | //
5 | // Created by MJ Lee on 15/4/24.
6 | // Copyright (c) 2015年 小码哥. All rights reserved.
7 | //
8 |
9 | #import "MJRefreshStateHeader.h"
10 |
11 | @interface MJRefreshNormalHeader : MJRefreshStateHeader
12 | @property (weak, nonatomic, readonly) UIImageView *arrowView;
13 | /** 菊花的样式 */
14 | @property (assign, nonatomic) UIActivityIndicatorViewStyle activityIndicatorViewStyle;
15 | @end
16 |
--------------------------------------------------------------------------------
/ios/MJRefresh/Custom/Header/MJRefreshNormalHeader.m:
--------------------------------------------------------------------------------
1 | //
2 | // MJRefreshNormalHeader.m
3 | // MJRefreshExample
4 | //
5 | // Created by MJ Lee on 15/4/24.
6 | // Copyright (c) 2015年 小码哥. All rights reserved.
7 | //
8 |
9 | #import "MJRefreshNormalHeader.h"
10 | #import "NSBundle+MJRefresh.h"
11 |
12 | @interface MJRefreshNormalHeader()
13 | {
14 | __unsafe_unretained UIImageView *_arrowView;
15 | }
16 | @property (weak, nonatomic) UIActivityIndicatorView *loadingView;
17 | @end
18 |
19 | @implementation MJRefreshNormalHeader
20 | #pragma mark - 懒加载子控件
21 | - (UIImageView *)arrowView
22 | {
23 | if (!_arrowView) {
24 | UIImageView *arrowView = [[UIImageView alloc] initWithImage:[NSBundle mj_arrowImage]];
25 | [self addSubview:_arrowView = arrowView];
26 | }
27 | return _arrowView;
28 | }
29 |
30 | - (UIActivityIndicatorView *)loadingView
31 | {
32 | if (!_loadingView) {
33 | UIActivityIndicatorView *loadingView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:self.activityIndicatorViewStyle];
34 | loadingView.hidesWhenStopped = YES;
35 | [self addSubview:_loadingView = loadingView];
36 | }
37 | return _loadingView;
38 | }
39 |
40 | #pragma mark - 公共方法
41 | - (void)setActivityIndicatorViewStyle:(UIActivityIndicatorViewStyle)activityIndicatorViewStyle
42 | {
43 | _activityIndicatorViewStyle = activityIndicatorViewStyle;
44 |
45 | self.loadingView = nil;
46 | [self setNeedsLayout];
47 | }
48 |
49 | #pragma mark - 重写父类的方法
50 | - (void)prepare
51 | {
52 | [super prepare];
53 |
54 | self.activityIndicatorViewStyle = UIActivityIndicatorViewStyleGray;
55 | }
56 |
57 | - (void)placeSubviews
58 | {
59 | [super placeSubviews];
60 |
61 | // 箭头的中心点
62 | CGFloat arrowCenterX = self.mj_w * 0.5;
63 | if (!self.stateLabel.hidden) {
64 | CGFloat stateWidth = self.stateLabel.mj_textWith;
65 | CGFloat timeWidth = 0.0;
66 | if (!self.lastUpdatedTimeLabel.hidden) {
67 | timeWidth = self.lastUpdatedTimeLabel.mj_textWith;
68 | }
69 | CGFloat textWidth = MAX(stateWidth, timeWidth);
70 | arrowCenterX -= textWidth / 2 + self.labelLeftInset;
71 | }
72 | CGFloat arrowCenterY = self.mj_h * 0.5;
73 | CGPoint arrowCenter = CGPointMake(arrowCenterX, arrowCenterY);
74 |
75 | // 箭头
76 | if (self.arrowView.constraints.count == 0) {
77 | self.arrowView.mj_size = self.arrowView.image.size;
78 | self.arrowView.center = arrowCenter;
79 | }
80 |
81 | // 圈圈
82 | if (self.loadingView.constraints.count == 0) {
83 | self.loadingView.center = arrowCenter;
84 | }
85 |
86 | self.arrowView.tintColor = self.stateLabel.textColor;
87 | }
88 |
89 | - (void)setState:(MJRefreshState)state
90 | {
91 | MJRefreshCheckState
92 |
93 | // 根据状态做事情
94 | if (state == MJRefreshStateIdle) {
95 | if (oldState == MJRefreshStateRefreshing) {
96 | self.arrowView.transform = CGAffineTransformIdentity;
97 |
98 | [UIView animateWithDuration:MJRefreshSlowAnimationDuration animations:^{
99 | self.loadingView.alpha = 0.0;
100 | } completion:^(BOOL finished) {
101 | // 如果执行完动画发现不是idle状态,就直接返回,进入其他状态
102 | if (self.state != MJRefreshStateIdle) return;
103 |
104 | self.loadingView.alpha = 1.0;
105 | [self.loadingView stopAnimating];
106 | self.arrowView.hidden = NO;
107 | }];
108 | } else {
109 | [self.loadingView stopAnimating];
110 | self.arrowView.hidden = NO;
111 | [UIView animateWithDuration:MJRefreshFastAnimationDuration animations:^{
112 | self.arrowView.transform = CGAffineTransformIdentity;
113 | }];
114 | }
115 | } else if (state == MJRefreshStatePulling) {
116 | [self.loadingView stopAnimating];
117 | self.arrowView.hidden = NO;
118 | [UIView animateWithDuration:MJRefreshFastAnimationDuration animations:^{
119 | self.arrowView.transform = CGAffineTransformMakeRotation(0.000001 - M_PI);
120 | }];
121 | } else if (state == MJRefreshStateRefreshing) {
122 | self.loadingView.alpha = 1.0; // 防止refreshing -> idle的动画完毕动作没有被执行
123 | [self.loadingView startAnimating];
124 | self.arrowView.hidden = YES;
125 | }
126 | }
127 | @end
128 |
--------------------------------------------------------------------------------
/ios/MJRefresh/Custom/Header/MJRefreshStateHeader.h:
--------------------------------------------------------------------------------
1 | //
2 | // MJRefreshStateHeader.h
3 | // MJRefreshExample
4 | //
5 | // Created by MJ Lee on 15/4/24.
6 | // Copyright (c) 2015年 小码哥. All rights reserved.
7 | //
8 |
9 | #import "MJRefreshHeader.h"
10 |
11 | @interface MJRefreshStateHeader : MJRefreshHeader
12 | #pragma mark - 刷新时间相关
13 | /** 利用这个block来决定显示的更新时间文字 */
14 | @property (copy, nonatomic) NSString *(^lastUpdatedTimeText)(NSDate *lastUpdatedTime);
15 | /** 显示上一次刷新时间的label */
16 | @property (weak, nonatomic, readonly) UILabel *lastUpdatedTimeLabel;
17 |
18 | #pragma mark - 状态相关
19 | /** 文字距离圈圈、箭头的距离 */
20 | @property (assign, nonatomic) CGFloat labelLeftInset;
21 | /** 显示刷新状态的label */
22 | @property (weak, nonatomic, readonly) UILabel *stateLabel;
23 | /** 设置state状态下的文字 */
24 | - (void)setTitle:(NSString *)title forState:(MJRefreshState)state;
25 | @end
26 |
--------------------------------------------------------------------------------
/ios/MJRefresh/Custom/Header/MJRefreshStateHeader.m:
--------------------------------------------------------------------------------
1 | //
2 | // MJRefreshStateHeader.m
3 | // MJRefreshExample
4 | //
5 | // Created by MJ Lee on 15/4/24.
6 | // Copyright (c) 2015年 小码哥. All rights reserved.
7 | //
8 |
9 | #import "MJRefreshStateHeader.h"
10 |
11 | @interface MJRefreshStateHeader()
12 | {
13 | /** 显示上一次刷新时间的label */
14 | __unsafe_unretained UILabel *_lastUpdatedTimeLabel;
15 | /** 显示刷新状态的label */
16 | __unsafe_unretained UILabel *_stateLabel;
17 | }
18 | /** 所有状态对应的文字 */
19 | @property (strong, nonatomic) NSMutableDictionary *stateTitles;
20 | @end
21 |
22 | @implementation MJRefreshStateHeader
23 | #pragma mark - 懒加载
24 | - (NSMutableDictionary *)stateTitles
25 | {
26 | if (!_stateTitles) {
27 | self.stateTitles = [NSMutableDictionary dictionary];
28 | }
29 | return _stateTitles;
30 | }
31 |
32 | - (UILabel *)stateLabel
33 | {
34 | if (!_stateLabel) {
35 | [self addSubview:_stateLabel = [UILabel mj_label]];
36 | }
37 | return _stateLabel;
38 | }
39 |
40 | - (UILabel *)lastUpdatedTimeLabel
41 | {
42 | if (!_lastUpdatedTimeLabel) {
43 | [self addSubview:_lastUpdatedTimeLabel = [UILabel mj_label]];
44 | }
45 | return _lastUpdatedTimeLabel;
46 | }
47 |
48 | #pragma mark - 公共方法
49 | - (void)setTitle:(NSString *)title forState:(MJRefreshState)state
50 | {
51 | if (title == nil) return;
52 | self.stateTitles[@(state)] = title;
53 | self.stateLabel.text = self.stateTitles[@(self.state)];
54 | }
55 |
56 | #pragma mark - 日历获取在9.x之后的系统使用currentCalendar会出异常。在8.0之后使用系统新API。
57 | - (NSCalendar *)currentCalendar {
58 | if ([NSCalendar respondsToSelector:@selector(calendarWithIdentifier:)]) {
59 | return [NSCalendar calendarWithIdentifier:NSCalendarIdentifierGregorian];
60 | }
61 | return [NSCalendar currentCalendar];
62 | }
63 |
64 | #pragma mark key的处理
65 | - (void)setLastUpdatedTimeKey:(NSString *)lastUpdatedTimeKey
66 | {
67 | [super setLastUpdatedTimeKey:lastUpdatedTimeKey];
68 |
69 | // 如果label隐藏了,就不用再处理
70 | if (self.lastUpdatedTimeLabel.hidden) return;
71 |
72 | NSDate *lastUpdatedTime = [[NSUserDefaults standardUserDefaults] objectForKey:lastUpdatedTimeKey];
73 |
74 | // 如果有block
75 | if (self.lastUpdatedTimeText) {
76 | self.lastUpdatedTimeLabel.text = self.lastUpdatedTimeText(lastUpdatedTime);
77 | return;
78 | }
79 |
80 | if (lastUpdatedTime) {
81 | // 1.获得年月日
82 | NSCalendar *calendar = [self currentCalendar];
83 | NSUInteger unitFlags = NSCalendarUnitYear| NSCalendarUnitMonth | NSCalendarUnitDay |NSCalendarUnitHour |NSCalendarUnitMinute;
84 | NSDateComponents *cmp1 = [calendar components:unitFlags fromDate:lastUpdatedTime];
85 | NSDateComponents *cmp2 = [calendar components:unitFlags fromDate:[NSDate date]];
86 |
87 | // 2.格式化日期
88 | NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
89 | BOOL isToday = NO;
90 | if ([cmp1 day] == [cmp2 day]) { // 今天
91 | formatter.dateFormat = @" HH:mm";
92 | isToday = YES;
93 | } else if ([cmp1 year] == [cmp2 year]) { // 今年
94 | formatter.dateFormat = @"MM-dd HH:mm";
95 | } else {
96 | formatter.dateFormat = @"yyyy-MM-dd HH:mm";
97 | }
98 | NSString *time = [formatter stringFromDate:lastUpdatedTime];
99 |
100 | // 3.显示日期
101 | self.lastUpdatedTimeLabel.text = [NSString stringWithFormat:@"%@%@%@",
102 | [NSBundle mj_localizedStringForKey:MJRefreshHeaderLastTimeText],
103 | isToday ? [NSBundle mj_localizedStringForKey:MJRefreshHeaderDateTodayText] : @"",
104 | time];
105 | } else {
106 | self.lastUpdatedTimeLabel.text = [NSString stringWithFormat:@"%@%@",
107 | [NSBundle mj_localizedStringForKey:MJRefreshHeaderLastTimeText],
108 | [NSBundle mj_localizedStringForKey:MJRefreshHeaderNoneLastDateText]];
109 | }
110 | }
111 |
112 | #pragma mark - 覆盖父类的方法
113 | - (void)prepare
114 | {
115 | [super prepare];
116 |
117 | // 初始化间距
118 | self.labelLeftInset = MJRefreshLabelLeftInset;
119 |
120 | // 初始化文字
121 | [self setTitle:[NSBundle mj_localizedStringForKey:MJRefreshHeaderIdleText] forState:MJRefreshStateIdle];
122 | [self setTitle:[NSBundle mj_localizedStringForKey:MJRefreshHeaderPullingText] forState:MJRefreshStatePulling];
123 | [self setTitle:[NSBundle mj_localizedStringForKey:MJRefreshHeaderRefreshingText] forState:MJRefreshStateRefreshing];
124 | }
125 |
126 | - (void)placeSubviews
127 | {
128 | [super placeSubviews];
129 |
130 | if (self.stateLabel.hidden) return;
131 |
132 | BOOL noConstrainsOnStatusLabel = self.stateLabel.constraints.count == 0;
133 |
134 | if (self.lastUpdatedTimeLabel.hidden) {
135 | // 状态
136 | if (noConstrainsOnStatusLabel) self.stateLabel.frame = self.bounds;
137 | } else {
138 | CGFloat stateLabelH = self.mj_h * 0.5;
139 | // 状态
140 | if (noConstrainsOnStatusLabel) {
141 | self.stateLabel.mj_x = 0;
142 | self.stateLabel.mj_y = 0;
143 | self.stateLabel.mj_w = self.mj_w;
144 | self.stateLabel.mj_h = stateLabelH;
145 | }
146 |
147 | // 更新时间
148 | if (self.lastUpdatedTimeLabel.constraints.count == 0) {
149 | self.lastUpdatedTimeLabel.mj_x = 0;
150 | self.lastUpdatedTimeLabel.mj_y = stateLabelH;
151 | self.lastUpdatedTimeLabel.mj_w = self.mj_w;
152 | self.lastUpdatedTimeLabel.mj_h = self.mj_h - self.lastUpdatedTimeLabel.mj_y;
153 | }
154 | }
155 | }
156 |
157 | - (void)setState:(MJRefreshState)state
158 | {
159 | MJRefreshCheckState
160 |
161 | // 设置状态文字
162 | self.stateLabel.text = self.stateTitles[@(state)];
163 |
164 | // 重新设置key(重新显示时间)
165 | self.lastUpdatedTimeKey = self.lastUpdatedTimeKey;
166 | }
167 | @end
168 |
--------------------------------------------------------------------------------
/ios/MJRefresh/MJRefresh.bundle/arrow@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gitSirzh/react-native-refresh-control-enrichment/HEAD/ios/MJRefresh/MJRefresh.bundle/arrow@2x.png
--------------------------------------------------------------------------------
/ios/MJRefresh/MJRefresh.bundle/en.lproj/Localizable.strings:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gitSirzh/react-native-refresh-control-enrichment/HEAD/ios/MJRefresh/MJRefresh.bundle/en.lproj/Localizable.strings
--------------------------------------------------------------------------------
/ios/MJRefresh/MJRefresh.bundle/zh-Hans.lproj/Localizable.strings:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gitSirzh/react-native-refresh-control-enrichment/HEAD/ios/MJRefresh/MJRefresh.bundle/zh-Hans.lproj/Localizable.strings
--------------------------------------------------------------------------------
/ios/MJRefresh/MJRefresh.bundle/zh-Hant.lproj/Localizable.strings:
--------------------------------------------------------------------------------
1 | "MJRefreshHeaderIdleText" = "下拉可以刷新";
2 | "MJRefreshHeaderPullingText" = "鬆開立即刷新";
3 | "MJRefreshHeaderRefreshingText" = "正在刷新數據中...";
4 |
5 | "MJRefreshAutoFooterIdleText" = "點擊或上拉加載更多";
6 | "MJRefreshAutoFooterRefreshingText" = "正在加載更多的數據...";
7 | "MJRefreshAutoFooterNoMoreDataText" = "已經全部加載完畢";
8 |
9 | "MJRefreshBackFooterIdleText" = "上拉可以加載更多";
10 | "MJRefreshBackFooterPullingText" = "鬆開立即加載更多";
11 | "MJRefreshBackFooterRefreshingText" = "正在加載更多的數據...";
12 | "MJRefreshBackFooterNoMoreDataText" = "已經全部加載完畢";
13 |
14 | "MJRefreshHeaderLastTimeText" = "最後更新:";
15 | "MJRefreshHeaderDateTodayText" = "今天";
16 | "MJRefreshHeaderNoneLastDateText" = "無記錄";
17 |
--------------------------------------------------------------------------------
/ios/MJRefresh/MJRefresh.h:
--------------------------------------------------------------------------------
1 | // 代码地址: https://github.com/CoderMJLee/MJRefresh
2 | // 代码地址: http://code4app.com/ios/%E5%BF%AB%E9%80%9F%E9%9B%86%E6%88%90%E4%B8%8B%E6%8B%89%E4%B8%8A%E6%8B%89%E5%88%B7%E6%96%B0/52326ce26803fabc46000000
3 |
4 | #import "UIScrollView+MJRefresh.h"
5 | #import "UIScrollView+MJExtension.h"
6 | #import "UIView+MJExtension.h"
7 |
8 | #import "MJRefreshNormalHeader.h"
9 | #import "MJRefreshGifHeader.h"
10 |
11 | #import "MJRefreshBackNormalFooter.h"
12 | #import "MJRefreshBackGifFooter.h"
13 | #import "MJRefreshAutoNormalFooter.h"
14 | #import "MJRefreshAutoGifFooter.h"
--------------------------------------------------------------------------------
/ios/MJRefresh/MJRefreshConst.h:
--------------------------------------------------------------------------------
1 | // 代码地址: https://github.com/CoderMJLee/MJRefresh
2 | // 代码地址: http://code4app.com/ios/%E5%BF%AB%E9%80%9F%E9%9B%86%E6%88%90%E4%B8%8B%E6%8B%89%E4%B8%8A%E6%8B%89%E5%88%B7%E6%96%B0/52326ce26803fabc46000000
3 | #import
4 | #import
5 |
6 | // 弱引用
7 | #define MJWeakSelf __weak typeof(self) weakSelf = self;
8 |
9 | // 日志输出
10 | #ifdef DEBUG
11 | #define MJRefreshLog(...) NSLog(__VA_ARGS__)
12 | #else
13 | #define MJRefreshLog(...)
14 | #endif
15 |
16 | // 过期提醒
17 | #define MJRefreshDeprecated(instead) NS_DEPRECATED(2_0, 2_0, 2_0, 2_0, instead)
18 |
19 | // 运行时objc_msgSend
20 | #define MJRefreshMsgSend(...) ((void (*)(void *, SEL, UIView *))objc_msgSend)(__VA_ARGS__)
21 | #define MJRefreshMsgTarget(target) (__bridge void *)(target)
22 |
23 | // RGB颜色
24 | #define MJRefreshColor(r, g, b) [UIColor colorWithRed:(r)/255.0 green:(g)/255.0 blue:(b)/255.0 alpha:1.0]
25 |
26 | // 文字颜色
27 | #define MJRefreshLabelTextColor MJRefreshColor(90, 90, 90)
28 |
29 | // 字体大小
30 | #define MJRefreshLabelFont [UIFont boldSystemFontOfSize:14]
31 |
32 | // 常量
33 | UIKIT_EXTERN const CGFloat MJRefreshLabelLeftInset;
34 | UIKIT_EXTERN const CGFloat MJRefreshHeaderHeight;
35 | UIKIT_EXTERN const CGFloat MJRefreshFooterHeight;
36 | UIKIT_EXTERN const CGFloat MJRefreshFastAnimationDuration;
37 | UIKIT_EXTERN const CGFloat MJRefreshSlowAnimationDuration;
38 |
39 | UIKIT_EXTERN NSString *const MJRefreshKeyPathContentOffset;
40 | UIKIT_EXTERN NSString *const MJRefreshKeyPathContentSize;
41 | UIKIT_EXTERN NSString *const MJRefreshKeyPathContentInset;
42 | UIKIT_EXTERN NSString *const MJRefreshKeyPathPanState;
43 |
44 | UIKIT_EXTERN NSString *const MJRefreshHeaderLastUpdatedTimeKey;
45 |
46 | UIKIT_EXTERN NSString *const MJRefreshHeaderIdleText;
47 | UIKIT_EXTERN NSString *const MJRefreshHeaderPullingText;
48 | UIKIT_EXTERN NSString *const MJRefreshHeaderRefreshingText;
49 |
50 | UIKIT_EXTERN NSString *const MJRefreshAutoFooterIdleText;
51 | UIKIT_EXTERN NSString *const MJRefreshAutoFooterRefreshingText;
52 | UIKIT_EXTERN NSString *const MJRefreshAutoFooterNoMoreDataText;
53 |
54 | UIKIT_EXTERN NSString *const MJRefreshBackFooterIdleText;
55 | UIKIT_EXTERN NSString *const MJRefreshBackFooterPullingText;
56 | UIKIT_EXTERN NSString *const MJRefreshBackFooterRefreshingText;
57 | UIKIT_EXTERN NSString *const MJRefreshBackFooterNoMoreDataText;
58 |
59 | UIKIT_EXTERN NSString *const MJRefreshHeaderLastTimeText;
60 | UIKIT_EXTERN NSString *const MJRefreshHeaderDateTodayText;
61 | UIKIT_EXTERN NSString *const MJRefreshHeaderNoneLastDateText;
62 |
63 | // 状态检查
64 | #define MJRefreshCheckState \
65 | MJRefreshState oldState = self.state; \
66 | if (state == oldState) return; \
67 | [super setState:state];
68 |
--------------------------------------------------------------------------------
/ios/MJRefresh/MJRefreshConst.m:
--------------------------------------------------------------------------------
1 | // 代码地址: https://github.com/CoderMJLee/MJRefresh
2 | // 代码地址: http://code4app.com/ios/%E5%BF%AB%E9%80%9F%E9%9B%86%E6%88%90%E4%B8%8B%E6%8B%89%E4%B8%8A%E6%8B%89%E5%88%B7%E6%96%B0/52326ce26803fabc46000000
3 | #import
4 |
5 | const CGFloat MJRefreshLabelLeftInset = 25;
6 | const CGFloat MJRefreshHeaderHeight = 54.0;
7 | const CGFloat MJRefreshFooterHeight = 44.0;
8 | const CGFloat MJRefreshFastAnimationDuration = 0.25;
9 | const CGFloat MJRefreshSlowAnimationDuration = 0.4;
10 |
11 | NSString *const MJRefreshKeyPathContentOffset = @"contentOffset";
12 | NSString *const MJRefreshKeyPathContentInset = @"contentInset";
13 | NSString *const MJRefreshKeyPathContentSize = @"contentSize";
14 | NSString *const MJRefreshKeyPathPanState = @"state";
15 |
16 | NSString *const MJRefreshHeaderLastUpdatedTimeKey = @"MJRefreshHeaderLastUpdatedTimeKey";
17 |
18 | NSString *const MJRefreshHeaderIdleText = @"MJRefreshHeaderIdleText";
19 | NSString *const MJRefreshHeaderPullingText = @"MJRefreshHeaderPullingText";
20 | NSString *const MJRefreshHeaderRefreshingText = @"MJRefreshHeaderRefreshingText";
21 |
22 | NSString *const MJRefreshAutoFooterIdleText = @"MJRefreshAutoFooterIdleText";
23 | NSString *const MJRefreshAutoFooterRefreshingText = @"MJRefreshAutoFooterRefreshingText";
24 | NSString *const MJRefreshAutoFooterNoMoreDataText = @"MJRefreshAutoFooterNoMoreDataText";
25 |
26 | NSString *const MJRefreshBackFooterIdleText = @"MJRefreshBackFooterIdleText";
27 | NSString *const MJRefreshBackFooterPullingText = @"MJRefreshBackFooterPullingText";
28 | NSString *const MJRefreshBackFooterRefreshingText = @"MJRefreshBackFooterRefreshingText";
29 | NSString *const MJRefreshBackFooterNoMoreDataText = @"MJRefreshBackFooterNoMoreDataText";
30 |
31 | NSString *const MJRefreshHeaderLastTimeText = @"MJRefreshHeaderLastTimeText";
32 | NSString *const MJRefreshHeaderDateTodayText = @"MJRefreshHeaderDateTodayText";
33 | NSString *const MJRefreshHeaderNoneLastDateText = @"MJRefreshHeaderNoneLastDateText";
--------------------------------------------------------------------------------
/ios/MJRefresh/NSBundle+MJRefresh.h:
--------------------------------------------------------------------------------
1 | //
2 | // NSBundle+MJRefresh.h
3 | // MJRefreshExample
4 | //
5 | // Created by MJ Lee on 16/6/13.
6 | // Copyright © 2016年 小码哥. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface NSBundle (MJRefresh)
12 | + (instancetype)mj_refreshBundle;
13 | + (UIImage *)mj_arrowImage;
14 | + (NSString *)mj_localizedStringForKey:(NSString *)key value:(NSString *)value;
15 | + (NSString *)mj_localizedStringForKey:(NSString *)key;
16 | @end
17 |
--------------------------------------------------------------------------------
/ios/MJRefresh/NSBundle+MJRefresh.m:
--------------------------------------------------------------------------------
1 | //
2 | // NSBundle+MJRefresh.m
3 | // MJRefreshExample
4 | //
5 | // Created by MJ Lee on 16/6/13.
6 | // Copyright © 2016年 小码哥. All rights reserved.
7 | //
8 |
9 | #import "NSBundle+MJRefresh.h"
10 | #import "MJRefreshComponent.h"
11 |
12 | @implementation NSBundle (MJRefresh)
13 | + (instancetype)mj_refreshBundle
14 | {
15 | static NSBundle *refreshBundle = nil;
16 | if (refreshBundle == nil) {
17 | // 这里不使用mainBundle是为了适配pod 1.x和0.x
18 | refreshBundle = [NSBundle bundleWithPath:[[NSBundle bundleForClass:[MJRefreshComponent class]] pathForResource:@"MJRefresh" ofType:@"bundle"]];
19 | }
20 | return refreshBundle;
21 | }
22 |
23 | + (UIImage *)mj_arrowImage
24 | {
25 | static UIImage *arrowImage = nil;
26 | if (arrowImage == nil) {
27 | arrowImage = [[UIImage imageWithContentsOfFile:[[self mj_refreshBundle] pathForResource:@"arrow@2x" ofType:@"png"]] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
28 | }
29 | return arrowImage;
30 | }
31 |
32 | + (NSString *)mj_localizedStringForKey:(NSString *)key
33 | {
34 | return [self mj_localizedStringForKey:key value:nil];
35 | }
36 |
37 | + (NSString *)mj_localizedStringForKey:(NSString *)key value:(NSString *)value
38 | {
39 | static NSBundle *bundle = nil;
40 | if (bundle == nil) {
41 | // (iOS获取的语言字符串比较不稳定)目前框架只处理en、zh-Hans、zh-Hant三种情况,其他按照系统默认处理
42 | NSString *language = [NSLocale preferredLanguages].firstObject;
43 | if ([language hasPrefix:@"en"]) {
44 | language = @"en";
45 | } else if ([language hasPrefix:@"zh"]) {
46 | if ([language rangeOfString:@"Hans"].location != NSNotFound) {
47 | language = @"zh-Hans"; // 简体中文
48 | } else { // zh-Hant\zh-HK\zh-TW
49 | language = @"zh-Hant"; // 繁體中文
50 | }
51 | } else {
52 | language = @"en";
53 | }
54 |
55 | // 从MJRefresh.bundle中查找资源
56 | bundle = [NSBundle bundleWithPath:[[NSBundle mj_refreshBundle] pathForResource:language ofType:@"lproj"]];
57 | }
58 | value = [bundle localizedStringForKey:key value:value table:nil];
59 | return [[NSBundle mainBundle] localizedStringForKey:key value:value table:nil];
60 | }
61 | @end
62 |
--------------------------------------------------------------------------------
/ios/MJRefresh/UIScrollView+MJExtension.h:
--------------------------------------------------------------------------------
1 | // 代码地址: https://github.com/CoderMJLee/MJRefresh
2 | // 代码地址: http://code4app.com/ios/%E5%BF%AB%E9%80%9F%E9%9B%86%E6%88%90%E4%B8%8B%E6%8B%89%E4%B8%8A%E6%8B%89%E5%88%B7%E6%96%B0/52326ce26803fabc46000000
3 | // UIScrollView+Extension.h
4 | // MJRefreshExample
5 | //
6 | // Created by MJ Lee on 14-5-28.
7 | // Copyright (c) 2014年 小码哥. All rights reserved.
8 | //
9 |
10 | #import
11 |
12 | @interface UIScrollView (MJExtension)
13 | @property (readonly, nonatomic) UIEdgeInsets mj_inset;
14 |
15 | @property (assign, nonatomic) CGFloat mj_insetT;
16 | @property (assign, nonatomic) CGFloat mj_insetB;
17 | @property (assign, nonatomic) CGFloat mj_insetL;
18 | @property (assign, nonatomic) CGFloat mj_insetR;
19 |
20 | @property (assign, nonatomic) CGFloat mj_offsetX;
21 | @property (assign, nonatomic) CGFloat mj_offsetY;
22 |
23 | @property (assign, nonatomic) CGFloat mj_contentW;
24 | @property (assign, nonatomic) CGFloat mj_contentH;
25 | @end
26 |
--------------------------------------------------------------------------------
/ios/MJRefresh/UIScrollView+MJExtension.m:
--------------------------------------------------------------------------------
1 | // 代码地址: https://github.com/CoderMJLee/MJRefresh
2 | // 代码地址: http://code4app.com/ios/%E5%BF%AB%E9%80%9F%E9%9B%86%E6%88%90%E4%B8%8B%E6%8B%89%E4%B8%8A%E6%8B%89%E5%88%B7%E6%96%B0/52326ce26803fabc46000000
3 | // UIScrollView+Extension.m
4 | // MJRefreshExample
5 | //
6 | // Created by MJ Lee on 14-5-28.
7 | // Copyright (c) 2014年 小码哥. All rights reserved.
8 | //
9 |
10 | #import "UIScrollView+MJExtension.h"
11 | #import
12 |
13 | #pragma clang diagnostic push
14 | #pragma clang diagnostic ignored "-Wunguarded-availability-new"
15 |
16 | @implementation UIScrollView (MJExtension)
17 |
18 | static BOOL gt_ios_11_;
19 | + (void)load
20 | {
21 | // 缓存判断值
22 | gt_ios_11_ = ([[[UIDevice currentDevice] systemVersion] compare:@"11" options:NSNumericSearch] != NSOrderedAscending);
23 | }
24 |
25 | - (UIEdgeInsets)mj_inset
26 | {
27 | #ifdef __IPHONE_11_0
28 | if (gt_ios_11_) {
29 | return self.adjustedContentInset;
30 | }
31 | #endif
32 | return self.contentInset;
33 | }
34 |
35 | - (void)setMj_insetT:(CGFloat)mj_insetT
36 | {
37 | UIEdgeInsets inset = self.contentInset;
38 | inset.top = mj_insetT;
39 | #ifdef __IPHONE_11_0
40 | if (gt_ios_11_) {
41 | inset.top -= (self.adjustedContentInset.top - self.contentInset.top);
42 | }
43 | #endif
44 | self.contentInset = inset;
45 | }
46 |
47 | - (CGFloat)mj_insetT
48 | {
49 | return self.mj_inset.top;
50 | }
51 |
52 | - (void)setMj_insetB:(CGFloat)mj_insetB
53 | {
54 | UIEdgeInsets inset = self.contentInset;
55 | inset.bottom = mj_insetB;
56 | #ifdef __IPHONE_11_0
57 | if (gt_ios_11_) {
58 | inset.bottom -= (self.adjustedContentInset.bottom - self.contentInset.bottom);
59 | }
60 | #endif
61 | self.contentInset = inset;
62 | }
63 |
64 | - (CGFloat)mj_insetB
65 | {
66 | return self.mj_inset.bottom;
67 | }
68 |
69 | - (void)setMj_insetL:(CGFloat)mj_insetL
70 | {
71 | UIEdgeInsets inset = self.contentInset;
72 | inset.left = mj_insetL;
73 | #ifdef __IPHONE_11_0
74 | if (gt_ios_11_) {
75 | inset.left -= (self.adjustedContentInset.left - self.contentInset.left);
76 | }
77 | #endif
78 | self.contentInset = inset;
79 | }
80 |
81 | - (CGFloat)mj_insetL
82 | {
83 | return self.mj_inset.left;
84 | }
85 |
86 | - (void)setMj_insetR:(CGFloat)mj_insetR
87 | {
88 | UIEdgeInsets inset = self.contentInset;
89 | inset.right = mj_insetR;
90 | #ifdef __IPHONE_11_0
91 | if (gt_ios_11_) {
92 | inset.right -= (self.adjustedContentInset.right - self.contentInset.right);
93 | }
94 | #endif
95 | self.contentInset = inset;
96 | }
97 |
98 | - (CGFloat)mj_insetR
99 | {
100 | return self.mj_inset.right;
101 | }
102 |
103 | - (void)setMj_offsetX:(CGFloat)mj_offsetX
104 | {
105 | CGPoint offset = self.contentOffset;
106 | offset.x = mj_offsetX;
107 | self.contentOffset = offset;
108 | }
109 |
110 | - (CGFloat)mj_offsetX
111 | {
112 | return self.contentOffset.x;
113 | }
114 |
115 | - (void)setMj_offsetY:(CGFloat)mj_offsetY
116 | {
117 | CGPoint offset = self.contentOffset;
118 | offset.y = mj_offsetY;
119 | self.contentOffset = offset;
120 | }
121 |
122 | - (CGFloat)mj_offsetY
123 | {
124 | return self.contentOffset.y;
125 | }
126 |
127 | - (void)setMj_contentW:(CGFloat)mj_contentW
128 | {
129 | CGSize size = self.contentSize;
130 | size.width = mj_contentW;
131 | self.contentSize = size;
132 | }
133 |
134 | - (CGFloat)mj_contentW
135 | {
136 | return self.contentSize.width;
137 | }
138 |
139 | - (void)setMj_contentH:(CGFloat)mj_contentH
140 | {
141 | CGSize size = self.contentSize;
142 | size.height = mj_contentH;
143 | self.contentSize = size;
144 | }
145 |
146 | - (CGFloat)mj_contentH
147 | {
148 | return self.contentSize.height;
149 | }
150 | @end
151 | #pragma clang diagnostic pop
152 |
--------------------------------------------------------------------------------
/ios/MJRefresh/UIScrollView+MJRefresh.h:
--------------------------------------------------------------------------------
1 | // 代码地址: https://github.com/CoderMJLee/MJRefresh
2 | // 代码地址: http://code4app.com/ios/%E5%BF%AB%E9%80%9F%E9%9B%86%E6%88%90%E4%B8%8B%E6%8B%89%E4%B8%8A%E6%8B%89%E5%88%B7%E6%96%B0/52326ce26803fabc46000000
3 | // UIScrollView+MJRefresh.h
4 | // MJRefreshExample
5 | //
6 | // Created by MJ Lee on 15/3/4.
7 | // Copyright (c) 2015年 小码哥. All rights reserved.
8 | // 给ScrollView增加下拉刷新、上拉刷新的功能
9 |
10 | #import
11 | #import "MJRefreshConst.h"
12 |
13 | @class MJRefreshHeader, MJRefreshFooter;
14 |
15 | @interface UIScrollView (MJRefresh)
16 | /** 下拉刷新控件 */
17 | @property (strong, nonatomic) MJRefreshHeader *mj_header;
18 | @property (strong, nonatomic) MJRefreshHeader *header MJRefreshDeprecated("使用mj_header");
19 | /** 上拉刷新控件 */
20 | @property (strong, nonatomic) MJRefreshFooter *mj_footer;
21 | @property (strong, nonatomic) MJRefreshFooter *footer MJRefreshDeprecated("使用mj_footer");
22 |
23 | #pragma mark - other
24 | - (NSInteger)mj_totalDataCount;
25 | @property (copy, nonatomic) void (^mj_reloadDataBlock)(NSInteger totalDataCount);
26 | @end
27 |
--------------------------------------------------------------------------------
/ios/MJRefresh/UIScrollView+MJRefresh.m:
--------------------------------------------------------------------------------
1 | // 代码地址: https://github.com/CoderMJLee/MJRefresh
2 | // 代码地址: http://code4app.com/ios/%E5%BF%AB%E9%80%9F%E9%9B%86%E6%88%90%E4%B8%8B%E6%8B%89%E4%B8%8A%E6%8B%89%E5%88%B7%E6%96%B0/52326ce26803fabc46000000
3 | // UIScrollView+MJRefresh.m
4 | // MJRefreshExample
5 | //
6 | // Created by MJ Lee on 15/3/4.
7 | // Copyright (c) 2015年 小码哥. All rights reserved.
8 | //
9 |
10 | #import "UIScrollView+MJRefresh.h"
11 | #import "MJRefreshHeader.h"
12 | #import "MJRefreshFooter.h"
13 | #import
14 |
15 | @implementation NSObject (MJRefresh)
16 |
17 | + (void)exchangeInstanceMethod1:(SEL)method1 method2:(SEL)method2
18 | {
19 | method_exchangeImplementations(class_getInstanceMethod(self, method1), class_getInstanceMethod(self, method2));
20 | }
21 |
22 | + (void)exchangeClassMethod1:(SEL)method1 method2:(SEL)method2
23 | {
24 | method_exchangeImplementations(class_getClassMethod(self, method1), class_getClassMethod(self, method2));
25 | }
26 |
27 | @end
28 |
29 | @implementation UIScrollView (MJRefresh)
30 |
31 | #pragma mark - header
32 | static const char MJRefreshHeaderKey = '\0';
33 | - (void)setMj_header:(MJRefreshHeader *)mj_header
34 | {
35 | if (mj_header != self.mj_header) {
36 | // 删除旧的,添加新的
37 | [self.mj_header removeFromSuperview];
38 | [self insertSubview:mj_header atIndex:0];
39 |
40 | // 存储新的
41 | objc_setAssociatedObject(self, &MJRefreshHeaderKey,
42 | mj_header, OBJC_ASSOCIATION_ASSIGN);
43 | }
44 | }
45 |
46 | - (MJRefreshHeader *)mj_header
47 | {
48 | return objc_getAssociatedObject(self, &MJRefreshHeaderKey);
49 | }
50 |
51 | #pragma mark - footer
52 | static const char MJRefreshFooterKey = '\0';
53 | - (void)setMj_footer:(MJRefreshFooter *)mj_footer
54 | {
55 | if (mj_footer != self.mj_footer) {
56 | // 删除旧的,添加新的
57 | [self.mj_footer removeFromSuperview];
58 | [self insertSubview:mj_footer atIndex:0];
59 |
60 | // 存储新的
61 | objc_setAssociatedObject(self, &MJRefreshFooterKey,
62 | mj_footer, OBJC_ASSOCIATION_ASSIGN);
63 | }
64 | }
65 |
66 | - (MJRefreshFooter *)mj_footer
67 | {
68 | return objc_getAssociatedObject(self, &MJRefreshFooterKey);
69 | }
70 |
71 | #pragma mark - 过期
72 | - (void)setFooter:(MJRefreshFooter *)footer
73 | {
74 | self.mj_footer = footer;
75 | }
76 |
77 | - (MJRefreshFooter *)footer
78 | {
79 | return self.mj_footer;
80 | }
81 |
82 | - (void)setHeader:(MJRefreshHeader *)header
83 | {
84 | self.mj_header = header;
85 | }
86 |
87 | - (MJRefreshHeader *)header
88 | {
89 | return self.mj_header;
90 | }
91 |
92 | #pragma mark - other
93 | - (NSInteger)mj_totalDataCount
94 | {
95 | NSInteger totalCount = 0;
96 | if ([self isKindOfClass:[UITableView class]]) {
97 | UITableView *tableView = (UITableView *)self;
98 |
99 | for (NSInteger section = 0; section
11 |
12 | @interface UIView (MJExtension)
13 | @property (assign, nonatomic) CGFloat mj_x;
14 | @property (assign, nonatomic) CGFloat mj_y;
15 | @property (assign, nonatomic) CGFloat mj_w;
16 | @property (assign, nonatomic) CGFloat mj_h;
17 | @property (assign, nonatomic) CGSize mj_size;
18 | @property (assign, nonatomic) CGPoint mj_origin;
19 | @end
20 |
--------------------------------------------------------------------------------
/ios/MJRefresh/UIView+MJExtension.m:
--------------------------------------------------------------------------------
1 | // 代码地址: https://github.com/CoderMJLee/MJRefresh
2 | // 代码地址: http://code4app.com/ios/%E5%BF%AB%E9%80%9F%E9%9B%86%E6%88%90%E4%B8%8B%E6%8B%89%E4%B8%8A%E6%8B%89%E5%88%B7%E6%96%B0/52326ce26803fabc46000000
3 | // UIView+Extension.m
4 | // MJRefreshExample
5 | //
6 | // Created by MJ Lee on 14-5-28.
7 | // Copyright (c) 2014年 小码哥. All rights reserved.
8 | //
9 |
10 | #import "UIView+MJExtension.h"
11 |
12 | @implementation UIView (MJExtension)
13 | - (void)setMj_x:(CGFloat)mj_x
14 | {
15 | CGRect frame = self.frame;
16 | frame.origin.x = mj_x;
17 | self.frame = frame;
18 | }
19 |
20 | - (CGFloat)mj_x
21 | {
22 | return self.frame.origin.x;
23 | }
24 |
25 | - (void)setMj_y:(CGFloat)mj_y
26 | {
27 | CGRect frame = self.frame;
28 | frame.origin.y = mj_y;
29 | self.frame = frame;
30 | }
31 |
32 | - (CGFloat)mj_y
33 | {
34 | return self.frame.origin.y;
35 | }
36 |
37 | - (void)setMj_w:(CGFloat)mj_w
38 | {
39 | CGRect frame = self.frame;
40 | frame.size.width = mj_w;
41 | self.frame = frame;
42 | }
43 |
44 | - (CGFloat)mj_w
45 | {
46 | return self.frame.size.width;
47 | }
48 |
49 | - (void)setMj_h:(CGFloat)mj_h
50 | {
51 | CGRect frame = self.frame;
52 | frame.size.height = mj_h;
53 | self.frame = frame;
54 | }
55 |
56 | - (CGFloat)mj_h
57 | {
58 | return self.frame.size.height;
59 | }
60 |
61 | - (void)setMj_size:(CGSize)mj_size
62 | {
63 | CGRect frame = self.frame;
64 | frame.size = mj_size;
65 | self.frame = frame;
66 | }
67 |
68 | - (CGSize)mj_size
69 | {
70 | return self.frame.size;
71 | }
72 |
73 | - (void)setMj_origin:(CGPoint)mj_origin
74 | {
75 | CGRect frame = self.frame;
76 | frame.origin = mj_origin;
77 | self.frame = frame;
78 | }
79 |
80 | - (CGPoint)mj_origin
81 | {
82 | return self.frame.origin;
83 | }
84 | @end
85 |
--------------------------------------------------------------------------------
/ios/RCTMJRefreshHeader/RCTMJRefreshHeader.h:
--------------------------------------------------------------------------------
1 | //
2 | // RCTMJRefreshHeader.h
3 | // RCTMJRefreshHeader
4 | //
5 | // Created by Macbook on 2018/6/25.
6 | // Copyright © 2018年 Macbook. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "MJRefresh.h"
11 |
12 | @interface RCTMJRefreshHeader : MJRefreshHeader
13 | /** 普通闲置状态 */
14 | //MJRefreshStateIdle = 1,
15 | /** 松开就可以进行刷新的状态 */
16 | //MJRefreshStatePulling,
17 | /** 正在刷新中的状态 */
18 | //MJRefreshStateRefreshing,
19 | /** 即将刷新的状态 */
20 | //MJRefreshStateWillRefresh,
21 | @property (nonatomic, copy) RCTBubblingEventBlock onMJRefreshIdle;
22 | @property (nonatomic, copy) RCTBubblingEventBlock onMJWillRefresh;
23 | @property (nonatomic, copy) RCTBubblingEventBlock onMJRefresh;
24 | @property (nonatomic, copy) RCTBubblingEventBlock onMJReleaseToRefresh;
25 | @property (nonatomic, copy) RCTBubblingEventBlock onMJPulling;
26 | @end
27 |
--------------------------------------------------------------------------------
/ios/RCTMJRefreshHeader/RCTMJRefreshHeader.m:
--------------------------------------------------------------------------------
1 | //
2 | // RCTMJRefreshHeader.m
3 | // RCTMJRefreshHeader
4 | //
5 | // Created by Macbook on 2018/6/25.
6 | // Copyright © 2018年 Macbook. All rights reserved.
7 | //
8 |
9 | #import "RCTMJRefreshHeader.h"
10 |
11 | @implementation RCTMJRefreshHeader
12 | - (void)setState:(MJRefreshState)state
13 | {
14 | MJRefreshCheckState;
15 |
16 | switch (state) {
17 | case MJRefreshStateIdle:
18 | if(self.reactTag)self.onMJRefreshIdle(@{@"target": self.reactTag});
19 | break;
20 | case MJRefreshStatePulling:
21 | if(self.reactTag)self.onMJReleaseToRefresh(@{@"target":self.reactTag});
22 | break;
23 | case MJRefreshStateRefreshing:
24 | if(self.reactTag)self.onMJRefresh(@{@"target":self.reactTag});
25 | break;
26 | default:
27 | break;
28 | }
29 | }
30 | - (void)setPullingPercent:(CGFloat)pullingPercent
31 | {
32 | [super setPullingPercent:pullingPercent];
33 | if(self.reactTag)self.onMJPulling(@{@"target":self.reactTag,@"percent":[NSNumber numberWithFloat:pullingPercent]});
34 | }
35 |
36 | @end
37 |
--------------------------------------------------------------------------------
/ios/RCTMJRefreshHeader/RCTMJRefreshViewManager.m:
--------------------------------------------------------------------------------
1 | //
2 | // RCTMJRefreshViewManager.m
3 | // React
4 | //
5 | // Created by Macbook on 2018/6/21.
6 | // Copyright © 2018年 Facebook. All rights reserved.
7 | //
8 | #import "MJRefresh.h"
9 | #import
10 | #import
11 | #import "RCTMJRefreshHeader.h"
12 | #import
13 | #import
14 | #import
15 |
16 | @interface RCTMJRefreshViewManager:RCTViewManager
17 | @end
18 |
19 | @implementation RCTMJRefreshViewManager
20 | {
21 | RCTMJRefreshHeader *header;
22 | }
23 |
24 | RCT_EXPORT_MODULE()
25 |
26 | -(UIView *)view
27 | {
28 | header=[RCTMJRefreshHeader headerWithRefreshingTarget:self refreshingAction:@selector(loadNewData)];
29 | return header;
30 | }
31 | - (NSArray *)customDirectEventTypes
32 | {
33 | return @[
34 | @"onMJRefresh",
35 | @"onMJReleaseToRefresh",
36 | @"onMJRefreshIdle",
37 | @"onMJPulling"
38 | ];
39 | }
40 | RCT_EXPORT_VIEW_PROPERTY(onMJRefresh, RCTBubblingEventBlock);
41 | RCT_EXPORT_VIEW_PROPERTY(onMJRefreshIdle, RCTBubblingEventBlock);
42 | RCT_EXPORT_VIEW_PROPERTY(onMJReleaseToRefresh, RCTBubblingEventBlock);
43 | RCT_EXPORT_VIEW_PROPERTY(onMJPulling, RCTBubblingEventBlock);
44 |
45 | RCT_EXPORT_METHOD(finishRefresh:(nonnull NSNumber *)reactTag)
46 | {
47 | [self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary *viewRegistry) {
48 | RCTMJRefreshHeader *view = viewRegistry[reactTag];
49 | if (![view isKindOfClass:[RCTMJRefreshHeader class]]) {
50 | RCTLogError(@"Invalid view returned from registry, expecting RCTBarrage, got: %@", view);
51 | } else {
52 | [view endRefreshing];
53 | }
54 | }];
55 | }
56 | RCT_EXPORT_METHOD(beginRefresh:(nonnull NSNumber *)reactTag)
57 | {
58 | [self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary *viewRegistry) {
59 | RCTMJRefreshHeader *view = viewRegistry[reactTag];
60 | if (![view isKindOfClass:[RCTMJRefreshHeader class]]) {
61 | RCTLogError(@"Invalid view returned from registry, expecting RCTBarrage, got: %@", view);
62 | } else {
63 | [view beginRefreshing];
64 | }
65 | }];
66 | }
67 | -(void)loadNewData
68 | {
69 |
70 | }
71 | - (dispatch_queue_t)methodQueue
72 | {
73 | return dispatch_get_main_queue();
74 | }
75 | @end
76 |
--------------------------------------------------------------------------------
/ios/RCTMJRefreshHeader/RCTMJScrollContentShadowView.h:
--------------------------------------------------------------------------------
1 | //
2 | // RCTMJScrollContentShadowView.h
3 | // RCTMJRefreshHeader
4 | //
5 | // Created by Macbook on 2018/7/6.
6 | // Copyright © 2018年 Macbook. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | #import
12 |
13 | @interface RCTMJScrollContentShadowView : RCTShadowView
14 |
15 | @end
16 |
--------------------------------------------------------------------------------
/ios/RCTMJRefreshHeader/RCTMJScrollContentShadowView.m:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2015-present, Facebook, Inc.
3 | *
4 | * This source code is licensed under the MIT license found in the
5 | * LICENSE file in the root directory of this source tree.
6 | */
7 |
8 | #import "RCTMJScrollContentShadowView.h"
9 |
10 | #import
11 |
12 | #import
13 |
14 | @implementation RCTMJScrollContentShadowView
15 |
16 | - (void)layoutWithMetrics:(RCTLayoutMetrics)layoutMetrics
17 | layoutContext:(RCTLayoutContext)layoutContext
18 | {
19 | if (layoutMetrics.layoutDirection == UIUserInterfaceLayoutDirectionRightToLeft) {
20 | // Motivation:
21 | // Yoga place `contentView` on the right side of `scrollView` when RTL layout is enfoced.
22 | // That breaks everything; it is completely pointless to (re)position `contentView`
23 | // because it is `contentView`'s job. So, we work around it here.
24 |
25 | layoutContext.absolutePosition.x += layoutMetrics.frame.size.width;
26 | layoutMetrics.frame.origin.x = 0;
27 | }
28 |
29 | [super layoutWithMetrics:layoutMetrics layoutContext:layoutContext];
30 | }
31 |
32 | @end
33 |
--------------------------------------------------------------------------------
/ios/RCTMJRefreshHeader/RCTMJScrollContentView.h:
--------------------------------------------------------------------------------
1 | //
2 | // RCTMJScrollContentView.h
3 | // RCTMJRefreshHeader
4 | //
5 | // Created by Macbook on 2018/7/6.
6 | // Copyright © 2018年 Macbook. All rights reserved.
7 | //
8 | #import
9 | @interface RCTMJScrollContentView : RCTView
10 |
11 | @end
12 |
13 |
--------------------------------------------------------------------------------
/ios/RCTMJRefreshHeader/RCTMJScrollContentView.m:
--------------------------------------------------------------------------------
1 | //
2 | // RCTMJScrollContentView.m
3 | // RCTMJRefreshHeader
4 | //
5 | // Created by Macbook on 2018/7/6.
6 | // Copyright © 2018年 Macbook. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 | #import
12 | #import
13 |
14 | #import "RCTMJScrollView.h"
15 | #import "RCTMJScrollContentView.h"
16 |
17 |
18 | @implementation RCTMJScrollContentView
19 |
20 | - (void)reactSetFrame:(CGRect)frame
21 | {
22 | [super reactSetFrame:frame];
23 |
24 | RCTMJScrollView *scrollView = (RCTMJScrollView *)self.superview.superview;
25 |
26 | if (!scrollView) {
27 | return;
28 | }
29 |
30 | RCTAssert([scrollView isKindOfClass:[RCTMJScrollView class]],
31 | @"Unexpected view hierarchy of RCTScrollView component.");
32 |
33 | [scrollView updateContentOffsetIfNeeded];
34 | }
35 |
36 | @end
37 |
--------------------------------------------------------------------------------
/ios/RCTMJRefreshHeader/RCTMJScrollContentViewMananger.m:
--------------------------------------------------------------------------------
1 | //
2 | // RCTMJScrollContentViewMananger.m
3 | // RCTMJRefreshHeader
4 | //
5 | // Created by Macbook on 2018/7/6.
6 | // Copyright © 2018年 Macbook. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 |
12 | #import "RCTMJScrollContentShadowView.h"
13 | #import "RCTMJScrollContentView.h"
14 |
15 | @interface RCTMJScrollContentViewManager : RCTViewManager
16 |
17 | @end
18 |
19 |
20 | @implementation RCTMJScrollContentViewManager
21 |
22 | RCT_EXPORT_MODULE()
23 |
24 | - (RCTMJScrollContentView *)view
25 | {
26 | return [RCTMJScrollContentView new];
27 | }
28 |
29 | - (RCTShadowView *)shadowView
30 | {
31 | return [RCTMJScrollContentShadowView new];
32 | }
33 |
34 | @end
35 |
--------------------------------------------------------------------------------
/ios/RCTMJRefreshHeader/RCTMJScrollView.h:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2015-present, Facebook, Inc.
3 | *
4 | * This source code is licensed under the MIT license found in the
5 | * LICENSE file in the root directory of this source tree.
6 | */
7 |
8 | #import
9 |
10 | #import
11 | #import
12 | #import
13 | #import
14 |
15 | @protocol UIScrollViewDelegate;
16 |
17 | @interface RCTMJScrollView : RCTView
18 |
19 | - (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher NS_DESIGNATED_INITIALIZER;
20 |
21 | /**
22 | * The `RCTScrollView` may have at most one single subview. This will ensure
23 | * that the scroll view's `contentSize` will be efficiently set to the size of
24 | * the single subview's frame. That frame size will be determined somewhat
25 | * efficiently since it will have already been computed by the off-main-thread
26 | * layout system.
27 | */
28 | @property (nonatomic, readonly) UIView *contentView;
29 |
30 | /**
31 | * If the `contentSize` is not specified (or is specified as {0, 0}, then the
32 | * `contentSize` will automatically be determined by the size of the subview.
33 | */
34 | @property (nonatomic, assign) CGSize contentSize;
35 |
36 | /**
37 | * The underlying scrollView (TODO: can we remove this?)
38 | */
39 | @property (nonatomic, readonly) UIScrollView *scrollView;
40 |
41 | @property (nonatomic, assign) UIEdgeInsets contentInset;
42 | @property (nonatomic, assign) BOOL automaticallyAdjustContentInsets;
43 | @property (nonatomic, assign) BOOL DEPRECATED_sendUpdatedChildFrames;
44 | @property (nonatomic, assign) NSTimeInterval scrollEventThrottle;
45 | @property (nonatomic, assign) BOOL centerContent;
46 | @property (nonatomic, copy) NSDictionary *maintainVisibleContentPosition;
47 | @property (nonatomic, assign) int snapToInterval;
48 | @property (nonatomic, copy) NSString *snapToAlignment;
49 |
50 | // NOTE: currently these event props are only declared so we can export the
51 | // event names to JS - we don't call the blocks directly because scroll events
52 | // need to be coalesced before sending, for performance reasons.
53 | @property (nonatomic, copy) RCTDirectEventBlock onScrollBeginDrag;
54 | @property (nonatomic, copy) RCTDirectEventBlock onScroll;
55 | @property (nonatomic, copy) RCTDirectEventBlock onScrollEndDrag;
56 | @property (nonatomic, copy) RCTDirectEventBlock onMomentumScrollBegin;
57 | @property (nonatomic, copy) RCTDirectEventBlock onMomentumScrollEnd;
58 |
59 | @end
60 |
61 | @interface RCTMJScrollView (Internal)
62 |
63 | - (void)updateContentOffsetIfNeeded;
64 |
65 | @end
66 |
67 | @interface RCTEventDispatcher (RCTMJScrollView)
68 |
69 | /**
70 | * Send a fake scroll event.
71 | */
72 | - (void)sendFakeScrollEvent:(NSNumber *)reactTag;
73 |
74 | @end
75 |
76 |
--------------------------------------------------------------------------------
/ios/RCTMJRefreshHeader/RCTMJScrollViewManager.h:
--------------------------------------------------------------------------------
1 | #import
2 | #import
3 |
4 | @interface RCTConvert (UIScrollView)
5 |
6 | + (UIScrollViewKeyboardDismissMode)UIScrollViewKeyboardDismissMode:(id)json;
7 |
8 | @end
9 |
10 | @interface RCTMJScrollViewManager : RCTViewManager
11 |
12 | @end
13 |
--------------------------------------------------------------------------------
/ios/RCTMJRefreshHeader/RCTMJScrollViewManager.m:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2015-present, Facebook, Inc.
3 | *
4 | * This source code is licensed under the MIT license found in the
5 | * LICENSE file in the root directory of this source tree.
6 | */
7 |
8 | #import "RCTMJScrollViewManager.h"
9 |
10 | #import
11 | #import "RCTMJScrollView.h"
12 | #import
13 | #import
14 |
15 | @interface RCTMJScrollView (Private1)
16 |
17 | - (NSArray *)calculateChildFramesData;
18 |
19 | @end
20 |
21 | @implementation RCTConvert (UIScrollView)
22 |
23 | RCT_ENUM_CONVERTER(UIScrollViewKeyboardDismissMode, (@{
24 | @"none": @(UIScrollViewKeyboardDismissModeNone),
25 | @"on-drag": @(UIScrollViewKeyboardDismissModeOnDrag),
26 | @"interactive": @(UIScrollViewKeyboardDismissModeInteractive),
27 | // Backwards compatibility
28 | @"onDrag": @(UIScrollViewKeyboardDismissModeOnDrag),
29 | }), UIScrollViewKeyboardDismissModeNone, integerValue)
30 |
31 | RCT_ENUM_CONVERTER(UIScrollViewIndicatorStyle, (@{
32 | @"default": @(UIScrollViewIndicatorStyleDefault),
33 | @"black": @(UIScrollViewIndicatorStyleBlack),
34 | @"white": @(UIScrollViewIndicatorStyleWhite),
35 | }), UIScrollViewIndicatorStyleDefault, integerValue)
36 |
37 | #if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000 /* __IPHONE_11_0 */
38 | RCT_ENUM_CONVERTER(UIScrollViewContentInsetAdjustmentBehavior, (@{
39 | @"automatic": @(UIScrollViewContentInsetAdjustmentAutomatic),
40 | @"scrollableAxes": @(UIScrollViewContentInsetAdjustmentScrollableAxes),
41 | @"never": @(UIScrollViewContentInsetAdjustmentNever),
42 | @"always": @(UIScrollViewContentInsetAdjustmentAlways),
43 | }), UIScrollViewContentInsetAdjustmentNever, integerValue)
44 | #endif
45 |
46 | @end
47 |
48 | @implementation RCTMJScrollViewManager
49 |
50 | RCT_EXPORT_MODULE()
51 |
52 | - (UIView *)view
53 | {
54 | return [[RCTMJScrollView alloc] initWithEventDispatcher:self.bridge.eventDispatcher];
55 | }
56 |
57 | RCT_EXPORT_VIEW_PROPERTY(alwaysBounceHorizontal, BOOL)
58 | RCT_EXPORT_VIEW_PROPERTY(alwaysBounceVertical, BOOL)
59 | RCT_EXPORT_VIEW_PROPERTY(bounces, BOOL)
60 | RCT_EXPORT_VIEW_PROPERTY(bouncesZoom, BOOL)
61 | RCT_EXPORT_VIEW_PROPERTY(canCancelContentTouches, BOOL)
62 | RCT_EXPORT_VIEW_PROPERTY(centerContent, BOOL)
63 | RCT_EXPORT_VIEW_PROPERTY(maintainVisibleContentPosition, NSDictionary)
64 | RCT_EXPORT_VIEW_PROPERTY(automaticallyAdjustContentInsets, BOOL)
65 | RCT_EXPORT_VIEW_PROPERTY(decelerationRate, CGFloat)
66 | RCT_EXPORT_VIEW_PROPERTY(directionalLockEnabled, BOOL)
67 | RCT_EXPORT_VIEW_PROPERTY(indicatorStyle, UIScrollViewIndicatorStyle)
68 | RCT_EXPORT_VIEW_PROPERTY(keyboardDismissMode, UIScrollViewKeyboardDismissMode)
69 | RCT_EXPORT_VIEW_PROPERTY(maximumZoomScale, CGFloat)
70 | RCT_EXPORT_VIEW_PROPERTY(minimumZoomScale, CGFloat)
71 | RCT_EXPORT_VIEW_PROPERTY(scrollEnabled, BOOL)
72 | #if !TARGET_OS_TV
73 | RCT_EXPORT_VIEW_PROPERTY(pagingEnabled, BOOL)
74 | RCT_REMAP_VIEW_PROPERTY(pinchGestureEnabled, scrollView.pinchGestureEnabled, BOOL)
75 | RCT_EXPORT_VIEW_PROPERTY(scrollsToTop, BOOL)
76 | #endif
77 | RCT_EXPORT_VIEW_PROPERTY(showsHorizontalScrollIndicator, BOOL)
78 | RCT_EXPORT_VIEW_PROPERTY(showsVerticalScrollIndicator, BOOL)
79 | RCT_EXPORT_VIEW_PROPERTY(scrollEventThrottle, NSTimeInterval)
80 | RCT_EXPORT_VIEW_PROPERTY(zoomScale, CGFloat)
81 | RCT_EXPORT_VIEW_PROPERTY(contentInset, UIEdgeInsets)
82 | RCT_EXPORT_VIEW_PROPERTY(scrollIndicatorInsets, UIEdgeInsets)
83 | RCT_EXPORT_VIEW_PROPERTY(snapToInterval, int)
84 | RCT_EXPORT_VIEW_PROPERTY(snapToAlignment, NSString)
85 | RCT_REMAP_VIEW_PROPERTY(contentOffset, scrollView.contentOffset, CGPoint)
86 | RCT_EXPORT_VIEW_PROPERTY(onScrollBeginDrag, RCTDirectEventBlock)
87 | RCT_EXPORT_VIEW_PROPERTY(onScroll, RCTDirectEventBlock)
88 | RCT_EXPORT_VIEW_PROPERTY(onScrollEndDrag, RCTDirectEventBlock)
89 | RCT_EXPORT_VIEW_PROPERTY(onMomentumScrollBegin, RCTDirectEventBlock)
90 | RCT_EXPORT_VIEW_PROPERTY(onMomentumScrollEnd, RCTDirectEventBlock)
91 | RCT_EXPORT_VIEW_PROPERTY(DEPRECATED_sendUpdatedChildFrames, BOOL)
92 | #if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000 /* __IPHONE_11_0 */
93 | RCT_EXPORT_VIEW_PROPERTY(contentInsetAdjustmentBehavior, UIScrollViewContentInsetAdjustmentBehavior)
94 | #endif
95 |
96 | // overflow is used both in css-layout as well as by react-native. In css-layout
97 | // we always want to treat overflow as scroll but depending on what the overflow
98 | // is set to from js we want to clip drawing or not. This piece of code ensures
99 | // that css-layout is always treating the contents of a scroll container as
100 | // overflow: 'scroll'.
101 | RCT_CUSTOM_SHADOW_PROPERTY(overflow, YGOverflow, RCTShadowView) {
102 | #pragma unused (json)
103 | view.overflow = YGOverflowScroll;
104 | }
105 |
106 | RCT_EXPORT_METHOD(getContentSize:(nonnull NSNumber *)reactTag
107 | callback:(RCTResponseSenderBlock)callback)
108 | {
109 | [self.bridge.uiManager addUIBlock:
110 | ^(__unused RCTUIManager *uiManager, NSDictionary *viewRegistry) {
111 |
112 | RCTMJScrollView *view = viewRegistry[reactTag];
113 | if (!view || ![view isKindOfClass:[RCTMJScrollView class]]) {
114 | RCTLogError(@"Cannot find RCTScrollView with tag #%@", reactTag);
115 | return;
116 | }
117 |
118 | CGSize size = view.scrollView.contentSize;
119 | callback(@[@{
120 | @"width" : @(size.width),
121 | @"height" : @(size.height)
122 | }]);
123 | }];
124 | }
125 |
126 | RCT_EXPORT_METHOD(calculateChildFrames:(nonnull NSNumber *)reactTag
127 | callback:(RCTResponseSenderBlock)callback)
128 | {
129 | [self.bridge.uiManager addUIBlock:
130 | ^(__unused RCTUIManager *uiManager, NSDictionary *viewRegistry) {
131 |
132 | RCTMJScrollView *view = viewRegistry[reactTag];
133 | if (!view || ![view isKindOfClass:[RCTMJScrollView class]]) {
134 | RCTLogError(@"Cannot find RCTScrollView with tag #%@", reactTag);
135 | return;
136 | }
137 |
138 | NSArray *childFrames = [view calculateChildFramesData];
139 | if (childFrames) {
140 | callback(@[childFrames]);
141 | }
142 | }];
143 | }
144 |
145 | RCT_EXPORT_METHOD(scrollTo:(nonnull NSNumber *)reactTag
146 | offsetX:(CGFloat)x
147 | offsetY:(CGFloat)y
148 | animated:(BOOL)animated)
149 | {
150 | [self.bridge.uiManager addUIBlock:
151 | ^(__unused RCTUIManager *uiManager, NSDictionary *viewRegistry){
152 | UIView *view = viewRegistry[reactTag];
153 | if ([view conformsToProtocol:@protocol(RCTScrollableProtocol)]) {
154 | [(id)view scrollToOffset:(CGPoint){x, y} animated:animated];
155 | } else {
156 | RCTLogError(@"tried to scrollTo: on non-RCTScrollableProtocol view %@ "
157 | "with tag #%@", view, reactTag);
158 | }
159 | }];
160 | }
161 |
162 | RCT_EXPORT_METHOD(scrollToEnd:(nonnull NSNumber *)reactTag
163 | animated:(BOOL)animated)
164 | {
165 | [self.bridge.uiManager addUIBlock:
166 | ^(__unused RCTUIManager *uiManager, NSDictionary *viewRegistry){
167 | UIView *view = viewRegistry[reactTag];
168 | if ([view conformsToProtocol:@protocol(RCTScrollableProtocol)]) {
169 | [(id)view scrollToEnd:animated];
170 | } else {
171 | RCTLogError(@"tried to scrollTo: on non-RCTScrollableProtocol view %@ "
172 | "with tag #%@", view, reactTag);
173 | }
174 | }];
175 | }
176 |
177 | RCT_EXPORT_METHOD(zoomToRect:(nonnull NSNumber *)reactTag
178 | withRect:(CGRect)rect
179 | animated:(BOOL)animated)
180 | {
181 | [self.bridge.uiManager addUIBlock:
182 | ^(__unused RCTUIManager *uiManager, NSDictionary *viewRegistry){
183 | UIView *view = viewRegistry[reactTag];
184 | if ([view conformsToProtocol:@protocol(RCTScrollableProtocol)]) {
185 | [(id)view zoomToRect:rect animated:animated];
186 | } else {
187 | RCTLogError(@"tried to zoomToRect: on non-RCTScrollableProtocol view %@ "
188 | "with tag #%@", view, reactTag);
189 | }
190 | }];
191 | }
192 |
193 | RCT_EXPORT_METHOD(flashScrollIndicators:(nonnull NSNumber *)reactTag)
194 | {
195 | [self.bridge.uiManager addUIBlock:
196 | ^(__unused RCTUIManager *uiManager, NSDictionary *viewRegistry){
197 |
198 | RCTMJScrollView *view = viewRegistry[reactTag];
199 | if (!view || ![view isKindOfClass:[RCTMJScrollView class]]) {
200 | RCTLogError(@"Cannot find RCTScrollView with tag #%@", reactTag);
201 | return;
202 | }
203 |
204 | [view.scrollView flashScrollIndicators];
205 | }];
206 | }
207 |
208 | @end
209 |
210 |
--------------------------------------------------------------------------------
/ios/RCTMJRefreshHeader/UIView+Private1.h:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2015-present, Facebook, Inc.
3 | *
4 | * This source code is licensed under the MIT license found in the
5 | * LICENSE file in the root directory of this source tree.
6 | */
7 |
8 | #import
9 |
10 | @interface UIView (Private1)
11 |
12 | // remove clipped subviews implementation
13 | - (void)react_remountAllSubviews;
14 | - (void)react_updateClippedSubviewsWithClipRect:(CGRect)clipRect relativeToView:(UIView *)clipView;
15 | - (UIView *)react_findClipView;
16 |
17 | @end
18 |
19 |
--------------------------------------------------------------------------------
/ios/RefreshControlEnrichment.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | B3E7B58A1CC2AC0600A0062D /* RefreshControlEnrichment.m in Sources */ = {isa = PBXBuildFile; fileRef = B3E7B5891CC2AC0600A0062D /* RefreshControlEnrichment.m */; };
11 | /* End PBXBuildFile section */
12 |
13 | /* Begin PBXCopyFilesBuildPhase section */
14 | 58B511D91A9E6C8500147676 /* CopyFiles */ = {
15 | isa = PBXCopyFilesBuildPhase;
16 | buildActionMask = 2147483647;
17 | dstPath = "include/$(PRODUCT_NAME)";
18 | dstSubfolderSpec = 16;
19 | files = (
20 | );
21 | runOnlyForDeploymentPostprocessing = 0;
22 | };
23 | /* End PBXCopyFilesBuildPhase section */
24 |
25 | /* Begin PBXFileReference section */
26 | 134814201AA4EA6300B7C361 /* libRefreshControlEnrichment.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRefreshControlEnrichment.a; sourceTree = BUILT_PRODUCTS_DIR; };
27 | B3E7B5881CC2AC0600A0062D /* RefreshControlEnrichment.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RefreshControlEnrichment.h; sourceTree = ""; };
28 | B3E7B5891CC2AC0600A0062D /* RefreshControlEnrichment.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RefreshControlEnrichment.m; sourceTree = ""; };
29 | /* End PBXFileReference section */
30 |
31 | /* Begin PBXFrameworksBuildPhase section */
32 | 58B511D81A9E6C8500147676 /* Frameworks */ = {
33 | isa = PBXFrameworksBuildPhase;
34 | buildActionMask = 2147483647;
35 | files = (
36 | );
37 | runOnlyForDeploymentPostprocessing = 0;
38 | };
39 | /* End PBXFrameworksBuildPhase section */
40 |
41 | /* Begin PBXGroup section */
42 | 134814211AA4EA7D00B7C361 /* Products */ = {
43 | isa = PBXGroup;
44 | children = (
45 | 134814201AA4EA6300B7C361 /* libRefreshControlEnrichment.a */,
46 | );
47 | name = Products;
48 | sourceTree = "";
49 | };
50 | 58B511D21A9E6C8500147676 = {
51 | isa = PBXGroup;
52 | children = (
53 | B3E7B5881CC2AC0600A0062D /* RefreshControlEnrichment.h */,
54 | B3E7B5891CC2AC0600A0062D /* RefreshControlEnrichment.m */,
55 | 134814211AA4EA7D00B7C361 /* Products */,
56 | );
57 | sourceTree = "";
58 | };
59 | /* End PBXGroup section */
60 |
61 | /* Begin PBXNativeTarget section */
62 | 58B511DA1A9E6C8500147676 /* RefreshControlEnrichment */ = {
63 | isa = PBXNativeTarget;
64 | buildConfigurationList = 58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "RefreshControlEnrichment" */;
65 | buildPhases = (
66 | 58B511D71A9E6C8500147676 /* Sources */,
67 | 58B511D81A9E6C8500147676 /* Frameworks */,
68 | 58B511D91A9E6C8500147676 /* CopyFiles */,
69 | );
70 | buildRules = (
71 | );
72 | dependencies = (
73 | );
74 | name = RefreshControlEnrichment;
75 | productName = RCTDataManager;
76 | productReference = 134814201AA4EA6300B7C361 /* libRefreshControlEnrichment.a */;
77 | productType = "com.apple.product-type.library.static";
78 | };
79 | /* End PBXNativeTarget section */
80 |
81 | /* Begin PBXProject section */
82 | 58B511D31A9E6C8500147676 /* Project object */ = {
83 | isa = PBXProject;
84 | attributes = {
85 | LastUpgradeCheck = 0920;
86 | ORGANIZATIONNAME = Facebook;
87 | TargetAttributes = {
88 | 58B511DA1A9E6C8500147676 = {
89 | CreatedOnToolsVersion = 6.1.1;
90 | };
91 | };
92 | };
93 | buildConfigurationList = 58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "RefreshControlEnrichment" */;
94 | compatibilityVersion = "Xcode 3.2";
95 | developmentRegion = English;
96 | hasScannedForEncodings = 0;
97 | knownRegions = (
98 | en,
99 | );
100 | mainGroup = 58B511D21A9E6C8500147676;
101 | productRefGroup = 58B511D21A9E6C8500147676;
102 | projectDirPath = "";
103 | projectRoot = "";
104 | targets = (
105 | 58B511DA1A9E6C8500147676 /* RefreshControlEnrichment */,
106 | );
107 | };
108 | /* End PBXProject section */
109 |
110 | /* Begin PBXSourcesBuildPhase section */
111 | 58B511D71A9E6C8500147676 /* Sources */ = {
112 | isa = PBXSourcesBuildPhase;
113 | buildActionMask = 2147483647;
114 | files = (
115 | B3E7B58A1CC2AC0600A0062D /* RefreshControlEnrichment.m in Sources */,
116 | );
117 | runOnlyForDeploymentPostprocessing = 0;
118 | };
119 | /* End PBXSourcesBuildPhase section */
120 |
121 | /* Begin XCBuildConfiguration section */
122 | 58B511ED1A9E6C8500147676 /* Debug */ = {
123 | isa = XCBuildConfiguration;
124 | buildSettings = {
125 | ALWAYS_SEARCH_USER_PATHS = NO;
126 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
127 | CLANG_CXX_LIBRARY = "libc++";
128 | CLANG_ENABLE_MODULES = YES;
129 | CLANG_ENABLE_OBJC_ARC = YES;
130 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
131 | CLANG_WARN_BOOL_CONVERSION = YES;
132 | CLANG_WARN_COMMA = YES;
133 | CLANG_WARN_CONSTANT_CONVERSION = YES;
134 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
135 | CLANG_WARN_EMPTY_BODY = YES;
136 | CLANG_WARN_ENUM_CONVERSION = YES;
137 | CLANG_WARN_INFINITE_RECURSION = YES;
138 | CLANG_WARN_INT_CONVERSION = YES;
139 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
140 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
141 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
142 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
143 | CLANG_WARN_STRICT_PROTOTYPES = YES;
144 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
145 | CLANG_WARN_UNREACHABLE_CODE = YES;
146 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
147 | COPY_PHASE_STRIP = NO;
148 | ENABLE_STRICT_OBJC_MSGSEND = YES;
149 | ENABLE_TESTABILITY = YES;
150 | GCC_C_LANGUAGE_STANDARD = gnu99;
151 | GCC_DYNAMIC_NO_PIC = NO;
152 | GCC_NO_COMMON_BLOCKS = YES;
153 | GCC_OPTIMIZATION_LEVEL = 0;
154 | GCC_PREPROCESSOR_DEFINITIONS = (
155 | "DEBUG=1",
156 | "$(inherited)",
157 | );
158 | GCC_SYMBOLS_PRIVATE_EXTERN = NO;
159 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
160 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
161 | GCC_WARN_UNDECLARED_SELECTOR = YES;
162 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
163 | GCC_WARN_UNUSED_FUNCTION = YES;
164 | GCC_WARN_UNUSED_VARIABLE = YES;
165 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
166 | MTL_ENABLE_DEBUG_INFO = YES;
167 | ONLY_ACTIVE_ARCH = YES;
168 | SDKROOT = iphoneos;
169 | };
170 | name = Debug;
171 | };
172 | 58B511EE1A9E6C8500147676 /* Release */ = {
173 | isa = XCBuildConfiguration;
174 | buildSettings = {
175 | ALWAYS_SEARCH_USER_PATHS = NO;
176 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
177 | CLANG_CXX_LIBRARY = "libc++";
178 | CLANG_ENABLE_MODULES = YES;
179 | CLANG_ENABLE_OBJC_ARC = YES;
180 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
181 | CLANG_WARN_BOOL_CONVERSION = YES;
182 | CLANG_WARN_COMMA = YES;
183 | CLANG_WARN_CONSTANT_CONVERSION = YES;
184 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
185 | CLANG_WARN_EMPTY_BODY = YES;
186 | CLANG_WARN_ENUM_CONVERSION = YES;
187 | CLANG_WARN_INFINITE_RECURSION = YES;
188 | CLANG_WARN_INT_CONVERSION = YES;
189 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
190 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
191 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
192 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
193 | CLANG_WARN_STRICT_PROTOTYPES = YES;
194 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
195 | CLANG_WARN_UNREACHABLE_CODE = YES;
196 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
197 | COPY_PHASE_STRIP = YES;
198 | ENABLE_NS_ASSERTIONS = NO;
199 | ENABLE_STRICT_OBJC_MSGSEND = YES;
200 | GCC_C_LANGUAGE_STANDARD = gnu99;
201 | GCC_NO_COMMON_BLOCKS = YES;
202 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
203 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
204 | GCC_WARN_UNDECLARED_SELECTOR = YES;
205 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
206 | GCC_WARN_UNUSED_FUNCTION = YES;
207 | GCC_WARN_UNUSED_VARIABLE = YES;
208 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
209 | MTL_ENABLE_DEBUG_INFO = NO;
210 | SDKROOT = iphoneos;
211 | VALIDATE_PRODUCT = YES;
212 | };
213 | name = Release;
214 | };
215 | 58B511F01A9E6C8500147676 /* Debug */ = {
216 | isa = XCBuildConfiguration;
217 | buildSettings = {
218 | HEADER_SEARCH_PATHS = (
219 | "$(inherited)",
220 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
221 | "$(SRCROOT)/../../../React/**",
222 | "$(SRCROOT)/../../react-native/React/**",
223 | );
224 | LIBRARY_SEARCH_PATHS = "$(inherited)";
225 | OTHER_LDFLAGS = "-ObjC";
226 | PRODUCT_NAME = RefreshControlEnrichment;
227 | SKIP_INSTALL = YES;
228 | };
229 | name = Debug;
230 | };
231 | 58B511F11A9E6C8500147676 /* Release */ = {
232 | isa = XCBuildConfiguration;
233 | buildSettings = {
234 | HEADER_SEARCH_PATHS = (
235 | "$(inherited)",
236 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
237 | "$(SRCROOT)/../../../React/**",
238 | "$(SRCROOT)/../../react-native/React/**",
239 | );
240 | LIBRARY_SEARCH_PATHS = "$(inherited)";
241 | OTHER_LDFLAGS = "-ObjC";
242 | PRODUCT_NAME = RefreshControlEnrichment;
243 | SKIP_INSTALL = YES;
244 | };
245 | name = Release;
246 | };
247 | /* End XCBuildConfiguration section */
248 |
249 | /* Begin XCConfigurationList section */
250 | 58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "RefreshControlEnrichment" */ = {
251 | isa = XCConfigurationList;
252 | buildConfigurations = (
253 | 58B511ED1A9E6C8500147676 /* Debug */,
254 | 58B511EE1A9E6C8500147676 /* Release */,
255 | );
256 | defaultConfigurationIsVisible = 0;
257 | defaultConfigurationName = Release;
258 | };
259 | 58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "RefreshControlEnrichment" */ = {
260 | isa = XCConfigurationList;
261 | buildConfigurations = (
262 | 58B511F01A9E6C8500147676 /* Debug */,
263 | 58B511F11A9E6C8500147676 /* Release */,
264 | );
265 | defaultConfigurationIsVisible = 0;
266 | defaultConfigurationName = Release;
267 | };
268 | /* End XCConfigurationList section */
269 | };
270 | rootObject = 58B511D31A9E6C8500147676 /* Project object */;
271 | }
272 |
--------------------------------------------------------------------------------
/ios/RefreshControlEnrichment.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-native-refresh-control-enrichment",
3 | "title": "React Native Refresh Control Enrichment",
4 | "version": "0.2.0",
5 | "description": "原生下拉刷新组件",
6 | "main": "index.js",
7 | "scripts": {
8 | "test": "echo \"Error: no test specified\" && exit 1"
9 | },
10 | "repository": {
11 | "type": "git",
12 | "url": "git+https://github.com/gitSirzh/react-native-refresh-control-enrichment.git",
13 | "baseUrl": "https://github.com/gitSirzh/react-native-refresh-control-enrichment"
14 | },
15 | "keywords": [
16 | "react-native",
17 | "refresh",
18 | "control",
19 | "ios",
20 | "android"
21 | ],
22 | "author": {
23 | "name": "jszh",
24 | "email": "969883825@qq.com"
25 | },
26 | "license": "MIT",
27 | "licenseFilename": "LICENSE",
28 | "readmeFilename": "README.md"
29 | }
30 |
--------------------------------------------------------------------------------
/react-native-refresh-control-enrichment.podspec:
--------------------------------------------------------------------------------
1 | require "json"
2 |
3 | package = JSON.parse(File.read(File.join(__dir__, "package.json")))
4 |
5 | Pod::Spec.new do |s|
6 | s.name = "react-native-refresh-control-enrichment"
7 | s.version = package["version"]
8 | s.summary = package["description"]
9 | s.description = <<-DESC
10 | react-native-refresh-control-enrichment
11 | DESC
12 | s.homepage = "https://github.com/gitSirzh/react-native-refresh-control-enrichment"
13 | # brief license entry:
14 | s.license = "MIT"
15 | # optional - use expanded license entry instead:
16 | # s.license = { :type => "MIT", :file => "LICENSE" }
17 | s.authors = { "jszh" => "969883825@qq.com" }
18 | s.platforms = { :ios => "9.0" }
19 | s.source = { :git => "https://github.com/gitSirzh/react-native-refresh-control-enrichment.git", :tag => "#{s.version}" }
20 |
21 | s.source_files = "ios/**/*.{h,m,swift}"
22 | s.requires_arc = true
23 |
24 | s.dependency "React"
25 | # ...
26 | # s.dependency "..."
27 | end
28 |
29 |
--------------------------------------------------------------------------------
/src/androidJS/AnyHeader.js:
--------------------------------------------------------------------------------
1 | import React, {Component} from 'react';
2 | import {
3 | StyleSheet,
4 | View,
5 | Text,
6 | requireNativeComponent,
7 | findNodeHandle,
8 | UIManager,
9 | } from 'react-native';
10 | import PropTypes from 'prop-types';
11 | import {ViewPropTypes} from './Util'
12 |
13 | const RCTAnyHeader = requireNativeComponent('RCTAnyHeader', RCTAnyHeader);
14 |
15 | class AnyHeader extends Component {
16 |
17 | render() {
18 | return (
19 |
22 |
23 | )
24 | }
25 | }
26 |
27 | AnyHeader.propTypes = {
28 | primaryColor:PropTypes.string,
29 | ...ViewPropTypes,
30 | }
31 | export default AnyHeader;
--------------------------------------------------------------------------------
/src/androidJS/DefaultHeader.js:
--------------------------------------------------------------------------------
1 | import React, {Component} from 'react';
2 | import {requireNativeComponent} from 'react-native';
3 | import {ViewPropTypes} from './Util';
4 | import PropTypes from 'prop-types';
5 |
6 | const RCTDefaultHeader = requireNativeComponent('RCTDefaultHeader', RCTDefaultHeader);
7 |
8 | export default class DefaultHeader extends Component {
9 | static propTypes = {
10 | primaryColor: PropTypes.string,
11 | accentColor: PropTypes.string,
12 | ...ViewPropTypes,
13 | }
14 |
15 | render() {
16 | return ()
17 | }
18 | }
--------------------------------------------------------------------------------
/src/androidJS/SmartRefreshControl.js:
--------------------------------------------------------------------------------
1 | import React, {Component} from 'react';
2 | import {
3 | requireNativeComponent,
4 | findNodeHandle,
5 | UIManager,
6 | NativeModules,
7 | Platform,
8 | PanResponder,
9 | } from 'react-native';
10 | import {ViewPropTypes} from './Util';
11 | import DefaultHeader from './DefaultHeader';
12 | import PropTypes from 'prop-types';
13 | import processColor from 'react-native/Libraries/StyleSheet/processColor';
14 | import deprecatedPropType from 'react-native/Libraries/Utilities/deprecatedPropType';
15 |
16 | const SPModule = Platform.OS === 'android' ? NativeModules.SpinnerStyleModule : {};
17 |
18 | const SmartRefreshLayout = requireNativeComponent('SmartRefreshLayout', SmartRefreshControl);
19 |
20 | class SmartRefreshControl extends Component {
21 |
22 | constructor(props) {
23 | super(props);
24 | this._panResponder = PanResponder.create({
25 | onMoveShouldSetPanResponderCapture: (evt, gestureState) => {
26 | if (this.shiftPercent >= 0.039 || this.footerShiftPercent >= 0.068) {//满足条件捕获事件
27 | return true;
28 | }
29 | return false;
30 | },
31 | });
32 | }
33 |
34 | static constants = {
35 | 'TRANSLATE': SPModule.translate,
36 | 'SCALE': SPModule.scale,
37 | 'FIX_BEHIND': SPModule.fixBehind,
38 | 'FIX_FRONT': SPModule.fixFront,
39 | 'MATCH_LAYOUT': SPModule.matchLayout,
40 | };
41 |
42 | /**
43 | * 参数格式为{delayed:number,success:bool}
44 | * delayed:延迟刷新
45 | * success:是否刷新成功
46 | * @param params
47 | */
48 | finishRefresh = ({delayed = -1, success = true} = {delayed: -1, success: true}) => {
49 | this.dispatchCommand('finishRefresh', [delayed, success]);
50 | this.props.onSuccessToRefresh && this.props.onSuccessToRefresh()
51 | };
52 | dispatchCommand = (commandName, params) => {
53 | UIManager.dispatchViewManagerCommand(this.findNode(),
54 | (UIManager.getViewManagerConfig ? UIManager.getViewManagerConfig('SmartRefreshLayout') : UIManager.SmartRefreshLayout).Commands[commandName],
55 | params);
56 | };
57 | findNode = () => {
58 | return findNodeHandle(this.refs.refreshLayout);
59 | };
60 |
61 | shiftPercent = 0;//header位移百分比,默认为0
62 |
63 | footerShiftPercent = 0;// footer位移百分比
64 | /**
65 | * 渲染Header
66 | * @return {*}
67 | */
68 | renderHeader = () => {
69 | const {HeaderComponent, renderHeader} = this.props;
70 | if (renderHeader) {
71 | return React.isValidElement(renderHeader) ? renderHeader : renderHeader();
72 | }
73 | if (HeaderComponent) {
74 | return HeaderComponent;
75 | }
76 | return ;
77 | };
78 | /**
79 | * 刷新时触发
80 | * @private
81 | */
82 | _onSmartRefresh = () => {
83 | let {onRefresh} = this.props;
84 | onRefresh && onRefresh();
85 | };
86 | /**
87 | * 下拉过程
88 | * @param event
89 | * @private
90 | */
91 | _onHeaderPulling = (event) => {
92 | this.shiftPercent = event.nativeEvent.percent;
93 | let {onHeaderPulling, onHeaderMoving} = this.props;
94 | onHeaderMoving && onHeaderMoving(event);
95 | onHeaderPulling && onHeaderPulling(event);
96 | };
97 | /**
98 | * 释放过程
99 | * @param event
100 | * @private
101 | */
102 | _onHeaderReleasing = (event) => {
103 | this.shiftPercent = event.nativeEvent.percent;
104 | let {onHeaderReleasing, onHeaderMoving} = this.props;
105 | onHeaderMoving && onHeaderMoving(event);
106 | onHeaderReleasing && onHeaderReleasing(event);
107 | };
108 | /**
109 | * 底部位移过程
110 | * @param event
111 | * @private
112 | */
113 | _onFooterMoving = (event) => {
114 | this.footerShiftPercent = event.nativeEvent.percent;
115 | };
116 |
117 | render() {
118 | const nativeProps = {
119 | ...this.props, ...{
120 | onSmartRefresh: this._onSmartRefresh,
121 | onHeaderPulling: this._onHeaderPulling,
122 | onHeaderReleasing: this._onHeaderReleasing,
123 | onFooterMoving: this._onFooterMoving,
124 | primaryColor: processColor(this.props.primaryColor),
125 | },
126 | };
127 | return (
128 |
133 | {this.renderHeader()}
134 | {this.props.children}
135 |
136 |
137 | );
138 | }
139 | }
140 |
141 | SmartRefreshControl.propTypes = {
142 | onRefresh: PropTypes.func,
143 | onLoadMore: PropTypes.func,
144 | onHeaderPulling: PropTypes.func,
145 | onHeaderReleasing: PropTypes.func,
146 | onHeaderMoving: PropTypes.func,//向外提供的接口
147 | onPullDownToRefresh: PropTypes.func,
148 | onReleaseToRefresh: PropTypes.func,
149 | onHeaderReleased: PropTypes.func,
150 | enableRefresh: PropTypes.bool,//是否启用下拉刷新功能
151 | HeaderComponent: deprecatedPropType(PropTypes.object, 'Use the `renderHeader` prop instead.'),
152 | renderHeader: PropTypes.oneOfType([
153 | PropTypes.func,
154 | PropTypes.element,
155 | ]),
156 | headerHeight: PropTypes.number,
157 | overScrollBounce: PropTypes.bool,//是否使用越界回弹
158 | overScrollDrag: PropTypes.bool,//是否使用越界拖动,类似IOS样式
159 | pureScroll: PropTypes.bool,//是否使用纯滚动模式
160 | dragRate: PropTypes.number,// 显示下拉高度/手指真实下拉高度=阻尼效果
161 | maxDragRate: PropTypes.number,//最大显示下拉高度/Header标准高度
162 | primaryColor: PropTypes.string,
163 | autoRefresh: PropTypes.shape({
164 | refresh: PropTypes.bool,
165 | time: PropTypes.number,
166 | }),//是否启动自动刷新
167 | ...ViewPropTypes,
168 | };
169 |
170 | SmartRefreshControl.defaultProps = {
171 | overScrollBounce: false,
172 | };
173 | export default SmartRefreshControl;
174 |
--------------------------------------------------------------------------------
/src/androidJS/Util.js:
--------------------------------------------------------------------------------
1 | import {
2 | View,
3 | BackHandler,
4 | ViewPropTypes as RNViewPropTypes,
5 | BackAndroid as DeprecatedBackAndroid,
6 | } from 'react-native';
7 |
8 | export const ViewPropTypes = RNViewPropTypes || View.propTypes;
9 | export const BackAndroid = BackHandler || DeprecatedBackAndroid;
10 |
--------------------------------------------------------------------------------
/src/file/androidVideo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gitSirzh/react-native-refresh-control-enrichment/HEAD/src/file/androidVideo.gif
--------------------------------------------------------------------------------
/src/file/iosVideo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gitSirzh/react-native-refresh-control-enrichment/HEAD/src/file/iosVideo.gif
--------------------------------------------------------------------------------
/src/fusion.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by jszh on 2020/5/12.
3 | */
4 |
5 | 'use strict';
6 |
7 | import React, {Component} from 'react';
8 | import {
9 | View,
10 | Text,
11 | Platform,
12 | StyleSheet,
13 | ActivityIndicator,
14 | ImageBackground,
15 | } from 'react-native';
16 | import PropTypes from 'prop-types';
17 |
18 | import MJRefresh from './iosJS/MJRefresh';
19 | import {default as SmartRefreshControl} from './androidJS/SmartRefreshControl';
20 | import {default as AnyHeader} from './androidJS/AnyHeader';
21 | import dateFormat from './util/date';
22 |
23 | const TITLE = ['下拉刷新', '释放刷新', '正在刷新', '刷新完成'];
24 |
25 | export default class ZHRefreshControl extends Component {
26 |
27 | constructor(props) {
28 | super(props);
29 | this.state = {
30 | text: props.titleArray ? props.titleArray[0] : TITLE[0],
31 | };
32 | }
33 |
34 | finishRefresh() {
35 | if (Platform.OS === 'ios') {
36 | this._mjRefresh && this._mjRefresh.finishRefresh();
37 | } else {
38 | this.rc && this.rc.finishRefresh();
39 | }
40 | }
41 |
42 | renderHeader() {
43 | let {text} = this.state;
44 | let {
45 | loadingView,
46 | headerHeight,
47 | centerTop,
48 | headerBackgroundColor,
49 | headerBackgroundImage,
50 | pullView,
51 | releaseView,
52 | successView,
53 | headerTitleStyle,
54 | headerDateStyle,
55 | titleArray,
56 | showDate,
57 | showText,
58 | } = this.props;
59 | if (headerBackgroundImage) {
60 | return (
61 |
63 |
64 | {text === titleArray[2] ? (
65 | <>{loadingView ? (loadingView) : ()}>
66 | ) : text === titleArray[0] ? (
67 | <>{pullView ? (pullView) : {'⬇️'}}>
68 | ) : text === titleArray[1] ? (
69 | <>{releaseView ? (releaseView) : {'⬆️️️'}}>
70 | ) : (
71 | <>{successView ? (successView) : null}>
72 | )}
73 | {showText ? (
74 | {text}
75 | ) : null}
76 |
77 | {showDate ? (上次更新 {dateFormat()}) : null}
78 |
79 | );
80 | } else {
81 | return (
82 |
86 |
87 | {text === titleArray[2] ? (
88 | <>{loadingView ? (loadingView) : ()}>
89 | ) : text === titleArray[0] ? (
90 | <>{pullView ? (pullView) : {'⬇️'}}>
91 | ) : text === titleArray[1] ? (
92 | <>{releaseView ? (releaseView) : {'⬆️️️'}}>
93 | ) : (
94 | <>{successView ? (successView) : null}>
95 | )}
96 | {showText ? (
97 | {text}
98 | ) : null}
99 |
100 | {showDate ? (上次更新 {dateFormat()}) : null}
101 |
102 | );
103 | }
104 | }
105 |
106 | render() {
107 | let {onRefresh, refreshState, headerHeight, centerTop, headerBackgroundColor, titleArray} = this.props;
108 | if (Platform.OS === 'ios') {
109 | return (
110 | this._mjRefresh = ref}
113 | onPulling={e => {
114 | if (e.nativeEvent.percent < 1) {
115 | this.setState({text: titleArray[0]});
116 | refreshState && refreshState(titleArray[0]);
117 | }
118 | }}
119 | onReleaseToRefresh={() => {
120 | this.setState({text: titleArray[1]});
121 | refreshState && refreshState(titleArray[1]);
122 | }}
123 | onRefresh={() => {
124 | this.setState({text: titleArray[2]});
125 | onRefresh && onRefresh();
126 | refreshState && refreshState(titleArray[2]);
127 | }}
128 | onRefreshIdle={() => {
129 | this.setState({text: titleArray[3]});
130 | refreshState && refreshState(titleArray[3]);
131 | }}
132 | >
133 | {this.renderHeader()}
134 |
135 | );
136 | } else {
137 | return (
138 | this.rc = ref}
141 | primaryColor={headerBackgroundColor}
142 | headerHeight={headerHeight + centerTop}
143 | renderHeader={(
144 | {this.renderHeader()}
145 | )}
146 | onPullDownToRefresh={() => {
147 | this.setState({text: titleArray[0]});
148 | refreshState && refreshState(titleArray[0]);
149 | }}
150 | onReleaseToRefresh={() => {
151 | this.setState({text: titleArray[1]});
152 | refreshState && refreshState(titleArray[1]);
153 | }}
154 | onRefresh={() => {
155 | onRefresh && onRefresh();
156 | this.setState({text: titleArray[2]});
157 | refreshState && refreshState(titleArray[2]);
158 | }}
159 | onSuccessToRefresh={() => {
160 | this.setState({text: titleArray[3]});
161 | refreshState && refreshState(titleArray[3]);
162 | }}
163 | />
164 | );
165 | }
166 | }
167 |
168 | }
169 |
170 | ZHRefreshControl.defaultProps = {
171 | headerHeight: 60,
172 | centerTop: 0,
173 | headerBackgroundColor: '#ffffff',
174 | headerTitleStyle: {fontSize: 14, color: '#333333'},
175 | headerDateStyle: {fontSize: 12, color: '#333333'},
176 | titleArray: TITLE,
177 | showDate: true,
178 | showText: true,
179 | };
180 |
181 | /**
182 | * loadingView : 左边加载中View
183 | * headerHeight : 头部高度
184 | * centerTop : 内容距离顶部高度(一般为状态栏高度)
185 | * headerBackgroundColor : 头部背景色
186 | * headerBackgroundImage : 头部背景图
187 | * headerTitleStyle : 刷新状态字体样式
188 | * headerDateStyle : 更新时间字体样式
189 | * titleArray : 自定义提示状态,注:数量需要为四个
190 | * pullView : 下拉样式
191 | * releaseView : 释放样式
192 | * successView : 成功样式
193 | * showDate : 展示刷新时间,默认true:展示
194 | * showText : 展示刷新状态,默认true:展示
195 | * onRefresh : 执行刷新回调方法
196 | * refreshState : 刷新状态回调方法
197 | */
198 | ZHRefreshControl.propTypes = {
199 | loadingView: PropTypes.element,
200 | headerHeight: PropTypes.number,
201 | centerTop: PropTypes.number,
202 | headerBackgroundColor: PropTypes.string,
203 | headerBackgroundImage: PropTypes.oneOfType([
204 | PropTypes.shape({
205 | uri: PropTypes.string,
206 | }),
207 | // Opaque type returned by require('./image.jpg')
208 | PropTypes.number,
209 | ]),
210 | pullView: PropTypes.element,
211 | releaseView: PropTypes.element,
212 | successView: PropTypes.element,
213 | headerTitleStyle: PropTypes.object,
214 | headerDateStyle: PropTypes.object,
215 | titleArray: PropTypes.array,
216 | showDate: PropTypes.bool,
217 | showText: PropTypes.bool,
218 | onRefresh: PropTypes.func,
219 | refreshState: PropTypes.func,
220 | };
221 |
222 | const styles = StyleSheet.create({
223 | headView: {
224 | justifyContent: 'center',
225 | alignItems: 'center',
226 | },
227 | });
228 |
229 |
--------------------------------------------------------------------------------
/src/iosJS/MJRefresh.js:
--------------------------------------------------------------------------------
1 | import React, {Component} from 'react';
2 | import {
3 | View,
4 | requireNativeComponent,
5 | ViewPropTypes as RNViewPropTypes,
6 | findNodeHandle,
7 | UIManager,
8 | Platform,
9 | } from 'react-native';
10 | import PropTypes from 'prop-types';
11 | import MJScrollView from './MJScrollView';
12 |
13 | const ViewPropTypes = RNViewPropTypes || View.propTypes;
14 | const RCTMJRefreshView = Platform.OS === 'ios' ? requireNativeComponent('RCTMJRefreshView', MJRefresh) : null;
15 |
16 | class MJRefresh extends Component {
17 |
18 | _onMJRefresh = () => {
19 | let {onRefresh} = this.props;
20 | onRefresh && onRefresh();
21 | };
22 |
23 | _onMJPulling = (e) => {
24 | let {onPulling} = this.props;
25 | onPulling && onPulling(e);
26 | };
27 |
28 | _onMJReleaseToRefresh = () => {
29 | let {onReleaseToRefresh} = this.props;
30 | onReleaseToRefresh && onReleaseToRefresh();
31 | };
32 |
33 | _onMJRefreshIdle = () => {
34 | let {onRefreshIdle} = this.props;
35 | onRefreshIdle && onRefreshIdle();
36 | };
37 |
38 | finishRefresh = () => {
39 | this.dispatchCommand('finishRefresh');
40 | };
41 |
42 | beginRefresh = () => {
43 | this.dispatchCommand('beginRefresh');
44 | };
45 |
46 | dispatchCommand(commandName, params) {
47 | UIManager.dispatchViewManagerCommand(this.findNode(), UIManager.RCTMJRefreshView.Commands[commandName], params);
48 | }
49 |
50 | findNode = () => {
51 | return findNodeHandle(this.refs.refreshView);
52 | };
53 |
54 | render() {
55 | let {style} = this.props;
56 | return (
57 |
76 | );
77 | }
78 | }
79 |
80 | MJRefresh.propTypes = {
81 | onRefresh: PropTypes.func,
82 | onRefreshIdle: PropTypes.func,
83 | onReleaseToRefresh: PropTypes.func,
84 | onPulling: PropTypes.func,
85 | ...ViewPropTypes,
86 | };
87 |
88 | export const ZHScrollView = MJScrollView;
89 | export default MJRefresh;
90 |
--------------------------------------------------------------------------------
/src/iosJS/MJScrollView.js:
--------------------------------------------------------------------------------
1 | import React, {Component} from 'react';
2 | import {
3 | requireNativeComponent,
4 | ScrollView,
5 | ScrollViewProps,
6 | View,
7 | Platform,
8 | StyleSheet,
9 | } from 'react-native';
10 |
11 | const warning = require('fbjs/lib/warning');
12 | const flattenStyle = require('react-native/Libraries/StyleSheet/flattenStyle');
13 | const invariant = require('fbjs/lib/invariant');
14 | const ScrollViewStickyHeader = require('react-native/Libraries/Components/ScrollView/ScrollViewStickyHeader');
15 | const processDecelerationRate = require('react-native/Libraries/Components/ScrollView/processDecelerationRate');
16 | const RCTMJScrollView = Platform.OS === 'ios' ? requireNativeComponent('RCTMJScrollView', MJScrollView, {
17 | nativeOnly: {
18 | onMomentumScrollBegin: true,
19 | onMomentumScrollEnd: true,
20 | onScrollBeginDrag: true,
21 | onScrollEndDrag: true,
22 | },
23 | }) : ScrollView;
24 |
25 | const RCTMJScrollContentView = Platform.OS === 'ios' ? requireNativeComponent('RCTMJScrollContentView', View) : ScrollView;
26 |
27 | class MJScrollView extends Component {
28 | render() {
29 | let ScrollViewClass = RCTMJScrollView;
30 | let ScrollContentContainerViewClass = RCTMJScrollContentView;
31 | warning(
32 | !this.props.snapToInterval || !this.props.pagingEnabled,
33 | 'snapToInterval is currently ignored when pagingEnabled is true.',
34 | );
35 | invariant(
36 | ScrollViewClass !== undefined,
37 | 'ScrollViewClass must not be undefined',
38 | );
39 | invariant(
40 | ScrollContentContainerViewClass !== undefined,
41 | 'ScrollContentContainerViewClass must not be undefined',
42 | );
43 | const contentContainerStyle = [
44 | this.props.horizontal && styles.contentContainerHorizontal,
45 | this.props.contentContainerStyle,
46 | ];
47 | let style, childLayoutProps;
48 | if (__DEV__ && this.props.style) {
49 | style = flattenStyle(this.props.style);
50 | childLayoutProps = ['alignItems', 'justifyContent']
51 | .filter((prop) => style && style[prop] !== undefined);
52 | invariant(
53 | childLayoutProps.length === 0,
54 | 'ScrollView child layout (' + JSON.stringify(childLayoutProps) +
55 | ') must be applied through the contentContainerStyle prop.',
56 | );
57 | }
58 |
59 | let contentSizeChangeProps = {};
60 | if (this.props.onContentSizeChange) {
61 | contentSizeChangeProps = {
62 | onLayout: this._handleContentOnLayout,
63 | };
64 | }
65 |
66 | const {stickyHeaderIndices} = this.props;
67 | const hasStickyHeaders = stickyHeaderIndices && stickyHeaderIndices.length > 0;
68 | /* $FlowFixMe(>=0.53.0 site=react_native_fb,react_native_oss) This comment
69 | * suppresses an error when upgrading Flow's support for React. To see the
70 | * error delete this comment and run Flow. */
71 | const childArray = hasStickyHeaders && React.Children.toArray(this.props.children);
72 | const children = hasStickyHeaders ?
73 | /* $FlowFixMe(>=0.53.0 site=react_native_fb,react_native_oss) This
74 | * comment suppresses an error when upgrading Flow's support for React.
75 | * To see the error delete this comment and run Flow. */
76 | childArray.map((child, index) => {
77 | /* $FlowFixMe(>=0.53.0 site=react_native_fb,react_native_oss) This
78 | * comment suppresses an error when upgrading Flow's support for React.
79 | * To see the error delete this comment and run Flow. */
80 | const indexOfIndex = child ? stickyHeaderIndices.indexOf(index) : -1;
81 | if (indexOfIndex > -1) {
82 | const key = child.key;
83 | /* $FlowFixMe(>=0.53.0 site=react_native_fb,react_native_oss) This
84 | * comment suppresses an error when upgrading Flow's support for
85 | * React. To see the error delete this comment and run Flow. */
86 | const nextIndex = stickyHeaderIndices[indexOfIndex + 1];
87 | return (
88 | this._setStickyHeaderRef(key, ref)}
91 | nextHeaderLayoutY={
92 | /* $FlowFixMe(>=0.53.0 site=react_native_fb,react_native_oss)
93 | * This comment suppresses an error when upgrading Flow's
94 | * support for React. To see the error delete this comment and
95 | * run Flow. */
96 | this._headerLayoutYs.get(this._getKeyForIndex(nextIndex, childArray))
97 | }
98 | onLayout={(event) => this._onStickyHeaderLayout(index, event, key)}
99 | scrollAnimatedValue={this._scrollAnimatedValue}>
100 | {child}
101 |
102 | );
103 | } else {
104 | return child;
105 | }
106 | }) :
107 | /* $FlowFixMe(>=0.53.0 site=react_native_fb,react_native_oss) This
108 | * comment suppresses an error when upgrading Flow's support for React.
109 | * To see the error delete this comment and run Flow. */
110 | this.props.children;
111 | const contentContainer =
112 | =0.53.0 site=react_native_fb,react_native_oss) This
115 | * comment suppresses an error when upgrading Flow's support for React.
116 | * To see the error delete this comment and run Flow. */
117 | ref={this._setInnerViewRef}
118 | style={contentContainerStyle}
119 | removeClippedSubviews={
120 | // Subview clipping causes issues with sticky headers on Android and
121 | // would be hard to fix properly in a performant way.
122 | Platform.OS === 'android' && hasStickyHeaders ?
123 | false :
124 | this.props.removeClippedSubviews
125 | }
126 | collapsable={false}>
127 | {children}
128 | ;
129 |
130 | const alwaysBounceHorizontal =
131 | this.props.alwaysBounceHorizontal !== undefined ?
132 | this.props.alwaysBounceHorizontal :
133 | this.props.horizontal;
134 |
135 | const alwaysBounceVertical =
136 | this.props.alwaysBounceVertical !== undefined ?
137 | this.props.alwaysBounceVertical :
138 | !this.props.horizontal;
139 |
140 | const DEPRECATED_sendUpdatedChildFrames =
141 | !!this.props.DEPRECATED_sendUpdatedChildFrames;
142 |
143 | const baseStyle = this.props.horizontal ? styles.baseHorizontal : styles.baseVertical;
144 | const props = {
145 | ...this.props,
146 | alwaysBounceHorizontal,
147 | alwaysBounceVertical,
148 | style: [baseStyle, this.props.style],
149 | // Override the onContentSizeChange from props, since this event can
150 | // bubble up from TextInputs
151 | onContentSizeChange: null,
152 | onMomentumScrollBegin: this.scrollResponderHandleMomentumScrollBegin,
153 | onMomentumScrollEnd: this.scrollResponderHandleMomentumScrollEnd,
154 | onResponderGrant: this.scrollResponderHandleResponderGrant,
155 | onResponderReject: this.scrollResponderHandleResponderReject,
156 | onResponderRelease: this.scrollResponderHandleResponderRelease,
157 | /* $FlowFixMe(>=0.53.0 site=react_native_fb,react_native_oss) This
158 | * comment suppresses an error when upgrading Flow's support for React.
159 | * To see the error delete this comment and run Flow. */
160 | onResponderTerminate: this.scrollResponderHandleTerminate,
161 | onResponderTerminationRequest: this.scrollResponderHandleTerminationRequest,
162 | onScroll: this._handleScroll,
163 | onScrollBeginDrag: this.scrollResponderHandleScrollBeginDrag,
164 | onScrollEndDrag: this.scrollResponderHandleScrollEndDrag,
165 | onScrollShouldSetResponder: this.scrollResponderHandleScrollShouldSetResponder,
166 | onStartShouldSetResponder: this.scrollResponderHandleStartShouldSetResponder,
167 | onStartShouldSetResponderCapture: this.scrollResponderHandleStartShouldSetResponderCapture,
168 | onTouchEnd: this.scrollResponderHandleTouchEnd,
169 | onTouchMove: this.scrollResponderHandleTouchMove,
170 | onTouchStart: this.scrollResponderHandleTouchStart,
171 | scrollEventThrottle: hasStickyHeaders ? 1 : this.props.scrollEventThrottle,
172 | sendMomentumEvents: (this.props.onMomentumScrollBegin || this.props.onMomentumScrollEnd) ?
173 | true : false,
174 | DEPRECATED_sendUpdatedChildFrames,
175 | };
176 |
177 | const {decelerationRate} = this.props;
178 | if (decelerationRate) {
179 | props.decelerationRate = processDecelerationRate(decelerationRate);
180 | }
181 |
182 | const refreshControl = this.props.refreshControl;
183 |
184 | if (refreshControl) {
185 | // On iOS the RefreshControl is a child of the ScrollView.
186 | // tvOS lacks native support for RefreshControl, so don't include it in that case
187 | return (
188 | /* $FlowFixMe(>=0.53.0 site=react_native_fb,react_native_oss) This
189 | * comment suppresses an error when upgrading Flow's support for
190 | * React. To see the error delete this comment and run Flow. */
191 |
192 | {Platform.isTVOS ? null : refreshControl}
193 | {contentContainer}
194 |
195 | );
196 | }
197 | return (
198 | /* $FlowFixMe(>=0.53.0 site=react_native_fb,react_native_oss) This
199 | * comment suppresses an error when upgrading Flow's support for React.
200 | * To see the error delete this comment and run Flow. */
201 |
202 | {contentContainer}
203 |
204 | );
205 | }
206 | }
207 |
208 | const styles = StyleSheet.create({
209 | baseVertical: {
210 | flexGrow: 1,
211 | flexShrink: 1,
212 | flexDirection: 'column',
213 | overflow: 'scroll',
214 | },
215 | baseHorizontal: {
216 | flexGrow: 1,
217 | flexShrink: 1,
218 | flexDirection: 'row',
219 | overflow: 'scroll',
220 | },
221 | contentContainerHorizontal: {
222 | flexDirection: 'row',
223 | },
224 | });
225 |
226 | export default Platform.OS === 'ios' ? MJScrollView : ScrollView;
227 |
228 |
229 |
--------------------------------------------------------------------------------
/src/util/date.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by jszh on 2020/5/13.
3 | */
4 |
5 | export default function dateFormat() {
6 | let date = new Date(new Date() - 0);
7 | let y = date.getFullYear(); //年
8 | let m = date.getMonth() + 1;
9 | m = m < 10 ? ('0' + m) : m; //月
10 | let d = date.getDate();
11 | d = d < 10 ? ('0' + d) : d; //天
12 | let h = date.getHours();
13 | h = h < 10 ? ('0' + h) : h; //时
14 | let minute = date.getMinutes();
15 | minute = minute < 10 ? ('0' + minute) : minute; //分
16 | let second = date.getSeconds();
17 | second = second < 10 ? ('0' + second) : second; //秒
18 | return m + '-' + d + ' ' + h + ':' + minute;
19 | }
20 |
--------------------------------------------------------------------------------