/node_modules/react-native/Libraries/Image/RelativeImageStub'
38 |
39 | suppress_type=$FlowIssue
40 | suppress_type=$FlowFixMe
41 | suppress_type=$FlowFixMeProps
42 | suppress_type=$FlowFixMeState
43 |
44 | [lints]
45 | sketchy-null-number=warn
46 | sketchy-null-mixed=warn
47 | sketchy-number=warn
48 | untyped-type-import=warn
49 | nonstrict-import=warn
50 | deprecated-type=warn
51 | unsafe-getters-setters=warn
52 | unnecessary-invariant=warn
53 | signature-verification-failure=warn
54 |
55 | [strict]
56 | deprecated-type
57 | nonstrict-import
58 | sketchy-null
59 | unclear-type
60 | unsafe-getters-setters
61 | untyped-import
62 | untyped-type-import
63 |
64 | [version]
65 | ^0.158.0
66 |
--------------------------------------------------------------------------------
/example/.gitattributes:
--------------------------------------------------------------------------------
1 | # Windows files should use crlf line endings
2 | # https://help.github.com/articles/dealing-with-line-endings/
3 | *.bat text eol=crlf
4 |
--------------------------------------------------------------------------------
/example/.gitignore:
--------------------------------------------------------------------------------
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 | # Android/IntelliJ
25 | #
26 | build/
27 | .idea
28 | .gradle
29 | local.properties
30 | *.iml
31 | *.hprof
32 |
33 | # node.js
34 | #
35 | node_modules/
36 | npm-debug.log
37 | yarn-error.log
38 |
39 | # BUCK
40 | buck-out/
41 | \.buckd/
42 | *.keystore
43 | !debug.keystore
44 |
45 | # fastlane
46 | #
47 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
48 | # screenshots whenever they are needed.
49 | # For more information about the recommended setup visit:
50 | # https://docs.fastlane.tools/best-practices/source-control/
51 |
52 | */fastlane/report.xml
53 | */fastlane/Preview.html
54 | */fastlane/screenshots
55 |
56 | # Bundle artifact
57 | *.jsbundle
58 |
59 | # CocoaPods
60 | /ios/Pods/
61 |
--------------------------------------------------------------------------------
/example/.prettierrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | bracketSpacing: false,
3 | jsxBracketSameLine: true,
4 | singleQuote: true,
5 | trailingComma: 'all',
6 | arrowParens: 'avoid',
7 | };
8 |
--------------------------------------------------------------------------------
/example/.watchmanconfig:
--------------------------------------------------------------------------------
1 | {}
--------------------------------------------------------------------------------
/example/App.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * Sample React Native App
3 | *
4 | * adapted from App.js generated by the following command:
5 | *
6 | * react-native init example
7 | *
8 | * https://github.com/facebook/react-native
9 | */
10 |
11 | import React, {useState} from 'react';
12 | import {SafeAreaView} from 'react-native';
13 | import {StyleSheet, Text, View} from 'react-native';
14 | import RefreshFlatList from './RefreshFlatList';
15 |
16 | async function waitForDisplayed(ms: number) {
17 | return new Promise(resolve => {
18 | setTimeout(() => resolve(void 0), ms);
19 | });
20 | }
21 |
22 | const App = () => {
23 | const [list, setList] = useState(randomColors());
24 |
25 | const onRefresh = async () => {
26 | await waitForDisplayed(2000);
27 | setList(randomColors());
28 | };
29 |
30 | const onEndReached = async () => {
31 | if (list.length > 50) {
32 | return;
33 | }
34 | await waitForDisplayed(2000);
35 | setList(_list => {
36 | return _list.concat(randomColors());
37 | });
38 | };
39 |
40 | return (
41 |
42 |
49 |
50 | );
51 | };
52 |
53 | const renderItem = ({item, index}: {item: string; index: number}) => {
54 | return (
55 |
56 | {index}
57 | _{item}
58 |
59 | );
60 | };
61 |
62 | export default App;
63 |
64 | const randomColors = (size = 10) => {
65 | const colors = [];
66 | for (let i = 0; i < size; i++) {
67 | const r = Math.floor(Math.random() * 256);
68 | const g = Math.floor(Math.random() * 256);
69 | const b = Math.floor(Math.random() * 256);
70 | colors.push('#' + r.toString(16) + g.toString(16) + b.toString(16));
71 | }
72 | return colors;
73 | };
74 |
75 | const styles = StyleSheet.create({
76 | container: {
77 | flex: 1,
78 | backgroundColor: '#fff',
79 | },
80 | list: {
81 | backgroundColor: 'rgba(0, 0, 0, 0.5)',
82 | },
83 | item: {
84 | marginHorizontal: 15,
85 | marginVertical: 5,
86 | alignItems: 'center',
87 | justifyContent: 'center',
88 | height: 80,
89 | borderRadius: 12,
90 | },
91 | text: {
92 | fontSize: 24,
93 | color: '#fff',
94 | },
95 | index: {
96 | fontSize: 24,
97 | color: '#000',
98 | },
99 | control: {
100 | height: 100,
101 | marginTop: -100,
102 | alignItems: 'center',
103 | justifyContent: 'center',
104 | },
105 | control_text: {
106 | fontSize: 18,
107 | color: 'red',
108 | },
109 | });
110 |
--------------------------------------------------------------------------------
/example/__tests__/App-test.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @format
3 | */
4 |
5 | import 'react-native';
6 | import React from 'react';
7 | import App from '../App';
8 |
9 | // Note: test renderer must be required after react-native.
10 | import renderer from 'react-test-renderer';
11 |
12 | it('renders correctly', () => {
13 | renderer.create();
14 | });
15 |
--------------------------------------------------------------------------------
/example/android/app/_BUCK:
--------------------------------------------------------------------------------
1 | # To learn about Buck see [Docs](https://buckbuild.com/).
2 | # To run your application with Buck:
3 | # - install Buck
4 | # - `npm start` - to start the packager
5 | # - `cd android`
6 | # - `keytool -genkey -v -keystore keystores/debug.keystore -storepass android -alias androiddebugkey -keypass android -dname "CN=Android Debug,O=Android,C=US"`
7 | # - `./gradlew :app:copyDownloadableDepsToLibs` - make all Gradle compile dependencies available to Buck
8 | # - `buck install -r android/app` - compile, install and run application
9 | #
10 |
11 | load(":build_defs.bzl", "create_aar_targets", "create_jar_targets")
12 |
13 | lib_deps = []
14 |
15 | create_aar_targets(glob(["libs/*.aar"]))
16 |
17 | create_jar_targets(glob(["libs/*.jar"]))
18 |
19 | android_library(
20 | name = "all-libs",
21 | exported_deps = lib_deps,
22 | )
23 |
24 | android_library(
25 | name = "app-code",
26 | srcs = glob([
27 | "src/main/java/**/*.java",
28 | ]),
29 | deps = [
30 | ":all-libs",
31 | ":build_config",
32 | ":res",
33 | ],
34 | )
35 |
36 | android_build_config(
37 | name = "build_config",
38 | package = "com.example",
39 | )
40 |
41 | android_resource(
42 | name = "res",
43 | package = "com.example",
44 | res = "src/main/res",
45 | )
46 |
47 | android_binary(
48 | name = "app",
49 | keystore = "//android/keystores:debug",
50 | manifest = "src/main/AndroidManifest.xml",
51 | package_type = "debug",
52 | deps = [
53 | ":app-code",
54 | ],
55 | )
56 |
--------------------------------------------------------------------------------
/example/android/app/build_defs.bzl:
--------------------------------------------------------------------------------
1 | """Helper definitions to glob .aar and .jar targets"""
2 |
3 | def create_aar_targets(aarfiles):
4 | for aarfile in aarfiles:
5 | name = "aars__" + aarfile[aarfile.rindex("/") + 1:aarfile.rindex(".aar")]
6 | lib_deps.append(":" + name)
7 | android_prebuilt_aar(
8 | name = name,
9 | aar = aarfile,
10 | )
11 |
12 | def create_jar_targets(jarfiles):
13 | for jarfile in jarfiles:
14 | name = "jars__" + jarfile[jarfile.rindex("/") + 1:jarfile.rindex(".jar")]
15 | lib_deps.append(":" + name)
16 | prebuilt_jar(
17 | name = name,
18 | binary_jar = jarfile,
19 | )
20 |
--------------------------------------------------------------------------------
/example/android/app/debug.keystore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/472647301/react-native-refresh-control/c3a967dc16ca0bb177443b1aa81836feb71465ac/example/android/app/debug.keystore
--------------------------------------------------------------------------------
/example/android/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
--------------------------------------------------------------------------------
/example/android/app/src/debug/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/example/android/app/src/debug/java/com/example/ReactNativeFlipper.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) Facebook, Inc. and its affiliates.
3 | *
4 | * This source code is licensed under the MIT license found in the LICENSE file in the root
5 | * directory of this source tree.
6 | */
7 | package com.example;
8 |
9 | import android.content.Context;
10 | import com.facebook.flipper.android.AndroidFlipperClient;
11 | import com.facebook.flipper.android.utils.FlipperUtils;
12 | import com.facebook.flipper.core.FlipperClient;
13 | import com.facebook.flipper.plugins.crashreporter.CrashReporterPlugin;
14 | import com.facebook.flipper.plugins.databases.DatabasesFlipperPlugin;
15 | import com.facebook.flipper.plugins.fresco.FrescoFlipperPlugin;
16 | import com.facebook.flipper.plugins.inspector.DescriptorMapping;
17 | import com.facebook.flipper.plugins.inspector.InspectorFlipperPlugin;
18 | import com.facebook.flipper.plugins.network.FlipperOkhttpInterceptor;
19 | import com.facebook.flipper.plugins.network.NetworkFlipperPlugin;
20 | import com.facebook.flipper.plugins.react.ReactFlipperPlugin;
21 | import com.facebook.flipper.plugins.sharedpreferences.SharedPreferencesFlipperPlugin;
22 | import com.facebook.react.ReactInstanceManager;
23 | import com.facebook.react.bridge.ReactContext;
24 | import com.facebook.react.modules.network.NetworkingModule;
25 | import okhttp3.OkHttpClient;
26 |
27 | public class ReactNativeFlipper {
28 | public static void initializeFlipper(Context context, ReactInstanceManager reactInstanceManager) {
29 | if (FlipperUtils.shouldEnableFlipper(context)) {
30 | final FlipperClient client = AndroidFlipperClient.getInstance(context);
31 |
32 | client.addPlugin(new InspectorFlipperPlugin(context, DescriptorMapping.withDefaults()));
33 | client.addPlugin(new ReactFlipperPlugin());
34 | client.addPlugin(new DatabasesFlipperPlugin(context));
35 | client.addPlugin(new SharedPreferencesFlipperPlugin(context));
36 | client.addPlugin(CrashReporterPlugin.getInstance());
37 |
38 | NetworkFlipperPlugin networkFlipperPlugin = new NetworkFlipperPlugin();
39 | NetworkingModule.setCustomClientBuilder(
40 | new NetworkingModule.CustomClientBuilder() {
41 | @Override
42 | public void apply(OkHttpClient.Builder builder) {
43 | builder.addNetworkInterceptor(new FlipperOkhttpInterceptor(networkFlipperPlugin));
44 | }
45 | });
46 | client.addPlugin(networkFlipperPlugin);
47 | client.start();
48 |
49 | // Fresco Plugin needs to ensure that ImagePipelineFactory is initialized
50 | // Hence we run if after all native modules have been initialized
51 | ReactContext reactContext = reactInstanceManager.getCurrentReactContext();
52 | if (reactContext == null) {
53 | reactInstanceManager.addReactInstanceEventListener(
54 | new ReactInstanceManager.ReactInstanceEventListener() {
55 | @Override
56 | public void onReactContextInitialized(ReactContext reactContext) {
57 | reactInstanceManager.removeReactInstanceEventListener(this);
58 | reactContext.runOnNativeModulesQueueThread(
59 | new Runnable() {
60 | @Override
61 | public void run() {
62 | client.addPlugin(new FrescoFlipperPlugin());
63 | }
64 | });
65 | }
66 | });
67 | } else {
68 | client.addPlugin(new FrescoFlipperPlugin());
69 | }
70 | }
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/example/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
16 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/example/android/app/src/main/java/com/example/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.example;
2 |
3 | import com.facebook.react.ReactActivity;
4 |
5 | public class MainActivity extends ReactActivity {
6 |
7 | /**
8 | * Returns the name of the main component registered from JavaScript. This is used to schedule
9 | * rendering of the component.
10 | */
11 | @Override
12 | protected String getMainComponentName() {
13 | return "example";
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/example/android/app/src/main/java/com/example/MainApplication.java:
--------------------------------------------------------------------------------
1 | package com.example;
2 |
3 | import android.app.Application;
4 | import android.content.Context;
5 | import com.facebook.react.PackageList;
6 | import com.facebook.react.ReactApplication;
7 | import com.facebook.react.ReactInstanceManager;
8 | import com.facebook.react.ReactNativeHost;
9 | import com.facebook.react.ReactPackage;
10 | import com.facebook.soloader.SoLoader;
11 | import java.lang.reflect.InvocationTargetException;
12 | import java.util.List;
13 |
14 | public class MainApplication extends Application implements ReactApplication {
15 |
16 | private final ReactNativeHost mReactNativeHost =
17 | new ReactNativeHost(this) {
18 | @Override
19 | public boolean getUseDeveloperSupport() {
20 | return BuildConfig.DEBUG;
21 | }
22 |
23 | @Override
24 | protected List getPackages() {
25 | @SuppressWarnings("UnnecessaryLocalVariable")
26 | List packages = new PackageList(this).getPackages();
27 | // Packages that cannot be autolinked yet can be added manually here, for example:
28 | // packages.add(new MyReactNativePackage());
29 | return packages;
30 | }
31 |
32 | @Override
33 | protected String getJSMainModuleName() {
34 | return "index";
35 | }
36 | };
37 |
38 | @Override
39 | public ReactNativeHost getReactNativeHost() {
40 | return mReactNativeHost;
41 | }
42 |
43 | @Override
44 | public void onCreate() {
45 | super.onCreate();
46 | SoLoader.init(this, /* native exopackage */ false);
47 | initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
48 | }
49 |
50 | /**
51 | * Loads Flipper in React Native templates. Call this in the onCreate method with something like
52 | * initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
53 | *
54 | * @param context
55 | * @param reactInstanceManager
56 | */
57 | private static void initializeFlipper(
58 | Context context, ReactInstanceManager reactInstanceManager) {
59 | if (BuildConfig.DEBUG) {
60 | try {
61 | /*
62 | We use reflection here to pick up the class that initializes Flipper,
63 | since Flipper library is not available in release mode
64 | */
65 | Class> aClass = Class.forName("com.example.ReactNativeFlipper");
66 | aClass
67 | .getMethod("initializeFlipper", Context.class, ReactInstanceManager.class)
68 | .invoke(null, context, reactInstanceManager);
69 | } catch (ClassNotFoundException e) {
70 | e.printStackTrace();
71 | } catch (NoSuchMethodException e) {
72 | e.printStackTrace();
73 | } catch (IllegalAccessException e) {
74 | e.printStackTrace();
75 | } catch (InvocationTargetException e) {
76 | e.printStackTrace();
77 | }
78 | }
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/472647301/react-native-refresh-control/c3a967dc16ca0bb177443b1aa81836feb71465ac/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/472647301/react-native-refresh-control/c3a967dc16ca0bb177443b1aa81836feb71465ac/example/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/472647301/react-native-refresh-control/c3a967dc16ca0bb177443b1aa81836feb71465ac/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/472647301/react-native-refresh-control/c3a967dc16ca0bb177443b1aa81836feb71465ac/example/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/472647301/react-native-refresh-control/c3a967dc16ca0bb177443b1aa81836feb71465ac/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/472647301/react-native-refresh-control/c3a967dc16ca0bb177443b1aa81836feb71465ac/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/472647301/react-native-refresh-control/c3a967dc16ca0bb177443b1aa81836feb71465ac/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/472647301/react-native-refresh-control/c3a967dc16ca0bb177443b1aa81836feb71465ac/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/472647301/react-native-refresh-control/c3a967dc16ca0bb177443b1aa81836feb71465ac/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/472647301/react-native-refresh-control/c3a967dc16ca0bb177443b1aa81836feb71465ac/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | example
3 |
4 |
--------------------------------------------------------------------------------
/example/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/example/android/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | ext {
5 | buildToolsVersion = "30.0.2"
6 | minSdkVersion = 21
7 | compileSdkVersion = 30
8 | targetSdkVersion = 30
9 | ndkVersion = "21.4.7075529"
10 | }
11 | repositories {
12 | google()
13 | mavenCentral()
14 | }
15 | dependencies {
16 | classpath("com.android.tools.build:gradle:4.2.2")
17 | // NOTE: Do not place your application dependencies here; they belong
18 | // in the individual module build.gradle files
19 | }
20 | }
21 |
22 | allprojects {
23 | repositories {
24 | mavenCentral()
25 | mavenLocal()
26 | maven {
27 | // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
28 | url("$rootDir/../node_modules/react-native/android")
29 | }
30 | maven {
31 | // Android JSC is installed from npm
32 | url("$rootDir/../node_modules/jsc-android/dist")
33 | }
34 |
35 | google()
36 | maven { url 'https://www.jitpack.io' }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/example/android/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m
13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
14 |
15 | # When configured, Gradle will run in incubating parallel mode.
16 | # This option should only be used with decoupled projects. More details, visit
17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
18 | # org.gradle.parallel=true
19 |
20 | # AndroidX package structure to make it clearer which packages are bundled with the
21 | # Android operating system, and which are packaged with your app's APK
22 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
23 | android.useAndroidX=true
24 | # Automatically convert third-party libraries to use AndroidX
25 | android.enableJetifier=true
26 |
27 | # Version of flipper SDK to use with React Native
28 | FLIPPER_VERSION=0.99.0
29 |
--------------------------------------------------------------------------------
/example/android/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/472647301/react-native-refresh-control/c3a967dc16ca0bb177443b1aa81836feb71465ac/example/android/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/example/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.9-all.zip
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 |
--------------------------------------------------------------------------------
/example/android/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%" == "" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%" == "" set DIRNAME=.
29 | set APP_BASE_NAME=%~n0
30 | set APP_HOME=%DIRNAME%
31 |
32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
34 |
35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
37 |
38 | @rem Find java.exe
39 | if defined JAVA_HOME goto findJavaFromJavaHome
40 |
41 | set JAVA_EXE=java.exe
42 | %JAVA_EXE% -version >NUL 2>&1
43 | if "%ERRORLEVEL%" == "0" goto execute
44 |
45 | echo.
46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
47 | echo.
48 | echo Please set the JAVA_HOME variable in your environment to match the
49 | echo location of your Java installation.
50 |
51 | goto fail
52 |
53 | :findJavaFromJavaHome
54 | set JAVA_HOME=%JAVA_HOME:"=%
55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
56 |
57 | if exist "%JAVA_EXE%" goto execute
58 |
59 | echo.
60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
61 | echo.
62 | echo Please set the JAVA_HOME variable in your environment to match the
63 | echo location of your Java installation.
64 |
65 | goto fail
66 |
67 | :execute
68 | @rem Setup the command line
69 |
70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
71 |
72 |
73 | @rem Execute Gradle
74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
75 |
76 | :end
77 | @rem End local scope for the variables with windows NT shell
78 | if "%ERRORLEVEL%"=="0" goto mainEnd
79 |
80 | :fail
81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
82 | rem the _cmd.exe /c_ return code!
83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
84 | exit /b 1
85 |
86 | :mainEnd
87 | if "%OS%"=="Windows_NT" endlocal
88 |
89 | :omega
90 |
--------------------------------------------------------------------------------
/example/android/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name = 'example'
2 | apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings)
3 | include ':app'
4 |
--------------------------------------------------------------------------------
/example/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example",
3 | "displayName": "example"
4 | }
--------------------------------------------------------------------------------
/example/assets/arrow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/472647301/react-native-refresh-control/c3a967dc16ca0bb177443b1aa81836feb71465ac/example/assets/arrow.png
--------------------------------------------------------------------------------
/example/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: ['module:metro-react-native-babel-preset'],
3 | };
4 |
--------------------------------------------------------------------------------
/example/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @format
3 | */
4 |
5 | import {AppRegistry} from 'react-native';
6 | import App from './App';
7 | import {name as appName} from './app.json';
8 |
9 | AppRegistry.registerComponent(appName, () => App);
10 |
--------------------------------------------------------------------------------
/example/ios/Podfile:
--------------------------------------------------------------------------------
1 | require_relative '../node_modules/react-native/scripts/react_native_pods'
2 | require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules'
3 |
4 | platform :ios, '11.0'
5 |
6 | target 'example' do
7 | config = use_native_modules!
8 |
9 | use_react_native!(
10 | :path => config[:reactNativePath],
11 | # to enable hermes on iOS, change `false` to `true` and then install pods
12 | :hermes_enabled => false
13 | )
14 |
15 | target 'exampleTests' do
16 | inherit! :complete
17 | # Pods for testing
18 | end
19 |
20 | # Enables Flipper.
21 | #
22 | # Note that if you have use_frameworks! enabled, Flipper will not work and
23 | # you should disable the next line.
24 | use_flipper!()
25 |
26 | post_install do |installer|
27 | react_native_post_install(installer)
28 | __apply_Xcode_12_5_M1_post_install_workaround(installer)
29 | end
30 | end
31 |
--------------------------------------------------------------------------------
/example/ios/example.xcodeproj/xcshareddata/xcschemes/example.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
33 |
39 |
40 |
41 |
42 |
43 |
53 |
55 |
61 |
62 |
63 |
64 |
70 |
72 |
78 |
79 |
80 |
81 |
83 |
84 |
87 |
88 |
89 |
--------------------------------------------------------------------------------
/example/ios/example.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/example/ios/example.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/example/ios/example/AppDelegate.h:
--------------------------------------------------------------------------------
1 | #import
2 | #import
3 |
4 | @interface AppDelegate : UIResponder
5 |
6 | @property (nonatomic, strong) UIWindow *window;
7 |
8 | @end
9 |
--------------------------------------------------------------------------------
/example/ios/example/AppDelegate.m:
--------------------------------------------------------------------------------
1 | #import "AppDelegate.h"
2 |
3 | #import
4 | #import
5 | #import
6 |
7 | #ifdef FB_SONARKIT_ENABLED
8 | #import
9 | #import
10 | #import
11 | #import
12 | #import
13 | #import
14 |
15 | static void InitializeFlipper(UIApplication *application) {
16 | FlipperClient *client = [FlipperClient sharedClient];
17 | SKDescriptorMapper *layoutDescriptorMapper = [[SKDescriptorMapper alloc] initWithDefaults];
18 | [client addPlugin:[[FlipperKitLayoutPlugin alloc] initWithRootNode:application withDescriptorMapper:layoutDescriptorMapper]];
19 | [client addPlugin:[[FKUserDefaultsPlugin alloc] initWithSuiteName:nil]];
20 | [client addPlugin:[FlipperKitReactPlugin new]];
21 | [client addPlugin:[[FlipperKitNetworkPlugin alloc] initWithNetworkAdapter:[SKIOSNetworkAdapter new]]];
22 | [client start];
23 | }
24 | #endif
25 |
26 | @implementation AppDelegate
27 |
28 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
29 | {
30 | #ifdef FB_SONARKIT_ENABLED
31 | InitializeFlipper(application);
32 | #endif
33 |
34 | RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions];
35 | RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge
36 | moduleName:@"example"
37 | initialProperties:nil];
38 |
39 | if (@available(iOS 13.0, *)) {
40 | rootView.backgroundColor = [UIColor systemBackgroundColor];
41 | } else {
42 | rootView.backgroundColor = [UIColor whiteColor];
43 | }
44 |
45 | self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
46 | UIViewController *rootViewController = [UIViewController new];
47 | rootViewController.view = rootView;
48 | self.window.rootViewController = rootViewController;
49 | [self.window makeKeyAndVisible];
50 | return YES;
51 | }
52 |
53 | - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
54 | {
55 | #if DEBUG
56 | return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil];
57 | #else
58 | return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
59 | #endif
60 | }
61 |
62 | @end
63 |
--------------------------------------------------------------------------------
/example/ios/example/Images.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "29x29",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "29x29",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "40x40",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "40x40",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "60x60",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "60x60",
31 | "scale" : "3x"
32 | }
33 | ],
34 | "info" : {
35 | "version" : 1,
36 | "author" : "xcode"
37 | }
38 | }
--------------------------------------------------------------------------------
/example/ios/example/Images.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/example/ios/example/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleDisplayName
8 | example
9 | CFBundleExecutable
10 | $(EXECUTABLE_NAME)
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | APPL
19 | CFBundleShortVersionString
20 | 1.0
21 | CFBundleSignature
22 | ????
23 | CFBundleVersion
24 | 1
25 | LSRequiresIPhoneOS
26 |
27 | NSAppTransportSecurity
28 |
29 | NSExceptionDomains
30 |
31 | localhost
32 |
33 | NSExceptionAllowsInsecureHTTPLoads
34 |
35 |
36 |
37 |
38 | NSLocationWhenInUseUsageDescription
39 |
40 | UILaunchStoryboardName
41 | LaunchScreen
42 | UIRequiredDeviceCapabilities
43 |
44 | armv7
45 |
46 | UISupportedInterfaceOrientations
47 |
48 | UIInterfaceOrientationPortrait
49 | UIInterfaceOrientationLandscapeLeft
50 | UIInterfaceOrientationLandscapeRight
51 |
52 | UIViewControllerBasedStatusBarAppearance
53 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/example/ios/example/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
24 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/example/ios/example/main.m:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | #import "AppDelegate.h"
4 |
5 | int main(int argc, char * argv[]) {
6 | @autoreleasepool {
7 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/example/ios/exampleTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 |
24 |
25 |
--------------------------------------------------------------------------------
/example/ios/exampleTests/exampleTests.m:
--------------------------------------------------------------------------------
1 | #import
2 | #import
3 |
4 | #import
5 | #import
6 |
7 | #define TIMEOUT_SECONDS 600
8 | #define TEXT_TO_LOOK_FOR @"Welcome to React"
9 |
10 | @interface exampleTests : XCTestCase
11 |
12 | @end
13 |
14 | @implementation exampleTests
15 |
16 | - (BOOL)findSubviewInView:(UIView *)view matching:(BOOL(^)(UIView *view))test
17 | {
18 | if (test(view)) {
19 | return YES;
20 | }
21 | for (UIView *subview in [view subviews]) {
22 | if ([self findSubviewInView:subview matching:test]) {
23 | return YES;
24 | }
25 | }
26 | return NO;
27 | }
28 |
29 | - (void)testRendersWelcomeScreen
30 | {
31 | UIViewController *vc = [[[RCTSharedApplication() delegate] window] rootViewController];
32 | NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS];
33 | BOOL foundElement = NO;
34 |
35 | __block NSString *redboxError = nil;
36 | #ifdef DEBUG
37 | RCTSetLogFunction(^(RCTLogLevel level, RCTLogSource source, NSString *fileName, NSNumber *lineNumber, NSString *message) {
38 | if (level >= RCTLogLevelError) {
39 | redboxError = message;
40 | }
41 | });
42 | #endif
43 |
44 | while ([date timeIntervalSinceNow] > 0 && !foundElement && !redboxError) {
45 | [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
46 | [[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
47 |
48 | foundElement = [self findSubviewInView:vc.view matching:^BOOL(UIView *view) {
49 | if ([view.accessibilityLabel isEqualToString:TEXT_TO_LOOK_FOR]) {
50 | return YES;
51 | }
52 | return NO;
53 | }];
54 | }
55 |
56 | #ifdef DEBUG
57 | RCTSetLogFunction(RCTDefaultLogFunction);
58 | #endif
59 |
60 | XCTAssertNil(redboxError, @"RedBox error: %@", redboxError);
61 | XCTAssertTrue(foundElement, @"Couldn't find element with text '%@' in %d seconds", TEXT_TO_LOOK_FOR, TIMEOUT_SECONDS);
62 | }
63 |
64 |
65 | @end
66 |
--------------------------------------------------------------------------------
/example/metro.config.js:
--------------------------------------------------------------------------------
1 | // metro.config.js
2 | //
3 | // with multiple workarounds for this issue with symlinks:
4 | // https://github.com/facebook/metro/issues/1
5 | //
6 | // with thanks to @johnryan ()
7 | // for the pointers to multiple workaround solutions here:
8 | // https://github.com/facebook/metro/issues/1#issuecomment-541642857
9 | //
10 | // see also this discussion:
11 | // https://github.com/brodybits/create-react-native-module/issues/232
12 |
13 | const path = require('path')
14 |
15 | module.exports = {
16 | // workaround for an issue with symlinks encountered starting with
17 | // metro@0.55 / React Native 0.61
18 | // (not needed with React Native 0.60 / metro@0.54)
19 | resolver: {
20 | extraNodeModules: new Proxy(
21 | {},
22 | { get: (_, name) => path.resolve('.', 'node_modules', name) }
23 | )
24 | },
25 |
26 | // quick workaround for another issue with symlinks
27 | watchFolders: [path.resolve('.'), path.resolve('..')]
28 | }
29 |
--------------------------------------------------------------------------------
/example/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example",
3 | "version": "0.0.1",
4 | "private": true,
5 | "scripts": {
6 | "android": "react-native run-android",
7 | "ios": "react-native run-ios",
8 | "start": "react-native start",
9 | "test": "jest",
10 | "lint": "eslint ."
11 | },
12 | "dependencies": {
13 | "@byron-react-native/refresh-control": "link:../",
14 | "dayjs": "^1.10.7",
15 | "react": "17.0.2",
16 | "react-native": "0.66.4",
17 | "react-native-spinkit": "^1.5.1"
18 | },
19 | "devDependencies": {
20 | "@babel/core": "^7.12.9",
21 | "@babel/runtime": "^7.12.5",
22 | "@react-native-community/eslint-config": "^2.0.0",
23 | "@types/jest": "^27.5.0",
24 | "@types/react": "^18.0.9",
25 | "@types/react-native": "^0.69.1",
26 | "@types/react-test-renderer": "^18.0.0",
27 | "babel-jest": "^26.6.3",
28 | "eslint": "7.14.0",
29 | "jest": "^26.6.3",
30 | "metro-react-native-babel-preset": "^0.66.2",
31 | "react-test-renderer": "17.0.2",
32 | "typescript": "^4.6.4"
33 | },
34 | "jest": {
35 | "preset": "react-native"
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/example/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "allowJs": true,
4 | "allowSyntheticDefaultImports": true,
5 | "esModuleInterop": true,
6 | "isolatedModules": true,
7 | "jsx": "react-native",
8 | "lib": ["es2017"],
9 | "moduleResolution": "node",
10 | "noEmit": true,
11 | "strict": true,
12 | "target": "esnext"
13 | },
14 | "exclude": [
15 | "node_modules",
16 | "babel.config.js",
17 | "metro.config.js",
18 | "jest.config.js"
19 | ]
20 | }
21 |
--------------------------------------------------------------------------------
/index.d.ts:
--------------------------------------------------------------------------------
1 | declare module "@byron-react-native/refresh-control" {
2 | import { ViewProps } from "react-native";
3 |
4 | export class RNRefreshHeader extends React.Component {}
5 |
6 | export interface RefreshControlProps extends ViewProps {
7 | refreshing?: boolean;
8 | onRefresh?: () => Promise;
9 | onChangeState?: (state: number) => void;
10 | onChangeOffset?: (offset: number) => void;
11 | }
12 |
13 | export type RNRefreshControlProps = Omit & {
14 | height: number;
15 | };
16 |
17 | export class RNRefreshControl extends React.Component {}
18 |
19 | export class RefreshControl extends React.Component {
20 | startRefresh: () => void;
21 | stopRefresh: () => void;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/ios/MJRefresh/Base/MJRefreshAutoFooter.h:
--------------------------------------------------------------------------------
1 | //
2 | // MJRefreshAutoFooter.h
3 | // MJRefresh
4 | //
5 | // Created by MJ Lee on 15/4/24.
6 | // Copyright (c) 2015年 小码哥. All rights reserved.
7 | //
8 |
9 | #if __has_include()
10 | #import
11 | #else
12 | #import "MJRefreshFooter.h"
13 | #endif
14 |
15 | NS_ASSUME_NONNULL_BEGIN
16 |
17 | @interface MJRefreshAutoFooter : MJRefreshFooter
18 | /** 是否自动刷新(默认为YES) */
19 | @property (assign, nonatomic, getter=isAutomaticallyRefresh) BOOL automaticallyRefresh;
20 |
21 | /** 当底部控件出现多少时就自动刷新(默认为1.0,也就是底部控件完全出现时,才会自动刷新) */
22 | @property (assign, nonatomic) CGFloat appearencePercentTriggerAutoRefresh MJRefreshDeprecated("请使用triggerAutomaticallyRefreshPercent属性");
23 |
24 | /** 当底部控件出现多少时就自动刷新(默认为1.0,也就是底部控件完全出现时,才会自动刷新) */
25 | @property (assign, nonatomic) CGFloat triggerAutomaticallyRefreshPercent;
26 |
27 | /** 自动触发次数, 默认为 1, 仅在拖拽 ScrollView 时才生效,
28 |
29 | 如果为 -1, 则为无限触发
30 | */
31 | @property (nonatomic) NSInteger autoTriggerTimes;
32 | @end
33 |
34 | NS_ASSUME_NONNULL_END
35 |
--------------------------------------------------------------------------------
/ios/MJRefresh/Base/MJRefreshBackFooter.h:
--------------------------------------------------------------------------------
1 | //
2 | // MJRefreshBackFooter.h
3 | // MJRefresh
4 | //
5 | // Created by MJ Lee on 15/4/24.
6 | // Copyright (c) 2015年 小码哥. All rights reserved.
7 | //
8 |
9 | #if __has_include()
10 | #import
11 | #else
12 | #import "MJRefreshFooter.h"
13 | #endif
14 |
15 | NS_ASSUME_NONNULL_BEGIN
16 |
17 | @interface MJRefreshBackFooter : MJRefreshFooter
18 |
19 | @end
20 |
21 | NS_ASSUME_NONNULL_END
22 |
--------------------------------------------------------------------------------
/ios/MJRefresh/Base/MJRefreshComponent.h:
--------------------------------------------------------------------------------
1 | // 代码地址: https://github.com/CoderMJLee/MJRefresh
2 | // MJRefreshComponent.h
3 | // MJRefresh
4 | //
5 | // Created by MJ Lee on 15/3/4.
6 | // Copyright (c) 2015年 小码哥. All rights reserved.
7 | // 刷新控件的基类
8 |
9 | #import
10 | #if __has_include()
11 | #import
12 | #else
13 | #import "MJRefreshConst.h"
14 | #endif
15 |
16 | NS_ASSUME_NONNULL_BEGIN
17 |
18 | /** 刷新控件的状态 */
19 | typedef NS_ENUM(NSInteger, MJRefreshState) {
20 | /** 普通闲置状态 */
21 | MJRefreshStateIdle = 1,
22 | /** 松开就可以进行刷新的状态 */
23 | MJRefreshStatePulling,
24 | /** 正在刷新中的状态 */
25 | MJRefreshStateRefreshing,
26 | /** 即将刷新的状态 */
27 | MJRefreshStateWillRefresh,
28 | /** 所有数据加载完毕,没有更多的数据了 */
29 | MJRefreshStateNoMoreData
30 | };
31 |
32 | /** 进入刷新状态的回调 */
33 | typedef void (^MJRefreshComponentRefreshingBlock)(void) MJRefreshDeprecated("first deprecated in 3.3.0 - Use `MJRefreshComponentAction` instead");
34 | /** 开始刷新后的回调(进入刷新状态后的回调) */
35 | typedef void (^MJRefreshComponentBeginRefreshingCompletionBlock)(void) MJRefreshDeprecated("first deprecated in 3.3.0 - Use `MJRefreshComponentAction` instead");
36 | /** 结束刷新后的回调 */
37 | typedef void (^MJRefreshComponentEndRefreshingCompletionBlock)(void) MJRefreshDeprecated("first deprecated in 3.3.0 - Use `MJRefreshComponentAction` instead");
38 |
39 | /** 刷新用到的回调类型 */
40 | typedef void (^MJRefreshComponentAction)(void);
41 |
42 | /** 刷新控件的基类 */
43 | @interface MJRefreshComponent : UIView
44 | {
45 | /** 记录scrollView刚开始的inset */
46 | UIEdgeInsets _scrollViewOriginalInset;
47 | /** 父控件 */
48 | __weak UIScrollView *_scrollView;
49 | }
50 |
51 | #pragma mark - 刷新动画时间控制
52 | /** 快速动画时间(一般用在刷新开始的回弹动画), 默认 0.25 */
53 | @property (nonatomic) NSTimeInterval fastAnimationDuration;
54 | /** 慢速动画时间(一般用在刷新结束后的回弹动画), 默认 0.4*/
55 | @property (nonatomic) NSTimeInterval slowAnimationDuration;
56 | /** 关闭全部默认动画效果, 可以简单粗暴地解决 CollectionView 的回弹动画 bug */
57 | - (instancetype)setAnimationDisabled;
58 |
59 | #pragma mark - 刷新回调
60 | /** 正在刷新的回调 */
61 | @property (copy, nonatomic, nullable) MJRefreshComponentAction refreshingBlock;
62 | /** 设置回调对象和回调方法 */
63 | - (void)setRefreshingTarget:(id)target refreshingAction:(SEL)action;
64 |
65 | /** 回调对象 */
66 | @property (weak, nonatomic) id refreshingTarget;
67 | /** 回调方法 */
68 | @property (assign, nonatomic) SEL refreshingAction;
69 | /** 触发回调(交给子类去调用) */
70 | - (void)executeRefreshingCallback;
71 |
72 | #pragma mark - 刷新状态控制
73 | /** 进入刷新状态 */
74 | - (void)beginRefreshing;
75 | - (void)beginRefreshingWithCompletionBlock:(void (^)(void))completionBlock;
76 | /** 开始刷新后的回调(进入刷新状态后的回调) */
77 | @property (copy, nonatomic, nullable) MJRefreshComponentAction beginRefreshingCompletionBlock;
78 | /** 带动画的结束刷新的回调 */
79 | @property (copy, nonatomic, nullable) MJRefreshComponentAction endRefreshingAnimateCompletionBlock MJRefreshDeprecated("first deprecated in 3.3.0 - Use `endRefreshingAnimationBeginAction` instead");
80 | @property (copy, nonatomic, nullable) MJRefreshComponentAction endRefreshingAnimationBeginAction;
81 | /** 结束刷新的回调 */
82 | @property (copy, nonatomic, nullable) MJRefreshComponentAction endRefreshingCompletionBlock;
83 | /** 结束刷新状态 */
84 | - (void)endRefreshing;
85 | - (void)endRefreshingWithCompletionBlock:(void (^)(void))completionBlock;
86 | /** 是否正在刷新 */
87 | @property (assign, nonatomic, readonly, getter=isRefreshing) BOOL refreshing;
88 |
89 | /** 刷新状态 一般交给子类内部实现 */
90 | @property (assign, nonatomic) MJRefreshState state;
91 |
92 | #pragma mark - 交给子类去访问
93 | /** 记录scrollView刚开始的inset */
94 | @property (assign, nonatomic, readonly) UIEdgeInsets scrollViewOriginalInset;
95 | /** 父控件 */
96 | @property (weak, nonatomic, readonly) UIScrollView *scrollView;
97 |
98 | #pragma mark - 交给子类们去实现
99 | /** 初始化 */
100 | - (void)prepare NS_REQUIRES_SUPER;
101 | /** 摆放子控件frame */
102 | - (void)placeSubviews NS_REQUIRES_SUPER;
103 | /** 当scrollView的contentOffset发生改变的时候调用 */
104 | - (void)scrollViewContentOffsetDidChange:(nullable NSDictionary *)change NS_REQUIRES_SUPER;
105 | /** 当scrollView的contentSize发生改变的时候调用 */
106 | - (void)scrollViewContentSizeDidChange:(nullable NSDictionary *)change NS_REQUIRES_SUPER;
107 | /** 当scrollView的拖拽状态发生改变的时候调用 */
108 | - (void)scrollViewPanStateDidChange:(nullable NSDictionary *)change NS_REQUIRES_SUPER;
109 |
110 | /** 多语言配置 language 发生变化时调用
111 |
112 | `MJRefreshConfig.defaultConfig.language` 发生改变时调用.
113 |
114 | ⚠️ 父类会调用 `placeSubviews` 方法, 请勿在 placeSubviews 中调用本方法, 造成死循环. 子类在需要重新布局时, 在配置完修改后, 最后再调用 super 方法, 否则可能导致配置修改后, 定位先于修改执行.
115 | */
116 | - (void)i18nDidChange NS_REQUIRES_SUPER;
117 |
118 | #pragma mark - 其他
119 | /** 拉拽的百分比(交给子类重写) */
120 | @property (assign, nonatomic) CGFloat pullingPercent;
121 | /** 根据拖拽比例自动切换透明度 */
122 | @property (assign, nonatomic, getter=isAutoChangeAlpha) BOOL autoChangeAlpha MJRefreshDeprecated("请使用automaticallyChangeAlpha属性");
123 | /** 根据拖拽比例自动切换透明度 */
124 | @property (assign, nonatomic, getter=isAutomaticallyChangeAlpha) BOOL automaticallyChangeAlpha;
125 | @end
126 |
127 | @interface UILabel(MJRefresh)
128 | + (instancetype)mj_label;
129 | - (CGFloat)mj_textWidth;
130 | @end
131 |
132 | @interface MJRefreshComponent (ChainingGrammar)
133 |
134 | #pragma mark - <<< 为 Swift 扩展链式语法 >>> -
135 | /// 自动变化透明度
136 | - (instancetype)autoChangeTransparency:(BOOL)isAutoChange;
137 | /// 刷新开始后立即调用的回调
138 | - (instancetype)afterBeginningAction:(MJRefreshComponentAction)action;
139 | /// 刷新动画开始后立即调用的回调
140 | - (instancetype)endingAnimationBeginningAction:(MJRefreshComponentAction)action;
141 | /// 刷新结束后立即调用的回调
142 | - (instancetype)afterEndingAction:(MJRefreshComponentAction)action;
143 |
144 |
145 | /// 需要子类必须实现
146 | /// @param scrollView 赋值给的 ScrollView 的 Header/Footer/Trailer
147 | - (instancetype)linkTo:(UIScrollView *)scrollView;
148 |
149 | @end
150 |
151 | NS_ASSUME_NONNULL_END
152 |
--------------------------------------------------------------------------------
/ios/MJRefresh/Base/MJRefreshFooter.h:
--------------------------------------------------------------------------------
1 | // 代码地址: https://github.com/CoderMJLee/MJRefresh
2 | // MJRefreshFooter.h
3 | // MJRefresh
4 | //
5 | // Created by MJ Lee on 15/3/5.
6 | // Copyright (c) 2015年 小码哥. All rights reserved.
7 | // 上拉刷新控件
8 |
9 | #if __has_include()
10 | #import
11 | #else
12 | #import "MJRefreshComponent.h"
13 | #endif
14 |
15 | NS_ASSUME_NONNULL_BEGIN
16 |
17 | @interface MJRefreshFooter : MJRefreshComponent
18 | /** 创建footer */
19 | + (instancetype)footerWithRefreshingBlock:(MJRefreshComponentAction)refreshingBlock;
20 | /** 创建footer */
21 | + (instancetype)footerWithRefreshingTarget:(id)target refreshingAction:(SEL)action;
22 |
23 | /** 提示没有更多的数据 */
24 | - (void)endRefreshingWithNoMoreData;
25 | - (void)noticeNoMoreData MJRefreshDeprecated("使用endRefreshingWithNoMoreData");
26 |
27 | /** 重置没有更多的数据(消除没有更多数据的状态) */
28 | - (void)resetNoMoreData;
29 |
30 | /** 忽略多少scrollView的contentInset的bottom */
31 | @property (assign, nonatomic) CGFloat ignoredScrollViewContentInsetBottom;
32 |
33 | /** 自动根据有无数据来显示和隐藏(有数据就显示,没有数据隐藏。默认是NO) */
34 | @property (assign, nonatomic, getter=isAutomaticallyHidden) BOOL automaticallyHidden MJRefreshDeprecated("已废弃此属性,开发者请自行控制footer的显示和隐藏");
35 | @end
36 |
37 | NS_ASSUME_NONNULL_END
38 |
--------------------------------------------------------------------------------
/ios/MJRefresh/Base/MJRefreshFooter.m:
--------------------------------------------------------------------------------
1 | // 代码地址: https://github.com/CoderMJLee/MJRefresh
2 | // MJRefreshFooter.m
3 | // MJRefresh
4 | //
5 | // Created by MJ Lee on 15/3/5.
6 | // Copyright (c) 2015年 小码哥. All rights reserved.
7 | //
8 |
9 | #import "MJRefreshFooter.h"
10 | #import "UIScrollView+MJRefresh.h"
11 | #import "UIView+MJExtension.h"
12 |
13 | @interface MJRefreshFooter()
14 |
15 | @end
16 |
17 | @implementation MJRefreshFooter
18 | #pragma mark - 构造方法
19 | + (instancetype)footerWithRefreshingBlock:(MJRefreshComponentAction)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 | #pragma mark . 链式语法部分 .
45 |
46 | - (instancetype)linkTo:(UIScrollView *)scrollView {
47 | scrollView.mj_footer = self;
48 | return self;
49 | }
50 |
51 | #pragma mark - 公共方法
52 | - (void)endRefreshingWithNoMoreData
53 | {
54 | MJRefreshDispatchAsyncOnMainQueue(self.state = MJRefreshStateNoMoreData;)
55 | }
56 |
57 | - (void)noticeNoMoreData
58 | {
59 | [self endRefreshingWithNoMoreData];
60 | }
61 |
62 | - (void)resetNoMoreData
63 | {
64 | MJRefreshDispatchAsyncOnMainQueue(self.state = MJRefreshStateIdle;)
65 | }
66 |
67 | - (void)setAutomaticallyHidden:(BOOL)automaticallyHidden
68 | {
69 | _automaticallyHidden = automaticallyHidden;
70 | }
71 | @end
72 |
--------------------------------------------------------------------------------
/ios/MJRefresh/Base/MJRefreshHeader.h:
--------------------------------------------------------------------------------
1 | // 代码地址: https://github.com/CoderMJLee/MJRefresh
2 | // MJRefreshHeader.h
3 | // MJRefresh
4 | //
5 | // Created by MJ Lee on 15/3/4.
6 | // Copyright (c) 2015年 小码哥. All rights reserved.
7 | // 下拉刷新控件:负责监控用户下拉的状态
8 |
9 | #if __has_include()
10 | #import
11 | #else
12 | #import "MJRefreshComponent.h"
13 | #endif
14 |
15 | NS_ASSUME_NONNULL_BEGIN
16 |
17 | @interface MJRefreshHeader : MJRefreshComponent
18 | /** 创建header */
19 | + (instancetype)headerWithRefreshingBlock:(MJRefreshComponentAction)refreshingBlock;
20 | /** 创建header */
21 | + (instancetype)headerWithRefreshingTarget:(id)target refreshingAction:(SEL)action;
22 |
23 | /** 这个key用来存储上一次下拉刷新成功的时间 */
24 | @property (copy, nonatomic) NSString *lastUpdatedTimeKey;
25 | /** 上一次下拉刷新成功的时间 */
26 | @property (strong, nonatomic, readonly, nullable) NSDate *lastUpdatedTime;
27 |
28 | /** 忽略多少scrollView的contentInset的top */
29 | @property (assign, nonatomic) CGFloat ignoredScrollViewContentInsetTop;
30 |
31 | /** 默认是关闭状态, 如果遇到 CollectionView 的动画异常问题可以尝试打开 */
32 | @property (nonatomic) BOOL isCollectionViewAnimationBug;
33 | @end
34 |
35 | NS_ASSUME_NONNULL_END
36 |
--------------------------------------------------------------------------------
/ios/MJRefresh/Base/MJRefreshTrailer.h:
--------------------------------------------------------------------------------
1 | //
2 | // MJRefreshTrailer.h
3 | // MJRefresh
4 | //
5 | // Created by kinarobin on 2020/5/3.
6 | // Copyright © 2020 小码哥. All rights reserved.
7 | //
8 |
9 | #if __has_include()
10 | #import
11 | #else
12 | #import "MJRefreshComponent.h"
13 | #endif
14 |
15 | NS_ASSUME_NONNULL_BEGIN
16 |
17 | @interface MJRefreshTrailer : MJRefreshComponent
18 |
19 | /** 创建trailer*/
20 | + (instancetype)trailerWithRefreshingBlock:(MJRefreshComponentAction)refreshingBlock;
21 | /** 创建trailer */
22 | + (instancetype)trailerWithRefreshingTarget:(id)target refreshingAction:(SEL)action;
23 |
24 | /** 忽略多少scrollView的contentInset的right */
25 | @property (assign, nonatomic) CGFloat ignoredScrollViewContentInsetRight;
26 |
27 |
28 | @end
29 |
30 | NS_ASSUME_NONNULL_END
31 |
--------------------------------------------------------------------------------
/ios/MJRefresh/Custom/Footer/Auto/MJRefreshAutoGifFooter.h:
--------------------------------------------------------------------------------
1 | //
2 | // MJRefreshAutoGifFooter.h
3 | // MJRefresh
4 | //
5 | // Created by MJ Lee on 15/4/24.
6 | // Copyright (c) 2015年 小码哥. All rights reserved.
7 | //
8 |
9 | #if __has_include()
10 | #import
11 | #else
12 | #import "MJRefreshAutoStateFooter.h"
13 | #endif
14 |
15 | NS_ASSUME_NONNULL_BEGIN
16 |
17 | @interface MJRefreshAutoGifFooter : MJRefreshAutoStateFooter
18 | @property (weak, nonatomic, readonly) UIImageView *gifView;
19 |
20 | /** 设置state状态下的动画图片images 动画持续时间duration*/
21 | - (instancetype)setImages:(NSArray *)images duration:(NSTimeInterval)duration forState:(MJRefreshState)state;
22 | - (instancetype)setImages:(NSArray *)images forState:(MJRefreshState)state;
23 | @end
24 |
25 | NS_ASSUME_NONNULL_END
26 |
--------------------------------------------------------------------------------
/ios/MJRefresh/Custom/Footer/Auto/MJRefreshAutoGifFooter.m:
--------------------------------------------------------------------------------
1 | //
2 | // MJRefreshAutoGifFooter.m
3 | // MJRefresh
4 | //
5 | // Created by MJ Lee on 15/4/24.
6 | // Copyright (c) 2015年 小码哥. All rights reserved.
7 | //
8 |
9 | #import "MJRefreshAutoGifFooter.h"
10 | #import "NSBundle+MJRefresh.h"
11 | #import "UIView+MJExtension.h"
12 | #import "UIScrollView+MJExtension.h"
13 | #import "UIScrollView+MJRefresh.h"
14 |
15 | @interface MJRefreshAutoGifFooter()
16 | {
17 | __unsafe_unretained UIImageView *_gifView;
18 | }
19 | /** 所有状态对应的动画图片 */
20 | @property (strong, nonatomic) NSMutableDictionary *stateImages;
21 | /** 所有状态对应的动画时间 */
22 | @property (strong, nonatomic) NSMutableDictionary *stateDurations;
23 | @end
24 |
25 | @implementation MJRefreshAutoGifFooter
26 | #pragma mark - 懒加载
27 | - (UIImageView *)gifView
28 | {
29 | if (!_gifView) {
30 | UIImageView *gifView = [[UIImageView alloc] init];
31 | [self addSubview:_gifView = gifView];
32 | }
33 | return _gifView;
34 | }
35 |
36 | - (NSMutableDictionary *)stateImages
37 | {
38 | if (!_stateImages) {
39 | self.stateImages = [NSMutableDictionary dictionary];
40 | }
41 | return _stateImages;
42 | }
43 |
44 | - (NSMutableDictionary *)stateDurations
45 | {
46 | if (!_stateDurations) {
47 | self.stateDurations = [NSMutableDictionary dictionary];
48 | }
49 | return _stateDurations;
50 | }
51 |
52 | #pragma mark - 公共方法
53 | - (instancetype)setImages:(NSArray *)images duration:(NSTimeInterval)duration forState:(MJRefreshState)state
54 | {
55 | if (images == nil) return self;
56 |
57 | self.stateImages[@(state)] = images;
58 | self.stateDurations[@(state)] = @(duration);
59 |
60 | /* 根据图片设置控件的高度 */
61 | UIImage *image = [images firstObject];
62 | if (image.size.height > self.mj_h) {
63 | self.mj_h = image.size.height;
64 | }
65 | return self;
66 | }
67 |
68 | - (instancetype)setImages:(NSArray *)images forState:(MJRefreshState)state
69 | {
70 | return [self setImages:images duration:images.count * 0.1 forState:state];
71 | }
72 |
73 | #pragma mark - 实现父类的方法
74 | - (void)prepare
75 | {
76 | [super prepare];
77 |
78 | // 初始化间距
79 | self.labelLeftInset = 20;
80 | }
81 |
82 | - (void)placeSubviews
83 | {
84 | [super placeSubviews];
85 |
86 | if (self.gifView.constraints.count) return;
87 |
88 | self.gifView.frame = self.bounds;
89 | if (self.isRefreshingTitleHidden) {
90 | self.gifView.contentMode = UIViewContentModeCenter;
91 | } else {
92 | self.gifView.contentMode = UIViewContentModeRight;
93 | self.gifView.mj_w = self.mj_w * 0.5 - self.labelLeftInset - self.stateLabel.mj_textWidth * 0.5;
94 | }
95 | }
96 |
97 | - (void)setState:(MJRefreshState)state
98 | {
99 | MJRefreshCheckState
100 |
101 | // 根据状态做事情
102 | if (state == MJRefreshStateRefreshing) {
103 | NSArray *images = self.stateImages[@(state)];
104 | if (images.count == 0) return;
105 | [self.gifView stopAnimating];
106 |
107 | self.gifView.hidden = NO;
108 | if (images.count == 1) { // 单张图片
109 | self.gifView.image = [images lastObject];
110 | } else { // 多张图片
111 | self.gifView.animationImages = images;
112 | self.gifView.animationDuration = [self.stateDurations[@(state)] doubleValue];
113 | [self.gifView startAnimating];
114 | }
115 | } else if (state == MJRefreshStateNoMoreData || state == MJRefreshStateIdle) {
116 | [self.gifView stopAnimating];
117 | self.gifView.hidden = YES;
118 | }
119 | }
120 | @end
121 |
122 |
--------------------------------------------------------------------------------
/ios/MJRefresh/Custom/Footer/Auto/MJRefreshAutoNormalFooter.h:
--------------------------------------------------------------------------------
1 | //
2 | // MJRefreshAutoNormalFooter.h
3 | // MJRefresh
4 | //
5 | // Created by MJ Lee on 15/4/24.
6 | // Copyright (c) 2015年 小码哥. All rights reserved.
7 | //
8 |
9 | #if __has_include()
10 | #import
11 | #else
12 | #import "MJRefreshAutoStateFooter.h"
13 | #endif
14 |
15 | NS_ASSUME_NONNULL_BEGIN
16 |
17 | @interface MJRefreshAutoNormalFooter : MJRefreshAutoStateFooter
18 | @property (weak, nonatomic, readonly) UIActivityIndicatorView *loadingView;
19 |
20 | /** 菊花的样式 */
21 | @property (assign, nonatomic) UIActivityIndicatorViewStyle activityIndicatorViewStyle MJRefreshDeprecated("first deprecated in 3.2.2 - Use `loadingView` property");
22 | @end
23 |
24 |
25 | NS_ASSUME_NONNULL_END
26 |
--------------------------------------------------------------------------------
/ios/MJRefresh/Custom/Footer/Auto/MJRefreshAutoNormalFooter.m:
--------------------------------------------------------------------------------
1 | //
2 | // MJRefreshAutoNormalFooter.m
3 | // MJRefresh
4 | //
5 | // Created by MJ Lee on 15/4/24.
6 | // Copyright (c) 2015年 小码哥. All rights reserved.
7 | //
8 |
9 | #import "MJRefreshAutoNormalFooter.h"
10 | #import "NSBundle+MJRefresh.h"
11 | #import "UIView+MJExtension.h"
12 | #import "UIScrollView+MJExtension.h"
13 | #import "UIScrollView+MJRefresh.h"
14 |
15 | @interface MJRefreshAutoNormalFooter()
16 | @property (weak, nonatomic) UIActivityIndicatorView *loadingView;
17 | @end
18 |
19 | @implementation MJRefreshAutoNormalFooter
20 | #pragma mark - 懒加载子控件
21 | - (UIActivityIndicatorView *)loadingView
22 | {
23 | if (!_loadingView) {
24 | UIActivityIndicatorView *loadingView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:_activityIndicatorViewStyle];
25 | loadingView.hidesWhenStopped = YES;
26 | [self addSubview:_loadingView = loadingView];
27 | }
28 | return _loadingView;
29 | }
30 |
31 | - (void)setActivityIndicatorViewStyle:(UIActivityIndicatorViewStyle)activityIndicatorViewStyle
32 | {
33 | _activityIndicatorViewStyle = activityIndicatorViewStyle;
34 |
35 | [self.loadingView removeFromSuperview];
36 | self.loadingView = nil;
37 | [self setNeedsLayout];
38 | }
39 | #pragma mark - 重写父类的方法
40 | - (void)prepare
41 | {
42 | [super prepare];
43 |
44 | #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000
45 | if (@available(iOS 13.0, *)) {
46 | _activityIndicatorViewStyle = UIActivityIndicatorViewStyleMedium;
47 | return;
48 | }
49 | #endif
50 |
51 | _activityIndicatorViewStyle = UIActivityIndicatorViewStyleGray;
52 | }
53 |
54 | - (void)placeSubviews
55 | {
56 | [super placeSubviews];
57 |
58 | if (self.loadingView.constraints.count) return;
59 |
60 | // 圈圈
61 | CGFloat loadingCenterX = self.mj_w * 0.5;
62 | if (!self.isRefreshingTitleHidden) {
63 | loadingCenterX -= self.stateLabel.mj_textWidth * 0.5 + self.labelLeftInset;
64 | }
65 | CGFloat loadingCenterY = self.mj_h * 0.5;
66 | self.loadingView.center = CGPointMake(loadingCenterX, loadingCenterY);
67 | }
68 |
69 | - (void)setState:(MJRefreshState)state
70 | {
71 | MJRefreshCheckState
72 |
73 | // 根据状态做事情
74 | if (state == MJRefreshStateNoMoreData || state == MJRefreshStateIdle) {
75 | [self.loadingView stopAnimating];
76 | } else if (state == MJRefreshStateRefreshing) {
77 | [self.loadingView startAnimating];
78 | }
79 | }
80 |
81 | @end
82 |
--------------------------------------------------------------------------------
/ios/MJRefresh/Custom/Footer/Auto/MJRefreshAutoStateFooter.h:
--------------------------------------------------------------------------------
1 | //
2 | // MJRefreshAutoStateFooter.h
3 | // MJRefresh
4 | //
5 | // Created by MJ Lee on 15/6/13.
6 | // Copyright © 2015年 小码哥. All rights reserved.
7 | //
8 |
9 | #if __has_include()
10 | #import
11 | #else
12 | #import "MJRefreshAutoFooter.h"
13 | #endif
14 |
15 | NS_ASSUME_NONNULL_BEGIN
16 |
17 | @interface MJRefreshAutoStateFooter : MJRefreshAutoFooter
18 | /** 文字距离圈圈、箭头的距离 */
19 | @property (assign, nonatomic) CGFloat labelLeftInset;
20 | /** 显示刷新状态的label */
21 | @property (weak, nonatomic, readonly) UILabel *stateLabel;
22 |
23 | /** 设置state状态下的文字 */
24 | - (instancetype)setTitle:(NSString *)title forState:(MJRefreshState)state;
25 |
26 | /** 隐藏刷新状态的文字 */
27 | @property (assign, nonatomic, getter=isRefreshingTitleHidden) BOOL refreshingTitleHidden;
28 | @end
29 |
30 | NS_ASSUME_NONNULL_END
31 |
--------------------------------------------------------------------------------
/ios/MJRefresh/Custom/Footer/Auto/MJRefreshAutoStateFooter.m:
--------------------------------------------------------------------------------
1 | //
2 | // MJRefreshAutoStateFooter.m
3 | // MJRefresh
4 | //
5 | // Created by MJ Lee on 15/6/13.
6 | // Copyright © 2015年 小码哥. All rights reserved.
7 | //
8 |
9 | #import "MJRefreshAutoStateFooter.h"
10 | #import "NSBundle+MJRefresh.h"
11 |
12 | @interface MJRefreshAutoFooter (TapTriggerFix)
13 |
14 | - (void)beginRefreshingWithoutValidation;
15 | @end
16 |
17 |
18 | @implementation MJRefreshAutoFooter (TapTriggerFix)
19 |
20 | - (void)beginRefreshingWithoutValidation {
21 | [super beginRefreshing];
22 | }
23 |
24 | @end
25 |
26 | @interface MJRefreshAutoStateFooter()
27 | {
28 | /** 显示刷新状态的label */
29 | __unsafe_unretained UILabel *_stateLabel;
30 | }
31 | /** 所有状态对应的文字 */
32 | @property (strong, nonatomic) NSMutableDictionary *stateTitles;
33 | @end
34 |
35 | @implementation MJRefreshAutoStateFooter
36 | #pragma mark - 懒加载
37 | - (NSMutableDictionary *)stateTitles
38 | {
39 | if (!_stateTitles) {
40 | self.stateTitles = [NSMutableDictionary dictionary];
41 | }
42 | return _stateTitles;
43 | }
44 |
45 | - (UILabel *)stateLabel
46 | {
47 | if (!_stateLabel) {
48 | [self addSubview:_stateLabel = [UILabel mj_label]];
49 | }
50 | return _stateLabel;
51 | }
52 |
53 | #pragma mark - 公共方法
54 | - (instancetype)setTitle:(NSString *)title forState:(MJRefreshState)state
55 | {
56 | if (title == nil) return self;
57 | self.stateTitles[@(state)] = title;
58 | self.stateLabel.text = self.stateTitles[@(self.state)];
59 | return self;
60 | }
61 |
62 | #pragma mark - 私有方法
63 | - (void)stateLabelClick
64 | {
65 | if (self.state == MJRefreshStateIdle) {
66 | [super beginRefreshingWithoutValidation];
67 | }
68 | }
69 |
70 | - (void)textConfiguration {
71 | // 初始化文字
72 | [self setTitle:[NSBundle mj_localizedStringForKey:MJRefreshAutoFooterIdleText] forState:MJRefreshStateIdle];
73 | [self setTitle:[NSBundle mj_localizedStringForKey:MJRefreshAutoFooterRefreshingText] forState:MJRefreshStateRefreshing];
74 | [self setTitle:[NSBundle mj_localizedStringForKey:MJRefreshAutoFooterNoMoreDataText] forState:MJRefreshStateNoMoreData];
75 | }
76 |
77 | #pragma mark - 重写父类的方法
78 | - (void)prepare
79 | {
80 | [super prepare];
81 |
82 | // 初始化间距
83 | self.labelLeftInset = MJRefreshLabelLeftInset;
84 |
85 | [self textConfiguration];
86 |
87 | // 监听label
88 | self.stateLabel.userInteractionEnabled = YES;
89 | [self.stateLabel addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(stateLabelClick)]];
90 | }
91 |
92 | - (void)i18nDidChange {
93 | [self textConfiguration];
94 |
95 | [super i18nDidChange];
96 | }
97 |
98 |
99 | - (void)placeSubviews
100 | {
101 | [super placeSubviews];
102 |
103 | if (self.stateLabel.constraints.count) return;
104 |
105 | // 状态标签
106 | self.stateLabel.frame = self.bounds;
107 | }
108 |
109 | - (void)setState:(MJRefreshState)state
110 | {
111 | MJRefreshCheckState
112 |
113 | if (self.isRefreshingTitleHidden && state == MJRefreshStateRefreshing) {
114 | self.stateLabel.text = nil;
115 | } else {
116 | self.stateLabel.text = self.stateTitles[@(state)];
117 | }
118 | }
119 | @end
120 |
--------------------------------------------------------------------------------
/ios/MJRefresh/Custom/Footer/Back/MJRefreshBackGifFooter.h:
--------------------------------------------------------------------------------
1 | //
2 | // MJRefreshBackGifFooter.h
3 | // MJRefresh
4 | //
5 | // Created by MJ Lee on 15/4/24.
6 | // Copyright (c) 2015年 小码哥. All rights reserved.
7 | //
8 |
9 | #if __has_include()
10 | #import
11 | #else
12 | #import "MJRefreshBackStateFooter.h"
13 | #endif
14 |
15 | NS_ASSUME_NONNULL_BEGIN
16 |
17 | @interface MJRefreshBackGifFooter : MJRefreshBackStateFooter
18 | @property (weak, nonatomic, readonly) UIImageView *gifView;
19 |
20 | /** 设置state状态下的动画图片images 动画持续时间duration*/
21 | - (instancetype)setImages:(NSArray *)images duration:(NSTimeInterval)duration forState:(MJRefreshState)state;
22 | - (instancetype)setImages:(NSArray *)images forState:(MJRefreshState)state;
23 | @end
24 |
25 | NS_ASSUME_NONNULL_END
26 |
--------------------------------------------------------------------------------
/ios/MJRefresh/Custom/Footer/Back/MJRefreshBackGifFooter.m:
--------------------------------------------------------------------------------
1 | //
2 | // MJRefreshBackGifFooter.m
3 | // MJRefresh
4 | //
5 | // Created by MJ Lee on 15/4/24.
6 | // Copyright (c) 2015年 小码哥. All rights reserved.
7 | //
8 |
9 | #import "MJRefreshBackGifFooter.h"
10 | #import "NSBundle+MJRefresh.h"
11 | #import "UIView+MJExtension.h"
12 | #import "UIScrollView+MJExtension.h"
13 | #import "UIScrollView+MJRefresh.h"
14 |
15 | @interface MJRefreshBackGifFooter()
16 | {
17 | __unsafe_unretained UIImageView *_gifView;
18 | }
19 | /** 所有状态对应的动画图片 */
20 | @property (strong, nonatomic) NSMutableDictionary *stateImages;
21 | /** 所有状态对应的动画时间 */
22 | @property (strong, nonatomic) NSMutableDictionary *stateDurations;
23 | @end
24 |
25 | @implementation MJRefreshBackGifFooter
26 | #pragma mark - 懒加载
27 | - (UIImageView *)gifView
28 | {
29 | if (!_gifView) {
30 | UIImageView *gifView = [[UIImageView alloc] init];
31 | [self addSubview:_gifView = gifView];
32 | }
33 | return _gifView;
34 | }
35 |
36 | - (NSMutableDictionary *)stateImages
37 | {
38 | if (!_stateImages) {
39 | self.stateImages = [NSMutableDictionary dictionary];
40 | }
41 | return _stateImages;
42 | }
43 |
44 | - (NSMutableDictionary *)stateDurations
45 | {
46 | if (!_stateDurations) {
47 | self.stateDurations = [NSMutableDictionary dictionary];
48 | }
49 | return _stateDurations;
50 | }
51 |
52 | #pragma mark - 公共方法
53 | - (instancetype)setImages:(NSArray *)images duration:(NSTimeInterval)duration forState:(MJRefreshState)state
54 | {
55 | if (images == nil) return self;
56 |
57 | self.stateImages[@(state)] = images;
58 | self.stateDurations[@(state)] = @(duration);
59 |
60 | /* 根据图片设置控件的高度 */
61 | UIImage *image = [images firstObject];
62 | if (image.size.height > self.mj_h) {
63 | self.mj_h = image.size.height;
64 | }
65 | return self;
66 | }
67 |
68 | - (instancetype)setImages:(NSArray *)images forState:(MJRefreshState)state
69 | {
70 | return [self setImages:images duration:images.count * 0.1 forState:state];
71 | }
72 |
73 | #pragma mark - 实现父类的方法
74 | - (void)prepare
75 | {
76 | [super prepare];
77 |
78 | // 初始化间距
79 | self.labelLeftInset = 20;
80 | }
81 |
82 | - (void)setPullingPercent:(CGFloat)pullingPercent
83 | {
84 | [super setPullingPercent:pullingPercent];
85 | NSArray *images = self.stateImages[@(MJRefreshStateIdle)];
86 | if (self.state != MJRefreshStateIdle || images.count == 0) return;
87 | [self.gifView stopAnimating];
88 | NSUInteger index = images.count * pullingPercent;
89 | if (index >= images.count) index = images.count - 1;
90 | self.gifView.image = images[index];
91 | }
92 |
93 | - (void)placeSubviews
94 | {
95 | [super placeSubviews];
96 |
97 | if (self.gifView.constraints.count) return;
98 |
99 | self.gifView.frame = self.bounds;
100 | if (self.stateLabel.hidden) {
101 | self.gifView.contentMode = UIViewContentModeCenter;
102 | } else {
103 | self.gifView.contentMode = UIViewContentModeRight;
104 | self.gifView.mj_w = self.mj_w * 0.5 - self.labelLeftInset - self.stateLabel.mj_textWidth * 0.5;
105 | }
106 | }
107 |
108 | - (void)setState:(MJRefreshState)state
109 | {
110 | MJRefreshCheckState
111 |
112 | // 根据状态做事情
113 | if (state == MJRefreshStatePulling || state == MJRefreshStateRefreshing) {
114 | NSArray *images = self.stateImages[@(state)];
115 | if (images.count == 0) return;
116 |
117 | self.gifView.hidden = NO;
118 | [self.gifView stopAnimating];
119 | if (images.count == 1) { // 单张图片
120 | self.gifView.image = [images lastObject];
121 | } else { // 多张图片
122 | self.gifView.animationImages = images;
123 | self.gifView.animationDuration = [self.stateDurations[@(state)] doubleValue];
124 | [self.gifView startAnimating];
125 | }
126 | } else if (state == MJRefreshStateIdle) {
127 | self.gifView.hidden = NO;
128 | } else if (state == MJRefreshStateNoMoreData) {
129 | self.gifView.hidden = YES;
130 | }
131 | }
132 | @end
133 |
--------------------------------------------------------------------------------
/ios/MJRefresh/Custom/Footer/Back/MJRefreshBackNormalFooter.h:
--------------------------------------------------------------------------------
1 | //
2 | // MJRefreshBackNormalFooter.h
3 | // MJRefresh
4 | //
5 | // Created by MJ Lee on 15/4/24.
6 | // Copyright (c) 2015年 小码哥. All rights reserved.
7 | //
8 |
9 | #if __has_include()
10 | #import
11 | #else
12 | #import "MJRefreshBackStateFooter.h"
13 | #endif
14 |
15 | NS_ASSUME_NONNULL_BEGIN
16 |
17 | @interface MJRefreshBackNormalFooter : MJRefreshBackStateFooter
18 | @property (weak, nonatomic, readonly) UIImageView *arrowView;
19 | @property (weak, nonatomic, readonly) UIActivityIndicatorView *loadingView;
20 |
21 | /** 菊花的样式 */
22 | @property (assign, nonatomic) UIActivityIndicatorViewStyle activityIndicatorViewStyle MJRefreshDeprecated("first deprecated in 3.2.2 - Use `loadingView` property");
23 | @end
24 |
25 | NS_ASSUME_NONNULL_END
26 |
--------------------------------------------------------------------------------
/ios/MJRefresh/Custom/Footer/Back/MJRefreshBackNormalFooter.m:
--------------------------------------------------------------------------------
1 | //
2 | // MJRefreshBackNormalFooter.m
3 | // MJRefresh
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 | #import "UIView+MJExtension.h"
12 |
13 | @interface MJRefreshBackNormalFooter()
14 | {
15 | __unsafe_unretained UIImageView *_arrowView;
16 | }
17 | @property (weak, nonatomic) UIActivityIndicatorView *loadingView;
18 | @end
19 |
20 | @implementation MJRefreshBackNormalFooter
21 | #pragma mark - 懒加载子控件
22 | - (UIImageView *)arrowView
23 | {
24 | if (!_arrowView) {
25 | UIImageView *arrowView = [[UIImageView alloc] initWithImage:[NSBundle mj_arrowImage]];
26 | [self addSubview:_arrowView = arrowView];
27 | }
28 | return _arrowView;
29 | }
30 |
31 |
32 | - (UIActivityIndicatorView *)loadingView
33 | {
34 | if (!_loadingView) {
35 | UIActivityIndicatorView *loadingView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:_activityIndicatorViewStyle];
36 | loadingView.hidesWhenStopped = YES;
37 | [self addSubview:_loadingView = loadingView];
38 | }
39 | return _loadingView;
40 | }
41 |
42 | - (void)setActivityIndicatorViewStyle:(UIActivityIndicatorViewStyle)activityIndicatorViewStyle
43 | {
44 | _activityIndicatorViewStyle = activityIndicatorViewStyle;
45 |
46 | [self.loadingView removeFromSuperview];
47 | self.loadingView = nil;
48 | [self setNeedsLayout];
49 | }
50 | #pragma mark - 重写父类的方法
51 | - (void)prepare
52 | {
53 | [super prepare];
54 |
55 | #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000
56 | if (@available(iOS 13.0, *)) {
57 | _activityIndicatorViewStyle = UIActivityIndicatorViewStyleMedium;
58 | return;
59 | }
60 | #endif
61 |
62 | _activityIndicatorViewStyle = UIActivityIndicatorViewStyleGray;
63 | }
64 |
65 | - (void)placeSubviews
66 | {
67 | [super placeSubviews];
68 |
69 | // 箭头的中心点
70 | CGFloat arrowCenterX = self.mj_w * 0.5;
71 | if (!self.stateLabel.hidden) {
72 | arrowCenterX -= self.labelLeftInset + self.stateLabel.mj_textWidth * 0.5;
73 | }
74 | CGFloat arrowCenterY = self.mj_h * 0.5;
75 | CGPoint arrowCenter = CGPointMake(arrowCenterX, arrowCenterY);
76 |
77 | // 箭头
78 | if (self.arrowView.constraints.count == 0) {
79 | self.arrowView.mj_size = self.arrowView.image.size;
80 | self.arrowView.center = arrowCenter;
81 | }
82 |
83 | // 圈圈
84 | if (self.loadingView.constraints.count == 0) {
85 | self.loadingView.center = arrowCenter;
86 | }
87 |
88 | self.arrowView.tintColor = self.stateLabel.textColor;
89 | }
90 |
91 | - (void)setState:(MJRefreshState)state
92 | {
93 | MJRefreshCheckState
94 |
95 | // 根据状态做事情
96 | if (state == MJRefreshStateIdle) {
97 | if (oldState == MJRefreshStateRefreshing) {
98 | self.arrowView.transform = CGAffineTransformMakeRotation(0.000001 - M_PI);
99 | [UIView animateWithDuration:self.slowAnimationDuration animations:^{
100 | self.loadingView.alpha = 0.0;
101 | } completion:^(BOOL finished) {
102 | // 防止动画结束后,状态已经不是MJRefreshStateIdle
103 | if (self.state != MJRefreshStateIdle) return;
104 |
105 | self.loadingView.alpha = 1.0;
106 | [self.loadingView stopAnimating];
107 |
108 | self.arrowView.hidden = NO;
109 | }];
110 | } else {
111 | self.arrowView.hidden = NO;
112 | [self.loadingView stopAnimating];
113 | [UIView animateWithDuration:self.fastAnimationDuration animations:^{
114 | self.arrowView.transform = CGAffineTransformMakeRotation(0.000001 - M_PI);
115 | }];
116 | }
117 | } else if (state == MJRefreshStatePulling) {
118 | self.arrowView.hidden = NO;
119 | [self.loadingView stopAnimating];
120 | [UIView animateWithDuration:self.fastAnimationDuration animations:^{
121 | self.arrowView.transform = CGAffineTransformIdentity;
122 | }];
123 | } else if (state == MJRefreshStateRefreshing) {
124 | self.arrowView.hidden = YES;
125 | [self.loadingView startAnimating];
126 | } else if (state == MJRefreshStateNoMoreData) {
127 | self.arrowView.hidden = YES;
128 | [self.loadingView stopAnimating];
129 | }
130 | }
131 |
132 | @end
133 |
--------------------------------------------------------------------------------
/ios/MJRefresh/Custom/Footer/Back/MJRefreshBackStateFooter.h:
--------------------------------------------------------------------------------
1 | //
2 | // MJRefreshBackStateFooter.h
3 | // MJRefresh
4 | //
5 | // Created by MJ Lee on 15/6/13.
6 | // Copyright © 2015年 小码哥. All rights reserved.
7 | //
8 |
9 | #if __has_include()
10 | #import
11 | #else
12 | #import "MJRefreshBackFooter.h"
13 | #endif
14 |
15 | NS_ASSUME_NONNULL_BEGIN
16 |
17 | @interface MJRefreshBackStateFooter : MJRefreshBackFooter
18 | /** 文字距离圈圈、箭头的距离 */
19 | @property (assign, nonatomic) CGFloat labelLeftInset;
20 | /** 显示刷新状态的label */
21 | @property (weak, nonatomic, readonly) UILabel *stateLabel;
22 | /** 设置state状态下的文字 */
23 | - (instancetype)setTitle:(NSString *)title forState:(MJRefreshState)state;
24 |
25 | /** 获取state状态下的title */
26 | - (NSString *)titleForState:(MJRefreshState)state;
27 | @end
28 |
29 | NS_ASSUME_NONNULL_END
30 |
--------------------------------------------------------------------------------
/ios/MJRefresh/Custom/Footer/Back/MJRefreshBackStateFooter.m:
--------------------------------------------------------------------------------
1 | //
2 | // MJRefreshBackStateFooter.m
3 | // MJRefresh
4 | //
5 | // Created by MJ Lee on 15/6/13.
6 | // Copyright © 2015年 小码哥. All rights reserved.
7 | //
8 |
9 | #import "MJRefreshBackStateFooter.h"
10 | #import "NSBundle+MJRefresh.h"
11 |
12 | @interface MJRefreshBackStateFooter()
13 | {
14 | /** 显示刷新状态的label */
15 | __unsafe_unretained UILabel *_stateLabel;
16 | }
17 | /** 所有状态对应的文字 */
18 | @property (strong, nonatomic) NSMutableDictionary *stateTitles;
19 | @end
20 |
21 | @implementation MJRefreshBackStateFooter
22 | #pragma mark - 懒加载
23 | - (NSMutableDictionary *)stateTitles
24 | {
25 | if (!_stateTitles) {
26 | self.stateTitles = [NSMutableDictionary dictionary];
27 | }
28 | return _stateTitles;
29 | }
30 |
31 | - (UILabel *)stateLabel
32 | {
33 | if (!_stateLabel) {
34 | [self addSubview:_stateLabel = [UILabel mj_label]];
35 | }
36 | return _stateLabel;
37 | }
38 |
39 | #pragma mark - 公共方法
40 | - (instancetype)setTitle:(NSString *)title forState:(MJRefreshState)state
41 | {
42 | if (title == nil) return self;
43 | self.stateTitles[@(state)] = title;
44 | self.stateLabel.text = self.stateTitles[@(self.state)];
45 | return self;
46 | }
47 |
48 | - (NSString *)titleForState:(MJRefreshState)state {
49 | return self.stateTitles[@(state)];
50 | }
51 |
52 | - (void)textConfiguration {
53 | // 初始化文字
54 | [self setTitle:[NSBundle mj_localizedStringForKey:MJRefreshBackFooterIdleText] forState:MJRefreshStateIdle];
55 | [self setTitle:[NSBundle mj_localizedStringForKey:MJRefreshBackFooterPullingText] forState:MJRefreshStatePulling];
56 | [self setTitle:[NSBundle mj_localizedStringForKey:MJRefreshBackFooterRefreshingText] forState:MJRefreshStateRefreshing];
57 | [self setTitle:[NSBundle mj_localizedStringForKey:MJRefreshBackFooterNoMoreDataText] forState:MJRefreshStateNoMoreData];
58 | }
59 |
60 | #pragma mark - 重写父类的方法
61 | - (void)prepare
62 | {
63 | [super prepare];
64 |
65 | // 初始化间距
66 | self.labelLeftInset = MJRefreshLabelLeftInset;
67 | [self textConfiguration];
68 | }
69 |
70 | - (void)i18nDidChange {
71 | [self textConfiguration];
72 |
73 | [super i18nDidChange];
74 | }
75 |
76 | - (void)placeSubviews
77 | {
78 | [super placeSubviews];
79 |
80 | if (self.stateLabel.constraints.count) return;
81 |
82 | // 状态标签
83 | self.stateLabel.frame = self.bounds;
84 | }
85 |
86 | - (void)setState:(MJRefreshState)state
87 | {
88 | MJRefreshCheckState
89 |
90 | // 设置状态文字
91 | self.stateLabel.text = self.stateTitles[@(state)];
92 | }
93 | @end
94 |
--------------------------------------------------------------------------------
/ios/MJRefresh/Custom/Header/MJRefreshGifHeader.h:
--------------------------------------------------------------------------------
1 | //
2 | // MJRefreshGifHeader.h
3 | // MJRefresh
4 | //
5 | // Created by MJ Lee on 15/4/24.
6 | // Copyright (c) 2015年 小码哥. All rights reserved.
7 | //
8 |
9 | #if __has_include()
10 | #import
11 | #else
12 | #import "MJRefreshStateHeader.h"
13 | #endif
14 |
15 | NS_ASSUME_NONNULL_BEGIN
16 |
17 | @interface MJRefreshGifHeader : MJRefreshStateHeader
18 | @property (weak, nonatomic, readonly) UIImageView *gifView;
19 |
20 | /** 设置state状态下的动画图片images 动画持续时间duration*/
21 | - (instancetype)setImages:(NSArray *)images duration:(NSTimeInterval)duration forState:(MJRefreshState)state;
22 | - (instancetype)setImages:(NSArray *)images forState:(MJRefreshState)state;
23 | @end
24 |
25 | NS_ASSUME_NONNULL_END
26 |
--------------------------------------------------------------------------------
/ios/MJRefresh/Custom/Header/MJRefreshGifHeader.m:
--------------------------------------------------------------------------------
1 | //
2 | // MJRefreshGifHeader.m
3 | // MJRefresh
4 | //
5 | // Created by MJ Lee on 15/4/24.
6 | // Copyright (c) 2015年 小码哥. All rights reserved.
7 | //
8 |
9 | #import "MJRefreshGifHeader.h"
10 | #import "UIView+MJExtension.h"
11 | #import "UIScrollView+MJExtension.h"
12 |
13 | @interface MJRefreshGifHeader()
14 | {
15 | __unsafe_unretained UIImageView *_gifView;
16 | }
17 | /** 所有状态对应的动画图片 */
18 | @property (strong, nonatomic) NSMutableDictionary *stateImages;
19 | /** 所有状态对应的动画时间 */
20 | @property (strong, nonatomic) NSMutableDictionary *stateDurations;
21 | @end
22 |
23 | @implementation MJRefreshGifHeader
24 | #pragma mark - 懒加载
25 | - (UIImageView *)gifView
26 | {
27 | if (!_gifView) {
28 | UIImageView *gifView = [[UIImageView alloc] init];
29 | [self addSubview:_gifView = gifView];
30 | }
31 | return _gifView;
32 | }
33 |
34 | - (NSMutableDictionary *)stateImages
35 | {
36 | if (!_stateImages) {
37 | self.stateImages = [NSMutableDictionary dictionary];
38 | }
39 | return _stateImages;
40 | }
41 |
42 | - (NSMutableDictionary *)stateDurations
43 | {
44 | if (!_stateDurations) {
45 | self.stateDurations = [NSMutableDictionary dictionary];
46 | }
47 | return _stateDurations;
48 | }
49 |
50 | #pragma mark - 公共方法
51 | - (instancetype)setImages:(NSArray *)images duration:(NSTimeInterval)duration forState:(MJRefreshState)state {
52 | if (images == nil) return self;
53 |
54 | self.stateImages[@(state)] = images;
55 | self.stateDurations[@(state)] = @(duration);
56 |
57 | /* 根据图片设置控件的高度 */
58 | UIImage *image = [images firstObject];
59 | if (image.size.height > self.mj_h) {
60 | self.mj_h = image.size.height;
61 | }
62 | return self;
63 | }
64 |
65 | - (instancetype)setImages:(NSArray *)images forState:(MJRefreshState)state
66 | {
67 | return [self setImages:images duration:images.count * 0.1 forState:state];
68 | }
69 |
70 | #pragma mark - 实现父类的方法
71 | - (void)prepare
72 | {
73 | [super prepare];
74 |
75 | // 初始化间距
76 | self.labelLeftInset = 20;
77 | }
78 |
79 | - (void)setPullingPercent:(CGFloat)pullingPercent
80 | {
81 | [super setPullingPercent:pullingPercent];
82 | NSArray *images = self.stateImages[@(MJRefreshStateIdle)];
83 | if (self.state != MJRefreshStateIdle || images.count == 0) return;
84 | // 停止动画
85 | [self.gifView stopAnimating];
86 | // 设置当前需要显示的图片
87 | NSUInteger index = images.count * pullingPercent;
88 | if (index >= images.count) index = images.count - 1;
89 | self.gifView.image = images[index];
90 | }
91 |
92 | - (void)placeSubviews
93 | {
94 | [super placeSubviews];
95 |
96 | if (self.gifView.constraints.count) return;
97 |
98 | self.gifView.frame = self.bounds;
99 | if (self.stateLabel.hidden && self.lastUpdatedTimeLabel.hidden) {
100 | self.gifView.contentMode = UIViewContentModeCenter;
101 | } else {
102 | self.gifView.contentMode = UIViewContentModeRight;
103 |
104 | CGFloat stateWidth = self.stateLabel.mj_textWidth;
105 | CGFloat timeWidth = 0.0;
106 | if (!self.lastUpdatedTimeLabel.hidden) {
107 | timeWidth = self.lastUpdatedTimeLabel.mj_textWidth;
108 | }
109 | CGFloat textWidth = MAX(stateWidth, timeWidth);
110 | self.gifView.mj_w = self.mj_w * 0.5 - textWidth * 0.5 - self.labelLeftInset;
111 | }
112 | }
113 |
114 | - (void)setState:(MJRefreshState)state
115 | {
116 | MJRefreshCheckState
117 |
118 | // 根据状态做事情
119 | if (state == MJRefreshStatePulling || state == MJRefreshStateRefreshing) {
120 | NSArray *images = self.stateImages[@(state)];
121 | if (images.count == 0) return;
122 |
123 | [self.gifView stopAnimating];
124 | if (images.count == 1) { // 单张图片
125 | self.gifView.image = [images lastObject];
126 | } else { // 多张图片
127 | self.gifView.animationImages = images;
128 | self.gifView.animationDuration = [self.stateDurations[@(state)] doubleValue];
129 | [self.gifView startAnimating];
130 | }
131 | } else if (state == MJRefreshStateIdle) {
132 | [self.gifView stopAnimating];
133 | }
134 | }
135 | @end
136 |
--------------------------------------------------------------------------------
/ios/MJRefresh/Custom/Header/MJRefreshNormalHeader.h:
--------------------------------------------------------------------------------
1 | //
2 | // MJRefreshNormalHeader.h
3 | // MJRefresh
4 | //
5 | // Created by MJ Lee on 15/4/24.
6 | // Copyright (c) 2015年 小码哥. All rights reserved.
7 | //
8 |
9 | #if __has_include()
10 | #import
11 | #else
12 | #import "MJRefreshStateHeader.h"
13 | #endif
14 |
15 | NS_ASSUME_NONNULL_BEGIN
16 |
17 | @interface MJRefreshNormalHeader : MJRefreshStateHeader
18 | @property (weak, nonatomic, readonly) UIImageView *arrowView;
19 | @property (weak, nonatomic, readonly) UIActivityIndicatorView *loadingView;
20 |
21 |
22 | /** 菊花的样式 */
23 | @property (assign, nonatomic) UIActivityIndicatorViewStyle activityIndicatorViewStyle MJRefreshDeprecated("first deprecated in 3.2.2 - Use `loadingView` property");
24 | @end
25 |
26 | NS_ASSUME_NONNULL_END
27 |
--------------------------------------------------------------------------------
/ios/MJRefresh/Custom/Header/MJRefreshNormalHeader.m:
--------------------------------------------------------------------------------
1 | //
2 | // MJRefreshNormalHeader.m
3 | // MJRefresh
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 | #import "UIScrollView+MJRefresh.h"
12 | #import "UIView+MJExtension.h"
13 |
14 | @interface MJRefreshNormalHeader()
15 | {
16 | __unsafe_unretained UIImageView *_arrowView;
17 | }
18 | @property (weak, nonatomic) UIActivityIndicatorView *loadingView;
19 | @end
20 |
21 | @implementation MJRefreshNormalHeader
22 | #pragma mark - 懒加载子控件
23 | - (UIImageView *)arrowView
24 | {
25 | if (!_arrowView) {
26 | UIImageView *arrowView = [[UIImageView alloc] initWithImage:[NSBundle mj_arrowImage]];
27 | [self addSubview:_arrowView = arrowView];
28 | }
29 | return _arrowView;
30 | }
31 |
32 | - (UIActivityIndicatorView *)loadingView
33 | {
34 | if (!_loadingView) {
35 | UIActivityIndicatorView *loadingView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:_activityIndicatorViewStyle];
36 | loadingView.hidesWhenStopped = YES;
37 | [self addSubview:_loadingView = loadingView];
38 | }
39 | return _loadingView;
40 | }
41 |
42 | #pragma mark - 公共方法
43 | - (void)setActivityIndicatorViewStyle:(UIActivityIndicatorViewStyle)activityIndicatorViewStyle
44 | {
45 | _activityIndicatorViewStyle = activityIndicatorViewStyle;
46 |
47 | [self.loadingView removeFromSuperview];
48 | self.loadingView = nil;
49 | [self setNeedsLayout];
50 | }
51 |
52 | #pragma mark - 重写父类的方法
53 | - (void)prepare
54 | {
55 | [super prepare];
56 |
57 | #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000
58 | if (@available(iOS 13.0, *)) {
59 | _activityIndicatorViewStyle = UIActivityIndicatorViewStyleMedium;
60 | return;
61 | }
62 | #endif
63 |
64 | _activityIndicatorViewStyle = UIActivityIndicatorViewStyleGray;
65 | }
66 |
67 | - (void)placeSubviews
68 | {
69 | [super placeSubviews];
70 |
71 | // 箭头的中心点
72 | CGFloat arrowCenterX = self.mj_w * 0.5;
73 | if (!self.stateLabel.hidden) {
74 | CGFloat stateWidth = self.stateLabel.mj_textWidth;
75 | CGFloat timeWidth = 0.0;
76 | if (!self.lastUpdatedTimeLabel.hidden) {
77 | timeWidth = self.lastUpdatedTimeLabel.mj_textWidth;
78 | }
79 | CGFloat textWidth = MAX(stateWidth, timeWidth);
80 | arrowCenterX -= textWidth / 2 + self.labelLeftInset;
81 | }
82 | CGFloat arrowCenterY = self.mj_h * 0.5;
83 | CGPoint arrowCenter = CGPointMake(arrowCenterX, arrowCenterY);
84 |
85 | // 箭头
86 | if (self.arrowView.constraints.count == 0) {
87 | self.arrowView.mj_size = self.arrowView.image.size;
88 | self.arrowView.center = arrowCenter;
89 | }
90 |
91 | // 圈圈
92 | if (self.loadingView.constraints.count == 0) {
93 | self.loadingView.center = arrowCenter;
94 | }
95 |
96 | self.arrowView.tintColor = self.stateLabel.textColor;
97 | }
98 |
99 | - (void)setState:(MJRefreshState)state
100 | {
101 | MJRefreshCheckState
102 |
103 | // 根据状态做事情
104 | if (state == MJRefreshStateIdle) {
105 | if (oldState == MJRefreshStateRefreshing) {
106 | self.arrowView.transform = CGAffineTransformIdentity;
107 |
108 | [UIView animateWithDuration:self.slowAnimationDuration animations:^{
109 | self.loadingView.alpha = 0.0;
110 | } completion:^(BOOL finished) {
111 | // 如果执行完动画发现不是idle状态,就直接返回,进入其他状态
112 | if (self.state != MJRefreshStateIdle) return;
113 |
114 | self.loadingView.alpha = 1.0;
115 | [self.loadingView stopAnimating];
116 | self.arrowView.hidden = NO;
117 | }];
118 | } else {
119 | [self.loadingView stopAnimating];
120 | self.arrowView.hidden = NO;
121 | [UIView animateWithDuration:self.fastAnimationDuration animations:^{
122 | self.arrowView.transform = CGAffineTransformIdentity;
123 | }];
124 | }
125 | } else if (state == MJRefreshStatePulling) {
126 | [self.loadingView stopAnimating];
127 | self.arrowView.hidden = NO;
128 | [UIView animateWithDuration:self.fastAnimationDuration animations:^{
129 | self.arrowView.transform = CGAffineTransformMakeRotation(0.000001 - M_PI);
130 | }];
131 | } else if (state == MJRefreshStateRefreshing) {
132 | self.loadingView.alpha = 1.0; // 防止refreshing -> idle的动画完毕动作没有被执行
133 | [self.loadingView startAnimating];
134 | self.arrowView.hidden = YES;
135 | }
136 | }
137 | @end
138 |
--------------------------------------------------------------------------------
/ios/MJRefresh/Custom/Header/MJRefreshStateHeader.h:
--------------------------------------------------------------------------------
1 | //
2 | // MJRefreshStateHeader.h
3 | // MJRefresh
4 | //
5 | // Created by MJ Lee on 15/4/24.
6 | // Copyright (c) 2015年 小码哥. All rights reserved.
7 | //
8 |
9 | #if __has_include()
10 | #import
11 | #else
12 | #import "MJRefreshHeader.h"
13 | #endif
14 |
15 | NS_ASSUME_NONNULL_BEGIN
16 |
17 | @interface MJRefreshStateHeader : MJRefreshHeader
18 | #pragma mark - 刷新时间相关
19 | /** 利用这个block来决定显示的更新时间文字 */
20 | @property (copy, nonatomic, nullable) NSString *(^lastUpdatedTimeText)(NSDate * _Nullable lastUpdatedTime);
21 | /** 显示上一次刷新时间的label */
22 | @property (weak, nonatomic, readonly) UILabel *lastUpdatedTimeLabel;
23 |
24 | #pragma mark - 状态相关
25 | /** 文字距离圈圈、箭头的距离 */
26 | @property (assign, nonatomic) CGFloat labelLeftInset;
27 | /** 显示刷新状态的label */
28 | @property (weak, nonatomic, readonly) UILabel *stateLabel;
29 | /** 设置state状态下的文字 */
30 | - (instancetype)setTitle:(NSString *)title forState:(MJRefreshState)state;
31 | @end
32 |
33 | @interface MJRefreshStateHeader (ChainingGrammar)
34 |
35 | - (instancetype)modifyLastUpdatedTimeText:(NSString * (^)(NSDate * _Nullable lastUpdatedTime))handler;
36 |
37 | @end
38 |
39 | NS_ASSUME_NONNULL_END
40 |
--------------------------------------------------------------------------------
/ios/MJRefresh/Custom/Trailer/MJRefreshNormalTrailer.h:
--------------------------------------------------------------------------------
1 | //
2 | // MJRefreshNormalTrailer.h
3 | // MJRefresh
4 | //
5 | // Created by kinarobin on 2020/5/3.
6 | // Copyright © 2020 小码哥. All rights reserved.
7 | //
8 |
9 | #if __has_include()
10 | #import
11 | #else
12 | #import "MJRefreshStateTrailer.h"
13 | #endif
14 |
15 | NS_ASSUME_NONNULL_BEGIN
16 |
17 | @interface MJRefreshNormalTrailer : MJRefreshStateTrailer
18 |
19 | @property (weak, nonatomic, readonly) UIImageView *arrowView;
20 |
21 | @end
22 |
23 | NS_ASSUME_NONNULL_END
24 |
--------------------------------------------------------------------------------
/ios/MJRefresh/Custom/Trailer/MJRefreshNormalTrailer.m:
--------------------------------------------------------------------------------
1 | //
2 | // MJRefreshNormalTrailer.m
3 | // MJRefresh
4 | //
5 | // Created by kinarobin on 2020/5/3.
6 | // Copyright © 2020 小码哥. All rights reserved.
7 | //
8 |
9 | #import "MJRefreshNormalTrailer.h"
10 | #import "NSBundle+MJRefresh.h"
11 | #import "UIView+MJExtension.h"
12 |
13 | @interface MJRefreshNormalTrailer() {
14 | __unsafe_unretained UIImageView *_arrowView;
15 | }
16 | @end
17 |
18 | @implementation MJRefreshNormalTrailer
19 | #pragma mark - 懒加载子控件
20 | - (UIImageView *)arrowView {
21 | if (!_arrowView) {
22 | UIImageView *arrowView = [[UIImageView alloc] initWithImage:[NSBundle mj_trailArrowImage]];
23 | [self addSubview:_arrowView = arrowView];
24 | }
25 | return _arrowView;
26 | }
27 |
28 | - (void)placeSubviews {
29 | [super placeSubviews];
30 |
31 | CGSize arrowSize = self.arrowView.image.size;
32 | // 箭头的中心点
33 | CGPoint selfCenter = CGPointMake(self.mj_w * 0.5, self.mj_h * 0.5);
34 | CGPoint arrowCenter = CGPointMake(arrowSize.width * 0.5 + 5, self.mj_h * 0.5);
35 | BOOL stateHidden = self.stateLabel.isHidden;
36 |
37 | if (self.arrowView.constraints.count == 0) {
38 | self.arrowView.mj_size = self.arrowView.image.size;
39 | self.arrowView.center = stateHidden ? selfCenter : arrowCenter ;
40 | }
41 | self.arrowView.tintColor = self.stateLabel.textColor;
42 |
43 | if (stateHidden) return;
44 |
45 | BOOL noConstrainsOnStatusLabel = self.stateLabel.constraints.count == 0;
46 | CGFloat stateLabelW = ceil(self.stateLabel.font.pointSize);
47 | // 状态
48 | if (noConstrainsOnStatusLabel) {
49 | BOOL arrowHidden = self.arrowView.isHidden;
50 | CGFloat stateCenterX = (self.mj_w + arrowSize.width) * 0.5;
51 | self.stateLabel.center = arrowHidden ? selfCenter : CGPointMake(stateCenterX, self.mj_h * 0.5);
52 | self.stateLabel.mj_size = CGSizeMake(stateLabelW, self.mj_h) ;
53 | }
54 | }
55 |
56 | - (void)setState:(MJRefreshState)state {
57 | MJRefreshCheckState
58 | // 根据状态做事情
59 | if (state == MJRefreshStateIdle) {
60 | if (oldState == MJRefreshStateRefreshing) {
61 | [UIView animateWithDuration:self.fastAnimationDuration animations:^{
62 | self.arrowView.transform = CGAffineTransformMakeRotation(M_PI);
63 | } completion:^(BOOL finished) {
64 | self.arrowView.transform = CGAffineTransformIdentity;
65 | }];
66 | } else {
67 | [UIView animateWithDuration:self.fastAnimationDuration animations:^{
68 | self.arrowView.transform = CGAffineTransformIdentity;
69 | }];
70 | }
71 | } else if (state == MJRefreshStatePulling) {
72 | [UIView animateWithDuration:self.fastAnimationDuration animations:^{
73 | self.arrowView.transform = CGAffineTransformMakeRotation(M_PI);
74 | }];
75 | }
76 | }
77 |
78 |
79 |
80 | @end
81 |
--------------------------------------------------------------------------------
/ios/MJRefresh/Custom/Trailer/MJRefreshStateTrailer.h:
--------------------------------------------------------------------------------
1 | //
2 | // MJRefreshStateTrailer.h
3 | // MJRefresh
4 | //
5 | // Created by kinarobin on 2020/5/3.
6 | // Copyright © 2020 小码哥. All rights reserved.
7 | //
8 |
9 | #if __has_include()
10 | #import
11 | #else
12 | #import "MJRefreshTrailer.h"
13 | #endif
14 |
15 | NS_ASSUME_NONNULL_BEGIN
16 |
17 |
18 | @interface MJRefreshStateTrailer : MJRefreshTrailer
19 |
20 | #pragma mark - 状态相关
21 | /** 显示刷新状态的label */
22 | @property (weak, nonatomic, readonly) UILabel *stateLabel;
23 | /** 设置state状态下的文字 */
24 | - (instancetype)setTitle:(NSString *)title forState:(MJRefreshState)state;
25 |
26 | @end
27 |
28 | NS_ASSUME_NONNULL_END
29 |
--------------------------------------------------------------------------------
/ios/MJRefresh/Custom/Trailer/MJRefreshStateTrailer.m:
--------------------------------------------------------------------------------
1 | //
2 | // MJRefreshStateTrailer.m
3 | // MJRefresh
4 | //
5 | // Created by kinarobin on 2020/5/3.
6 | // Copyright © 2020 小码哥. All rights reserved.
7 | //
8 |
9 | #import "MJRefreshStateTrailer.h"
10 | #import "NSBundle+MJRefresh.h"
11 | #import "UIView+MJExtension.h"
12 |
13 | @interface MJRefreshStateTrailer() {
14 | /** 显示刷新状态的label */
15 | __unsafe_unretained UILabel *_stateLabel;
16 | }
17 | /** 所有状态对应的文字 */
18 | @property (strong, nonatomic) NSMutableDictionary *stateTitles;
19 | @end
20 |
21 | @implementation MJRefreshStateTrailer
22 | #pragma mark - 懒加载
23 | - (NSMutableDictionary *)stateTitles {
24 | if (!_stateTitles) {
25 | self.stateTitles = [NSMutableDictionary dictionary];
26 | }
27 | return _stateTitles;
28 | }
29 |
30 | - (UILabel *)stateLabel {
31 | if (!_stateLabel) {
32 | UILabel *stateLabel = [UILabel mj_label];
33 | stateLabel.numberOfLines = 0;
34 | [self addSubview:_stateLabel = stateLabel];
35 | }
36 | return _stateLabel;
37 | }
38 |
39 | #pragma mark - 公共方法
40 | - (instancetype)setTitle:(NSString *)title forState:(MJRefreshState)state {
41 | if (title == nil) return self;
42 | self.stateTitles[@(state)] = title;
43 | self.stateLabel.text = self.stateTitles[@(self.state)];
44 | return self;
45 | }
46 |
47 | - (void)textConfiguration {
48 | // 初始化文字
49 | [self setTitle:[NSBundle mj_localizedStringForKey:MJRefreshTrailerIdleText] forState:MJRefreshStateIdle];
50 | [self setTitle:[NSBundle mj_localizedStringForKey:MJRefreshTrailerPullingText] forState:MJRefreshStatePulling];
51 | [self setTitle:[NSBundle mj_localizedStringForKey:MJRefreshTrailerPullingText] forState:MJRefreshStateRefreshing];
52 | }
53 |
54 | #pragma mark - 覆盖父类的方法
55 | - (void)prepare {
56 | [super prepare];
57 |
58 | [self textConfiguration];
59 | }
60 |
61 | - (void)i18nDidChange {
62 | [self textConfiguration];
63 |
64 | [super i18nDidChange];
65 | }
66 |
67 | - (void)setState:(MJRefreshState)state {
68 | MJRefreshCheckState
69 | // 设置状态文字
70 | self.stateLabel.text = self.stateTitles[@(state)];
71 | }
72 |
73 | - (void)placeSubviews {
74 | [super placeSubviews];
75 |
76 | if (self.stateLabel.hidden) return;
77 |
78 | BOOL noConstrainsOnStatusLabel = self.stateLabel.constraints.count == 0;
79 | CGFloat stateLabelW = ceil(self.stateLabel.font.pointSize);
80 | // 状态
81 | if (noConstrainsOnStatusLabel) {
82 | self.stateLabel.center = CGPointMake(self.mj_w * 0.5, self.mj_h * 0.5);
83 | self.stateLabel.mj_size = CGSizeMake(stateLabelW, self.mj_h) ;
84 | }
85 | }
86 |
87 | @end
88 |
--------------------------------------------------------------------------------
/ios/MJRefresh/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | $(CURRENT_PROJECT_VERSION)
21 | NSPrincipalClass
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/ios/MJRefresh/MJRefresh.bundle/arrow@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/472647301/react-native-refresh-control/c3a967dc16ca0bb177443b1aa81836feb71465ac/ios/MJRefresh/MJRefresh.bundle/arrow@2x.png
--------------------------------------------------------------------------------
/ios/MJRefresh/MJRefresh.bundle/en.lproj/Localizable.strings:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/472647301/react-native-refresh-control/c3a967dc16ca0bb177443b1aa81836feb71465ac/ios/MJRefresh/MJRefresh.bundle/en.lproj/Localizable.strings
--------------------------------------------------------------------------------
/ios/MJRefresh/MJRefresh.bundle/ko.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.bundle/ru.lproj/Localizable.strings:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/472647301/react-native-refresh-control/c3a967dc16ca0bb177443b1aa81836feb71465ac/ios/MJRefresh/MJRefresh.bundle/ru.lproj/Localizable.strings
--------------------------------------------------------------------------------
/ios/MJRefresh/MJRefresh.bundle/trail_arrow@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/472647301/react-native-refresh-control/c3a967dc16ca0bb177443b1aa81836feb71465ac/ios/MJRefresh/MJRefresh.bundle/trail_arrow@2x.png
--------------------------------------------------------------------------------
/ios/MJRefresh/MJRefresh.bundle/uk.lproj/Localizable.strings:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/472647301/react-native-refresh-control/c3a967dc16ca0bb177443b1aa81836feb71465ac/ios/MJRefresh/MJRefresh.bundle/uk.lproj/Localizable.strings
--------------------------------------------------------------------------------
/ios/MJRefresh/MJRefresh.bundle/zh-Hans.lproj/Localizable.strings:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/472647301/react-native-refresh-control/c3a967dc16ca0bb177443b1aa81836feb71465ac/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 | "MJRefreshTrailerIdleText" = "滑動查看圖文詳情";
6 | "MJRefreshTrailerPullingText" = "釋放查看圖文詳情";
7 |
8 | "MJRefreshAutoFooterIdleText" = "點擊或上拉加載更多";
9 | "MJRefreshAutoFooterRefreshingText" = "正在加載更多的數據...";
10 | "MJRefreshAutoFooterNoMoreDataText" = "已經全部加載完畢";
11 |
12 | "MJRefreshBackFooterIdleText" = "上拉可以加載更多";
13 | "MJRefreshBackFooterPullingText" = "鬆開立即加載更多";
14 | "MJRefreshBackFooterRefreshingText" = "正在加載更多的數據...";
15 | "MJRefreshBackFooterNoMoreDataText" = "已經全部加載完畢";
16 |
17 | "MJRefreshHeaderLastTimeText" = "最後更新:";
18 | "MJRefreshHeaderDateTodayText" = "今天";
19 | "MJRefreshHeaderNoneLastDateText" = "無記錄";
20 |
--------------------------------------------------------------------------------
/ios/MJRefresh/MJRefresh.h:
--------------------------------------------------------------------------------
1 | // 代码地址: https://github.com/CoderMJLee/MJRefresh
2 |
3 | #import
4 |
5 | #if __has_include()
6 | FOUNDATION_EXPORT double MJRefreshVersionNumber;
7 | FOUNDATION_EXPORT const unsigned char MJRefreshVersionString[];
8 |
9 | #import
10 | #import
11 | #import
12 |
13 | #import
14 | #import
15 |
16 | #import
17 | #import
18 | #import
19 | #import
20 |
21 | #import
22 | #import
23 | #import
24 | #import
25 | #else
26 | #import "UIScrollView+MJRefresh.h"
27 | #import "UIScrollView+MJExtension.h"
28 | #import "UIView+MJExtension.h"
29 |
30 | #import "MJRefreshNormalHeader.h"
31 | #import "MJRefreshGifHeader.h"
32 |
33 | #import "MJRefreshBackNormalFooter.h"
34 | #import "MJRefreshBackGifFooter.h"
35 | #import "MJRefreshAutoNormalFooter.h"
36 | #import "MJRefreshAutoGifFooter.h"
37 |
38 | #import "MJRefreshNormalTrailer.h"
39 | #import "MJRefreshConfig.h"
40 | #import "NSBundle+MJRefresh.h"
41 | #import "MJRefreshConst.h"
42 | #endif
43 |
--------------------------------------------------------------------------------
/ios/MJRefresh/MJRefreshConfig.h:
--------------------------------------------------------------------------------
1 | //
2 | // MJRefreshConfig.h
3 | //
4 | // Created by Frank on 2018/11/27.
5 | // Copyright © 2018 小码哥. All rights reserved.
6 | //
7 |
8 | #import
9 |
10 | NS_ASSUME_NONNULL_BEGIN
11 |
12 | @interface MJRefreshConfig : NSObject
13 |
14 | /** 默认使用的语言版本, 默认为 nil. 将随系统的语言自动改变 */
15 | @property (copy, nonatomic, nullable) NSString *languageCode;
16 |
17 | /** 默认使用的语言资源文件名, 默认为 nil, 即默认的 Localizable.strings.
18 |
19 | - Attention: 文件名不包含后缀.strings
20 | */
21 | @property (copy, nonatomic, nullable) NSString *i18nFilename;
22 | /** i18n 多语言资源加载自定义 Bundle.
23 |
24 | - Attention: 默认为 nil 采用内置逻辑. 这里设置后将忽略内置逻辑的多语言模式, 采用自定义的多语言 bundle
25 | */
26 | @property (nonatomic, nullable) NSBundle *i18nBundle;
27 |
28 | /** Singleton Config instance */
29 | @property (class, nonatomic, readonly) MJRefreshConfig *defaultConfig;
30 |
31 | - (instancetype)init NS_UNAVAILABLE;
32 | + (instancetype)new NS_UNAVAILABLE;
33 |
34 | @end
35 |
36 | NS_ASSUME_NONNULL_END
37 |
--------------------------------------------------------------------------------
/ios/MJRefresh/MJRefreshConfig.m:
--------------------------------------------------------------------------------
1 | //
2 | // MJRefreshConfig.m
3 | //
4 | // Created by Frank on 2018/11/27.
5 | // Copyright © 2018 小码哥. All rights reserved.
6 | //
7 |
8 | #import "MJRefreshConfig.h"
9 | #import "MJRefreshConst.h"
10 | #import "NSBundle+MJRefresh.h"
11 |
12 | @interface MJRefreshConfig (Bundle)
13 |
14 | + (void)resetLanguageResourceCache;
15 |
16 | @end
17 |
18 | @implementation MJRefreshConfig
19 |
20 | static MJRefreshConfig *mj_RefreshConfig = nil;
21 |
22 | + (instancetype)defaultConfig {
23 | static dispatch_once_t onceToken;
24 | dispatch_once(&onceToken, ^{
25 | mj_RefreshConfig = [[self alloc] init];
26 | });
27 | return mj_RefreshConfig;
28 | }
29 |
30 | - (void)setLanguageCode:(NSString *)languageCode {
31 | if ([languageCode isEqualToString:_languageCode]) {
32 | return;
33 | }
34 |
35 | _languageCode = languageCode;
36 | // 重置语言资源
37 | [MJRefreshConfig resetLanguageResourceCache];
38 | [NSNotificationCenter.defaultCenter
39 | postNotificationName:MJRefreshDidChangeLanguageNotification object:self];
40 | }
41 |
42 | @end
43 |
--------------------------------------------------------------------------------
/ios/MJRefresh/MJRefreshConst.h:
--------------------------------------------------------------------------------
1 | // 代码地址: https://github.com/CoderMJLee/MJRefresh
2 | #import
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(DESCRIPTION) __attribute__((deprecated(DESCRIPTION)))
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 MJRefreshTrailWidth;
37 | UIKIT_EXTERN const CGFloat MJRefreshFastAnimationDuration;
38 | UIKIT_EXTERN const CGFloat MJRefreshSlowAnimationDuration;
39 |
40 |
41 | UIKIT_EXTERN NSString *const MJRefreshKeyPathContentOffset;
42 | UIKIT_EXTERN NSString *const MJRefreshKeyPathContentSize;
43 | UIKIT_EXTERN NSString *const MJRefreshKeyPathContentInset;
44 | UIKIT_EXTERN NSString *const MJRefreshKeyPathPanState;
45 |
46 | UIKIT_EXTERN NSString *const MJRefreshHeaderLastUpdatedTimeKey;
47 |
48 | UIKIT_EXTERN NSString *const MJRefreshHeaderIdleText;
49 | UIKIT_EXTERN NSString *const MJRefreshHeaderPullingText;
50 | UIKIT_EXTERN NSString *const MJRefreshHeaderRefreshingText;
51 |
52 | UIKIT_EXTERN NSString *const MJRefreshTrailerIdleText;
53 | UIKIT_EXTERN NSString *const MJRefreshTrailerPullingText;
54 |
55 | UIKIT_EXTERN NSString *const MJRefreshAutoFooterIdleText;
56 | UIKIT_EXTERN NSString *const MJRefreshAutoFooterRefreshingText;
57 | UIKIT_EXTERN NSString *const MJRefreshAutoFooterNoMoreDataText;
58 |
59 | UIKIT_EXTERN NSString *const MJRefreshBackFooterIdleText;
60 | UIKIT_EXTERN NSString *const MJRefreshBackFooterPullingText;
61 | UIKIT_EXTERN NSString *const MJRefreshBackFooterRefreshingText;
62 | UIKIT_EXTERN NSString *const MJRefreshBackFooterNoMoreDataText;
63 |
64 | UIKIT_EXTERN NSString *const MJRefreshHeaderLastTimeText;
65 | UIKIT_EXTERN NSString *const MJRefreshHeaderDateTodayText;
66 | UIKIT_EXTERN NSString *const MJRefreshHeaderNoneLastDateText;
67 |
68 | UIKIT_EXTERN NSString *const MJRefreshDidChangeLanguageNotification;
69 |
70 | // 状态检查
71 | #define MJRefreshCheckState \
72 | MJRefreshState oldState = self.state; \
73 | if (state == oldState) return; \
74 | [super setState:state];
75 |
76 | // 异步主线程执行,不强持有Self
77 | #define MJRefreshDispatchAsyncOnMainQueue(x) \
78 | __weak typeof(self) weakSelf = self; \
79 | dispatch_async(dispatch_get_main_queue(), ^{ \
80 | typeof(weakSelf) self = weakSelf; \
81 | {x} \
82 | });
83 |
84 | /// 替换方法实现
85 | /// @param _fromClass 源类
86 | /// @param _originSelector 源类的 Selector
87 | /// @param _toClass 目标类
88 | /// @param _newSelector 目标类的 Selector
89 | CG_INLINE BOOL MJRefreshExchangeImplementations(
90 | Class _fromClass, SEL _originSelector,
91 | Class _toClass, SEL _newSelector) {
92 | if (!_fromClass || !_toClass) {
93 | return NO;
94 | }
95 |
96 | Method oriMethod = class_getInstanceMethod(_fromClass, _originSelector);
97 | Method newMethod = class_getInstanceMethod(_toClass, _newSelector);
98 | if (!newMethod) {
99 | return NO;
100 | }
101 |
102 | BOOL isAddedMethod = class_addMethod(_fromClass, _originSelector,
103 | method_getImplementation(newMethod),
104 | method_getTypeEncoding(newMethod));
105 | if (isAddedMethod) {
106 | // 如果 class_addMethod 成功了,说明之前 fromClass 里并不存在 originSelector,所以要用一个空的方法代替它,以避免 class_replaceMethod 后,后续 toClass 的这个方法被调用时可能会 crash
107 | IMP emptyIMP = imp_implementationWithBlock(^(id selfObject) {});
108 | IMP oriMethodIMP = method_getImplementation(oriMethod) ?: emptyIMP;
109 | const char *oriMethodTypeEncoding = method_getTypeEncoding(oriMethod) ?: "v@:";
110 | class_replaceMethod(_toClass, _newSelector, oriMethodIMP, oriMethodTypeEncoding);
111 | } else {
112 | method_exchangeImplementations(oriMethod, newMethod);
113 | }
114 | return YES;
115 | }
116 |
--------------------------------------------------------------------------------
/ios/MJRefresh/MJRefreshConst.m:
--------------------------------------------------------------------------------
1 | // 代码地址: https://github.com/CoderMJLee/MJRefresh
2 | #import
3 |
4 | const CGFloat MJRefreshLabelLeftInset = 25;
5 | const CGFloat MJRefreshHeaderHeight = 54.0;
6 | const CGFloat MJRefreshFooterHeight = 44.0;
7 | const CGFloat MJRefreshTrailWidth = 60.0;
8 | const CGFloat MJRefreshFastAnimationDuration = 0.25;
9 | const CGFloat MJRefreshSlowAnimationDuration = 0.4;
10 |
11 |
12 | NSString *const MJRefreshKeyPathContentOffset = @"contentOffset";
13 | NSString *const MJRefreshKeyPathContentInset = @"contentInset";
14 | NSString *const MJRefreshKeyPathContentSize = @"contentSize";
15 | NSString *const MJRefreshKeyPathPanState = @"state";
16 |
17 | NSString *const MJRefreshHeaderLastUpdatedTimeKey = @"MJRefreshHeaderLastUpdatedTimeKey";
18 |
19 | NSString *const MJRefreshHeaderIdleText = @"MJRefreshHeaderIdleText";
20 | NSString *const MJRefreshHeaderPullingText = @"MJRefreshHeaderPullingText";
21 | NSString *const MJRefreshHeaderRefreshingText = @"MJRefreshHeaderRefreshingText";
22 |
23 | NSString *const MJRefreshTrailerIdleText = @"MJRefreshTrailerIdleText";
24 | NSString *const MJRefreshTrailerPullingText = @"MJRefreshTrailerPullingText";
25 |
26 | NSString *const MJRefreshAutoFooterIdleText = @"MJRefreshAutoFooterIdleText";
27 | NSString *const MJRefreshAutoFooterRefreshingText = @"MJRefreshAutoFooterRefreshingText";
28 | NSString *const MJRefreshAutoFooterNoMoreDataText = @"MJRefreshAutoFooterNoMoreDataText";
29 |
30 | NSString *const MJRefreshBackFooterIdleText = @"MJRefreshBackFooterIdleText";
31 | NSString *const MJRefreshBackFooterPullingText = @"MJRefreshBackFooterPullingText";
32 | NSString *const MJRefreshBackFooterRefreshingText = @"MJRefreshBackFooterRefreshingText";
33 | NSString *const MJRefreshBackFooterNoMoreDataText = @"MJRefreshBackFooterNoMoreDataText";
34 |
35 | NSString *const MJRefreshHeaderLastTimeText = @"MJRefreshHeaderLastTimeText";
36 | NSString *const MJRefreshHeaderDateTodayText = @"MJRefreshHeaderDateTodayText";
37 | NSString *const MJRefreshHeaderNoneLastDateText = @"MJRefreshHeaderNoneLastDateText";
38 |
39 | NSString *const MJRefreshDidChangeLanguageNotification = @"MJRefreshDidChangeLanguageNotification";
40 |
--------------------------------------------------------------------------------
/ios/MJRefresh/NSBundle+MJRefresh.h:
--------------------------------------------------------------------------------
1 | //
2 | // NSBundle+MJRefresh.h
3 | // MJRefresh
4 | //
5 | // Created by MJ Lee on 16/6/13.
6 | // Copyright © 2016年 小码哥. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | NS_ASSUME_NONNULL_BEGIN
12 |
13 | @interface NSBundle (MJRefresh)
14 | + (instancetype)mj_refreshBundle;
15 | + (UIImage *)mj_arrowImage;
16 | + (UIImage *)mj_trailArrowImage;
17 | + (NSString *)mj_localizedStringForKey:(NSString *)key value:(nullable NSString *)value;
18 | + (NSString *)mj_localizedStringForKey:(NSString *)key;
19 | @end
20 |
21 | NS_ASSUME_NONNULL_END
22 |
--------------------------------------------------------------------------------
/ios/MJRefresh/NSBundle+MJRefresh.m:
--------------------------------------------------------------------------------
1 | //
2 | // NSBundle+MJRefresh.m
3 | // MJRefresh
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 | #import "MJRefreshConfig.h"
12 |
13 | static NSBundle *mj_defaultI18nBundle = nil;
14 | static NSBundle *mj_systemI18nBundle = nil;
15 |
16 | @implementation NSBundle (MJRefresh)
17 | + (instancetype)mj_refreshBundle
18 | {
19 | static NSBundle *refreshBundle = nil;
20 | if (refreshBundle == nil) {
21 | #ifdef SWIFT_PACKAGE
22 | NSBundle *containnerBundle = SWIFTPM_MODULE_BUNDLE;
23 | #else
24 | NSBundle *containnerBundle = [NSBundle bundleForClass:[MJRefreshComponent class]];
25 | #endif
26 | refreshBundle = [NSBundle bundleWithPath:[containnerBundle pathForResource:@"MJRefresh" ofType:@"bundle"]];
27 | }
28 | return refreshBundle;
29 | }
30 |
31 | + (UIImage *)mj_arrowImage
32 | {
33 | static UIImage *arrowImage = nil;
34 | if (arrowImage == nil) {
35 | arrowImage = [[UIImage imageWithContentsOfFile:[[self mj_refreshBundle] pathForResource:@"arrow@2x" ofType:@"png"]] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
36 | }
37 | return arrowImage;
38 | }
39 |
40 | + (UIImage *)mj_trailArrowImage {
41 | static UIImage *arrowImage = nil;
42 | if (arrowImage == nil) {
43 | arrowImage = [[UIImage imageWithContentsOfFile:[[self mj_refreshBundle] pathForResource:@"trail_arrow@2x" ofType:@"png"]] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
44 | }
45 | return arrowImage;
46 | }
47 |
48 | + (NSString *)mj_localizedStringForKey:(NSString *)key
49 | {
50 | return [self mj_localizedStringForKey:key value:nil];
51 | }
52 |
53 | + (NSString *)mj_localizedStringForKey:(NSString *)key value:(NSString *)value
54 | {
55 | NSString *table = MJRefreshConfig.defaultConfig.i18nFilename;
56 |
57 | // 如果没有缓存, 则走初始化逻辑
58 | if (mj_defaultI18nBundle == nil) {
59 | NSString *language = MJRefreshConfig.defaultConfig.languageCode;
60 | // 如果配置中没有配置语言
61 | if (!language) {
62 | language = [NSLocale preferredLanguages].firstObject;
63 | }
64 | NSBundle *bundle = MJRefreshConfig.defaultConfig.i18nBundle;
65 | // 首先优先使用公共配置中的 i18nBundle, 如果为空则使用 mainBundle
66 | bundle = bundle ? bundle : NSBundle.mainBundle;
67 | // 按语言选取语言包
68 | NSString *i18nFolderPath = [bundle pathForResource:language ofType:@"lproj"];
69 | mj_defaultI18nBundle = [NSBundle bundleWithPath:i18nFolderPath];
70 | // 检查语言包, 如果没有查找到, 则默认使用 mainBundle
71 | mj_defaultI18nBundle = mj_defaultI18nBundle ? mj_defaultI18nBundle : NSBundle.mainBundle;
72 |
73 | // 获取 MJRefresh 自有的语言包
74 | if (mj_systemI18nBundle == nil) {
75 | mj_systemI18nBundle = [self mj_defaultI18nBundleWithLanguage:language];
76 | }
77 | }
78 | // 首先在 MJRefresh 内置语言文件中寻找
79 | value = [mj_systemI18nBundle localizedStringForKey:key value:value table:nil];
80 | // 然后在 MainBundle 对应语言文件中寻找
81 | value = [mj_defaultI18nBundle localizedStringForKey:key value:value table:table];
82 | return value;
83 | }
84 |
85 | + (NSBundle *)mj_defaultI18nBundleWithLanguage:(NSString *)language {
86 | if ([language hasPrefix:@"en"]) {
87 | language = @"en";
88 | } else if ([language hasPrefix:@"zh"]) {
89 | if ([language rangeOfString:@"Hans"].location != NSNotFound) {
90 | language = @"zh-Hans"; // 简体中文
91 | } else { // zh-Hant\zh-HK\zh-TW
92 | language = @"zh-Hant"; // 繁體中文
93 | }
94 | } else if ([language hasPrefix:@"ko"]) {
95 | language = @"ko";
96 | } else if ([language hasPrefix:@"ru"]) {
97 | language = @"ru";
98 | } else if ([language hasPrefix:@"uk"]) {
99 | language = @"uk";
100 | } else {
101 | language = @"en";
102 | }
103 |
104 | // 从MJRefresh.bundle中查找资源
105 | return [NSBundle bundleWithPath:[[NSBundle mj_refreshBundle] pathForResource:language ofType:@"lproj"]];
106 | }
107 | @end
108 |
109 | @implementation MJRefreshConfig (Bundle)
110 |
111 | + (void)resetLanguageResourceCache {
112 | mj_defaultI18nBundle = nil;
113 | mj_systemI18nBundle = nil;
114 | }
115 |
116 | @end
117 |
--------------------------------------------------------------------------------
/ios/MJRefresh/UICollectionViewLayout+MJRefresh.h:
--------------------------------------------------------------------------------
1 | //
2 | // UICollectionViewLayout+MJRefresh.h
3 | //
4 | // 该类是用来解决 Footer 在底端加载完成后, 仍停留在原处的 bug.
5 | // 此问题出现在 iOS 14 及以下系统上.
6 | // Reference: https://github.com/CoderMJLee/MJRefresh/issues/1552
7 | //
8 | // Created by jiasong on 2021/11/15.
9 | // Copyright © 2021 小码哥. All rights reserved.
10 | //
11 |
12 | #import
13 |
14 | NS_ASSUME_NONNULL_BEGIN
15 |
16 | @interface UICollectionViewLayout (MJRefresh)
17 |
18 | @end
19 |
20 | NS_ASSUME_NONNULL_END
21 |
--------------------------------------------------------------------------------
/ios/MJRefresh/UICollectionViewLayout+MJRefresh.m:
--------------------------------------------------------------------------------
1 | //
2 | // UICollectionViewLayout+MJRefresh.m
3 | //
4 | // 该类是用来解决 Footer 在底端加载完成后, 仍停留在原处的 bug.
5 | // 此问题出现在 iOS 14 及以下系统上.
6 | // Reference: https://github.com/CoderMJLee/MJRefresh/issues/1552
7 | //
8 | // Created by jiasong on 2021/11/15.
9 | // Copyright © 2021 小码哥. All rights reserved.
10 | //
11 |
12 | #import "UICollectionViewLayout+MJRefresh.h"
13 | #import "MJRefreshConst.h"
14 | #import "MJRefreshFooter.h"
15 | #import "UIScrollView+MJRefresh.h"
16 |
17 | @implementation UICollectionViewLayout (MJRefresh)
18 |
19 | + (void)load {
20 | static dispatch_once_t onceToken;
21 | dispatch_once(&onceToken, ^{
22 | MJRefreshExchangeImplementations(self.class, @selector(finalizeCollectionViewUpdates),
23 | self.class, @selector(mj_finalizeCollectionViewUpdates));
24 | });
25 | }
26 |
27 | - (void)mj_finalizeCollectionViewUpdates {
28 | [self mj_finalizeCollectionViewUpdates];
29 |
30 | __kindof MJRefreshFooter *footer = self.collectionView.mj_footer;
31 | CGSize newSize = self.collectionViewContentSize;
32 | CGSize oldSize = self.collectionView.contentSize;
33 | if (footer != nil && !CGSizeEqualToSize(newSize, oldSize)) {
34 | NSDictionary *changed = @{
35 | NSKeyValueChangeNewKey: [NSValue valueWithCGSize:newSize],
36 | NSKeyValueChangeOldKey: [NSValue valueWithCGSize:oldSize],
37 | };
38 | [CATransaction begin];
39 | [CATransaction setDisableActions:YES];
40 | [footer scrollViewContentSizeDidChange:changed];
41 | [CATransaction commit];
42 | }
43 | }
44 |
45 | @end
46 |
--------------------------------------------------------------------------------
/ios/MJRefresh/UIScrollView+MJExtension.h:
--------------------------------------------------------------------------------
1 | // 代码地址: https://github.com/CoderMJLee/MJRefresh
2 | // UIScrollView+Extension.h
3 | // MJRefresh
4 | //
5 | // Created by MJ Lee on 14-5-28.
6 | // Copyright (c) 2014年 小码哥. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | NS_ASSUME_NONNULL_BEGIN
12 |
13 | @interface UIScrollView (MJExtension)
14 | @property (readonly, nonatomic) UIEdgeInsets mj_inset;
15 |
16 | @property (assign, nonatomic) CGFloat mj_insetT;
17 | @property (assign, nonatomic) CGFloat mj_insetB;
18 | @property (assign, nonatomic) CGFloat mj_insetL;
19 | @property (assign, nonatomic) CGFloat mj_insetR;
20 |
21 | @property (assign, nonatomic) CGFloat mj_offsetX;
22 | @property (assign, nonatomic) CGFloat mj_offsetY;
23 |
24 | @property (assign, nonatomic) CGFloat mj_contentW;
25 | @property (assign, nonatomic) CGFloat mj_contentH;
26 | @end
27 |
28 | NS_ASSUME_NONNULL_END
29 |
--------------------------------------------------------------------------------
/ios/MJRefresh/UIScrollView+MJExtension.m:
--------------------------------------------------------------------------------
1 | // 代码地址: https://github.com/CoderMJLee/MJRefresh
2 | // UIScrollView+Extension.m
3 | // MJRefresh
4 | //
5 | // Created by MJ Lee on 14-5-28.
6 | // Copyright (c) 2014年 小码哥. All rights reserved.
7 | //
8 |
9 | #import "UIScrollView+MJExtension.h"
10 | #import
11 |
12 | #pragma clang diagnostic push
13 | #pragma clang diagnostic ignored "-Wunguarded-availability-new"
14 |
15 | @implementation UIScrollView (MJExtension)
16 |
17 | static BOOL respondsToAdjustedContentInset_;
18 |
19 | + (void)load
20 | {
21 | static dispatch_once_t onceToken;
22 | dispatch_once(&onceToken, ^{
23 | respondsToAdjustedContentInset_ = [self instancesRespondToSelector:@selector(adjustedContentInset)];
24 | });
25 | }
26 |
27 | - (UIEdgeInsets)mj_inset
28 | {
29 | #ifdef __IPHONE_11_0
30 | if (respondsToAdjustedContentInset_) {
31 | return self.adjustedContentInset;
32 | }
33 | #endif
34 | return self.contentInset;
35 | }
36 |
37 | - (void)setMj_insetT:(CGFloat)mj_insetT
38 | {
39 | UIEdgeInsets inset = self.contentInset;
40 | inset.top = mj_insetT;
41 | #ifdef __IPHONE_11_0
42 | if (respondsToAdjustedContentInset_) {
43 | inset.top -= (self.adjustedContentInset.top - self.contentInset.top);
44 | }
45 | #endif
46 | self.contentInset = inset;
47 | }
48 |
49 | - (CGFloat)mj_insetT
50 | {
51 | return self.mj_inset.top;
52 | }
53 |
54 | - (void)setMj_insetB:(CGFloat)mj_insetB
55 | {
56 | UIEdgeInsets inset = self.contentInset;
57 | inset.bottom = mj_insetB;
58 | #ifdef __IPHONE_11_0
59 | if (respondsToAdjustedContentInset_) {
60 | inset.bottom -= (self.adjustedContentInset.bottom - self.contentInset.bottom);
61 | }
62 | #endif
63 | self.contentInset = inset;
64 | }
65 |
66 | - (CGFloat)mj_insetB
67 | {
68 | return self.mj_inset.bottom;
69 | }
70 |
71 | - (void)setMj_insetL:(CGFloat)mj_insetL
72 | {
73 | UIEdgeInsets inset = self.contentInset;
74 | inset.left = mj_insetL;
75 | #ifdef __IPHONE_11_0
76 | if (respondsToAdjustedContentInset_) {
77 | inset.left -= (self.adjustedContentInset.left - self.contentInset.left);
78 | }
79 | #endif
80 | self.contentInset = inset;
81 | }
82 |
83 | - (CGFloat)mj_insetL
84 | {
85 | return self.mj_inset.left;
86 | }
87 |
88 | - (void)setMj_insetR:(CGFloat)mj_insetR
89 | {
90 | UIEdgeInsets inset = self.contentInset;
91 | inset.right = mj_insetR;
92 | #ifdef __IPHONE_11_0
93 | if (respondsToAdjustedContentInset_) {
94 | inset.right -= (self.adjustedContentInset.right - self.contentInset.right);
95 | }
96 | #endif
97 | self.contentInset = inset;
98 | }
99 |
100 | - (CGFloat)mj_insetR
101 | {
102 | return self.mj_inset.right;
103 | }
104 |
105 | - (void)setMj_offsetX:(CGFloat)mj_offsetX
106 | {
107 | CGPoint offset = self.contentOffset;
108 | offset.x = mj_offsetX;
109 | self.contentOffset = offset;
110 | }
111 |
112 | - (CGFloat)mj_offsetX
113 | {
114 | return self.contentOffset.x;
115 | }
116 |
117 | - (void)setMj_offsetY:(CGFloat)mj_offsetY
118 | {
119 | CGPoint offset = self.contentOffset;
120 | offset.y = mj_offsetY;
121 | self.contentOffset = offset;
122 | }
123 |
124 | - (CGFloat)mj_offsetY
125 | {
126 | return self.contentOffset.y;
127 | }
128 |
129 | - (void)setMj_contentW:(CGFloat)mj_contentW
130 | {
131 | CGSize size = self.contentSize;
132 | size.width = mj_contentW;
133 | self.contentSize = size;
134 | }
135 |
136 | - (CGFloat)mj_contentW
137 | {
138 | return self.contentSize.width;
139 | }
140 |
141 | - (void)setMj_contentH:(CGFloat)mj_contentH
142 | {
143 | CGSize size = self.contentSize;
144 | size.height = mj_contentH;
145 | self.contentSize = size;
146 | }
147 |
148 | - (CGFloat)mj_contentH
149 | {
150 | return self.contentSize.height;
151 | }
152 | @end
153 | #pragma clang diagnostic pop
154 |
--------------------------------------------------------------------------------
/ios/MJRefresh/UIScrollView+MJRefresh.h:
--------------------------------------------------------------------------------
1 | // 代码地址: https://github.com/CoderMJLee/MJRefresh
2 | // UIScrollView+MJRefresh.h
3 | // MJRefresh
4 | //
5 | // Created by MJ Lee on 15/3/4.
6 | // Copyright (c) 2015年 小码哥. All rights reserved.
7 | // 给ScrollView增加下拉刷新、上拉刷新、 左滑刷新的功能
8 |
9 | #import
10 | #if __has_include()
11 | #import
12 | #else
13 | #import "MJRefreshConst.h"
14 | #endif
15 |
16 | @class MJRefreshHeader, MJRefreshFooter, MJRefreshTrailer;
17 |
18 | NS_ASSUME_NONNULL_BEGIN
19 |
20 | @interface UIScrollView (MJRefresh)
21 | /** 下拉刷新控件 */
22 | @property (strong, nonatomic, nullable) MJRefreshHeader *mj_header;
23 | @property (strong, nonatomic, nullable) MJRefreshHeader *header MJRefreshDeprecated("使用mj_header");
24 | /** 上拉刷新控件 */
25 | @property (strong, nonatomic, nullable) MJRefreshFooter *mj_footer;
26 | @property (strong, nonatomic, nullable) MJRefreshFooter *footer MJRefreshDeprecated("使用mj_footer");
27 |
28 | /** 左滑刷新控件 */
29 | @property (strong, nonatomic, nullable) MJRefreshTrailer *mj_trailer;
30 |
31 | #pragma mark - other
32 | - (NSInteger)mj_totalDataCount;
33 |
34 | @end
35 |
36 | NS_ASSUME_NONNULL_END
37 |
--------------------------------------------------------------------------------
/ios/MJRefresh/UIScrollView+MJRefresh.m:
--------------------------------------------------------------------------------
1 | // 代码地址: https://github.com/CoderMJLee/MJRefresh
2 | // UIScrollView+MJRefresh.m
3 | // MJRefresh
4 | //
5 | // Created by MJ Lee on 15/3/4.
6 | // Copyright (c) 2015年 小码哥. All rights reserved.
7 | //
8 |
9 | #import "UIScrollView+MJRefresh.h"
10 | #import "MJRefreshHeader.h"
11 | #import "MJRefreshFooter.h"
12 | #import "MJRefreshTrailer.h"
13 | #import
14 |
15 | @implementation UIScrollView (MJRefresh)
16 |
17 | #pragma mark - header
18 | static const char MJRefreshHeaderKey = '\0';
19 | - (void)setMj_header:(MJRefreshHeader *)mj_header
20 | {
21 | if (mj_header != self.mj_header) {
22 | // 删除旧的,添加新的
23 | [self.mj_header removeFromSuperview];
24 |
25 | if (mj_header) {
26 | [self insertSubview:mj_header atIndex:0];
27 | }
28 | // 存储新的
29 | objc_setAssociatedObject(self, &MJRefreshHeaderKey,
30 | mj_header, OBJC_ASSOCIATION_RETAIN);
31 | }
32 | }
33 |
34 | - (MJRefreshHeader *)mj_header
35 | {
36 | return objc_getAssociatedObject(self, &MJRefreshHeaderKey);
37 | }
38 |
39 | #pragma mark - footer
40 | static const char MJRefreshFooterKey = '\0';
41 | - (void)setMj_footer:(MJRefreshFooter *)mj_footer
42 | {
43 | if (mj_footer != self.mj_footer) {
44 | // 删除旧的,添加新的
45 | [self.mj_footer removeFromSuperview];
46 | if (mj_footer) {
47 | [self insertSubview:mj_footer atIndex:0];
48 | }
49 | // 存储新的
50 | objc_setAssociatedObject(self, &MJRefreshFooterKey,
51 | mj_footer, OBJC_ASSOCIATION_RETAIN);
52 | }
53 | }
54 |
55 | - (MJRefreshFooter *)mj_footer
56 | {
57 | return objc_getAssociatedObject(self, &MJRefreshFooterKey);
58 | }
59 |
60 | #pragma mark - footer
61 | static const char MJRefreshTrailerKey = '\0';
62 | - (void)setMj_trailer:(MJRefreshTrailer *)mj_trailer {
63 | if (mj_trailer != self.mj_trailer) {
64 | // 删除旧的,添加新的
65 | [self.mj_trailer removeFromSuperview];
66 | if (mj_trailer) {
67 | [self insertSubview:mj_trailer atIndex:0];
68 | }
69 | // 存储新的
70 | objc_setAssociatedObject(self, &MJRefreshTrailerKey,
71 | mj_trailer, OBJC_ASSOCIATION_RETAIN);
72 | }
73 | }
74 |
75 | - (MJRefreshTrailer *)mj_trailer {
76 | return objc_getAssociatedObject(self, &MJRefreshTrailerKey);
77 | }
78 |
79 | #pragma mark - 过期
80 | - (void)setFooter:(MJRefreshFooter *)footer
81 | {
82 | self.mj_footer = footer;
83 | }
84 |
85 | - (MJRefreshFooter *)footer
86 | {
87 | return self.mj_footer;
88 | }
89 |
90 | - (void)setHeader:(MJRefreshHeader *)header
91 | {
92 | self.mj_header = header;
93 | }
94 |
95 | - (MJRefreshHeader *)header
96 | {
97 | return self.mj_header;
98 | }
99 |
100 | #pragma mark - other
101 | - (NSInteger)mj_totalDataCount
102 | {
103 | NSInteger totalCount = 0;
104 | if ([self isKindOfClass:[UITableView class]]) {
105 | UITableView *tableView = (UITableView *)self;
106 |
107 | for (NSInteger section = 0; section < tableView.numberOfSections; section++) {
108 | totalCount += [tableView numberOfRowsInSection:section];
109 | }
110 | } else if ([self isKindOfClass:[UICollectionView class]]) {
111 | UICollectionView *collectionView = (UICollectionView *)self;
112 |
113 | for (NSInteger section = 0; section < collectionView.numberOfSections; section++) {
114 | totalCount += [collectionView numberOfItemsInSection:section];
115 | }
116 | }
117 | return totalCount;
118 | }
119 |
120 | @end
121 |
--------------------------------------------------------------------------------
/ios/MJRefresh/UIView+MJExtension.h:
--------------------------------------------------------------------------------
1 | // 代码地址: https://github.com/CoderMJLee/MJRefresh
2 | // UIView+Extension.h
3 | // MJRefresh
4 | //
5 | // Created by MJ Lee on 14-5-28.
6 | // Copyright (c) 2014年 小码哥. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | NS_ASSUME_NONNULL_BEGIN
12 |
13 | @interface UIView (MJExtension)
14 | @property (assign, nonatomic) CGFloat mj_x;
15 | @property (assign, nonatomic) CGFloat mj_y;
16 | @property (assign, nonatomic) CGFloat mj_w;
17 | @property (assign, nonatomic) CGFloat mj_h;
18 | @property (assign, nonatomic) CGSize mj_size;
19 | @property (assign, nonatomic) CGPoint mj_origin;
20 | @end
21 |
22 | NS_ASSUME_NONNULL_END
23 |
--------------------------------------------------------------------------------
/ios/MJRefresh/UIView+MJExtension.m:
--------------------------------------------------------------------------------
1 | // 代码地址: https://github.com/CoderMJLee/MJRefresh
2 | // UIView+Extension.m
3 | // MJRefresh
4 | //
5 | // Created by MJ Lee on 14-5-28.
6 | // Copyright (c) 2014年 小码哥. All rights reserved.
7 | //
8 |
9 | #import "UIView+MJExtension.h"
10 |
11 | @implementation UIView (MJExtension)
12 | - (void)setMj_x:(CGFloat)mj_x
13 | {
14 | CGRect frame = self.frame;
15 | frame.origin.x = mj_x;
16 | self.frame = frame;
17 | }
18 |
19 | - (CGFloat)mj_x
20 | {
21 | return self.frame.origin.x;
22 | }
23 |
24 | - (void)setMj_y:(CGFloat)mj_y
25 | {
26 | CGRect frame = self.frame;
27 | frame.origin.y = mj_y;
28 | self.frame = frame;
29 | }
30 |
31 | - (CGFloat)mj_y
32 | {
33 | return self.frame.origin.y;
34 | }
35 |
36 | - (void)setMj_w:(CGFloat)mj_w
37 | {
38 | CGRect frame = self.frame;
39 | frame.size.width = mj_w;
40 | self.frame = frame;
41 | }
42 |
43 | - (CGFloat)mj_w
44 | {
45 | return self.frame.size.width;
46 | }
47 |
48 | - (void)setMj_h:(CGFloat)mj_h
49 | {
50 | CGRect frame = self.frame;
51 | frame.size.height = mj_h;
52 | self.frame = frame;
53 | }
54 |
55 | - (CGFloat)mj_h
56 | {
57 | return self.frame.size.height;
58 | }
59 |
60 | - (void)setMj_size:(CGSize)mj_size
61 | {
62 | CGRect frame = self.frame;
63 | frame.size = mj_size;
64 | self.frame = frame;
65 | }
66 |
67 | - (CGSize)mj_size
68 | {
69 | return self.frame.size;
70 | }
71 |
72 | - (void)setMj_origin:(CGPoint)mj_origin
73 | {
74 | CGRect frame = self.frame;
75 | frame.origin = mj_origin;
76 | self.frame = frame;
77 | }
78 |
79 | - (CGPoint)mj_origin
80 | {
81 | return self.frame.origin;
82 | }
83 | @end
84 |
--------------------------------------------------------------------------------
/ios/MJRefresh/include/MJRefresh.h:
--------------------------------------------------------------------------------
1 | ../MJRefresh.h
--------------------------------------------------------------------------------
/ios/MJRefresh/include/MJRefreshAutoFooter.h:
--------------------------------------------------------------------------------
1 | ../Base/MJRefreshAutoFooter.h
--------------------------------------------------------------------------------
/ios/MJRefresh/include/MJRefreshAutoGifFooter.h:
--------------------------------------------------------------------------------
1 | ../Custom/Footer/Auto/MJRefreshAutoGifFooter.h
--------------------------------------------------------------------------------
/ios/MJRefresh/include/MJRefreshAutoNormalFooter.h:
--------------------------------------------------------------------------------
1 | ../Custom/Footer/Auto/MJRefreshAutoNormalFooter.h
--------------------------------------------------------------------------------
/ios/MJRefresh/include/MJRefreshAutoStateFooter.h:
--------------------------------------------------------------------------------
1 | ../Custom/Footer/Auto/MJRefreshAutoStateFooter.h
--------------------------------------------------------------------------------
/ios/MJRefresh/include/MJRefreshBackFooter.h:
--------------------------------------------------------------------------------
1 | ../Base/MJRefreshBackFooter.h
--------------------------------------------------------------------------------
/ios/MJRefresh/include/MJRefreshBackGifFooter.h:
--------------------------------------------------------------------------------
1 | ../Custom/Footer/Back/MJRefreshBackGifFooter.h
--------------------------------------------------------------------------------
/ios/MJRefresh/include/MJRefreshBackNormalFooter.h:
--------------------------------------------------------------------------------
1 | ../Custom/Footer/Back/MJRefreshBackNormalFooter.h
--------------------------------------------------------------------------------
/ios/MJRefresh/include/MJRefreshBackStateFooter.h:
--------------------------------------------------------------------------------
1 | ../Custom/Footer/Back/MJRefreshBackStateFooter.h
--------------------------------------------------------------------------------
/ios/MJRefresh/include/MJRefreshComponent.h:
--------------------------------------------------------------------------------
1 | ../Base/MJRefreshComponent.h
--------------------------------------------------------------------------------
/ios/MJRefresh/include/MJRefreshConfig.h:
--------------------------------------------------------------------------------
1 | ../MJRefreshConfig.h
--------------------------------------------------------------------------------
/ios/MJRefresh/include/MJRefreshConst.h:
--------------------------------------------------------------------------------
1 | ../MJRefreshConst.h
--------------------------------------------------------------------------------
/ios/MJRefresh/include/MJRefreshFooter.h:
--------------------------------------------------------------------------------
1 | ../Base/MJRefreshFooter.h
--------------------------------------------------------------------------------
/ios/MJRefresh/include/MJRefreshGifHeader.h:
--------------------------------------------------------------------------------
1 | ../Custom/Header/MJRefreshGifHeader.h
--------------------------------------------------------------------------------
/ios/MJRefresh/include/MJRefreshHeader.h:
--------------------------------------------------------------------------------
1 | ../Base/MJRefreshHeader.h
--------------------------------------------------------------------------------
/ios/MJRefresh/include/MJRefreshNormalHeader.h:
--------------------------------------------------------------------------------
1 | ../Custom/Header/MJRefreshNormalHeader.h
--------------------------------------------------------------------------------
/ios/MJRefresh/include/MJRefreshNormalTrailer.h:
--------------------------------------------------------------------------------
1 | ../Custom/Trailer/MJRefreshNormalTrailer.h
--------------------------------------------------------------------------------
/ios/MJRefresh/include/MJRefreshStateHeader.h:
--------------------------------------------------------------------------------
1 | ../Custom/Header/MJRefreshStateHeader.h
--------------------------------------------------------------------------------
/ios/MJRefresh/include/MJRefreshStateTrailer.h:
--------------------------------------------------------------------------------
1 | ../Custom/Trailer/MJRefreshStateTrailer.h
--------------------------------------------------------------------------------
/ios/MJRefresh/include/MJRefreshTrailer.h:
--------------------------------------------------------------------------------
1 | ../Base/MJRefreshTrailer.h
--------------------------------------------------------------------------------
/ios/MJRefresh/include/NSBundle+MJRefresh.h:
--------------------------------------------------------------------------------
1 | ../NSBundle+MJRefresh.h
--------------------------------------------------------------------------------
/ios/MJRefresh/include/UIScrollView+MJExtension.h:
--------------------------------------------------------------------------------
1 | ../UIScrollView+MJExtension.h
--------------------------------------------------------------------------------
/ios/MJRefresh/include/UIScrollView+MJRefresh.h:
--------------------------------------------------------------------------------
1 | ../UIScrollView+MJRefresh.h
--------------------------------------------------------------------------------
/ios/MJRefresh/include/UIView+MJExtension.h:
--------------------------------------------------------------------------------
1 | ../UIView+MJExtension.h
--------------------------------------------------------------------------------
/ios/RNByronCustomHeader.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | @interface RNByronCustomHeader : RCTViewManager
4 | @end
--------------------------------------------------------------------------------
/ios/RNByronCustomHeader.m:
--------------------------------------------------------------------------------
1 | #import "RNByronCustomHeader.h"
2 |
3 | @implementation RNByronCustomHeader
4 |
5 | RCT_EXPORT_MODULE(RNByronCustomHeader)
6 |
7 | - (UIView *)view
8 | {
9 | return [[UIView alloc] init];
10 | }
11 |
12 | @end
--------------------------------------------------------------------------------
/ios/RNByronRefreshControl.h:
--------------------------------------------------------------------------------
1 | // RNByronRefreshControl.h
2 |
3 | #import
4 |
5 | @interface RNByronRefreshControl : RCTViewManager
6 |
7 | @end
8 |
--------------------------------------------------------------------------------
/ios/RNByronRefreshControl.m:
--------------------------------------------------------------------------------
1 | // RNByronRefreshControl.m
2 |
3 | #import "RNByronRefreshControl.h"
4 | #import "RCTRefreshableProtocol.h"
5 | #import "RNByronRefreshHeader.h"
6 |
7 | @implementation RNByronRefreshControl
8 |
9 | RCT_EXPORT_MODULE()
10 |
11 | - (UIView *)view {
12 | return [[RNByronRefreshHeader alloc] init];
13 | }
14 |
15 | RCT_EXPORT_VIEW_PROPERTY(refreshing, BOOL)
16 | RCT_EXPORT_VIEW_PROPERTY(height, NSInteger)
17 | RCT_EXPORT_VIEW_PROPERTY(onChangeState, RCTDirectEventBlock)
18 | RCT_EXPORT_VIEW_PROPERTY(onChangeOffset, RCTDirectEventBlock)
19 |
20 | @end
21 |
--------------------------------------------------------------------------------
/ios/RNByronRefreshControl.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/ios/RNByronRefreshControl.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ios/RNByronRefreshHeader.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | #import
4 | #import
5 | #import "MJRefresh.h"
6 |
7 | NS_ASSUME_NONNULL_BEGIN
8 |
9 | @interface RNByronRefreshHeader : MJRefreshHeader
10 |
11 | @property (nonatomic, copy) RCTDirectEventBlock onChangeState;
12 | @property (nonatomic, copy) RCTDirectEventBlock onChangeOffset;
13 |
14 | @end
15 |
16 | NS_ASSUME_NONNULL_END
17 |
--------------------------------------------------------------------------------
/ios/RNByronRefreshHeader.m:
--------------------------------------------------------------------------------
1 | #import "RNByronRefreshHeader.h"
2 | #import
3 | #import "RCTUtils.h"
4 |
5 | @interface RNByronRefreshHeader () {
6 | float offset;
7 | }
8 | @end
9 |
10 | @implementation RNByronRefreshHeader
11 |
12 | - (instancetype)init {
13 | self = [super init];
14 | return self;
15 | }
16 |
17 | RCT_NOT_IMPLEMENTED(-(instancetype)initWithCoder : (NSCoder *)aDecoder)
18 |
19 | - (void)setRefreshing:(BOOL)refreshing {
20 | if (refreshing) {
21 | [self beginRefreshing];
22 | } else {
23 | [self endRefreshing];
24 | }
25 | }
26 |
27 | - (void)setHeight:(NSInteger *)height {
28 | self.mj_h = (long) height;
29 | }
30 |
31 | #pragma mark - 重写方法
32 | #pragma mark 在这里做一些初始化配置(比如添加子控件)
33 | - (void)prepare {
34 | [super prepare];
35 | }
36 |
37 | #pragma mark 在这里设置子控件的位置和尺寸
38 | - (void)placeSubviews {
39 | [super placeSubviews];
40 | }
41 |
42 | #pragma mark 监听scrollView的contentOffset改变
43 | - (void)scrollViewContentOffsetDidChange:(NSDictionary *)change {
44 | [super scrollViewContentOffsetDidChange:change];
45 | if (change && [change.allKeys containsObject:@"new"]) {
46 | CGPoint point = [[change objectForKey:@"new"] CGPointValue];
47 | if(_onChangeOffset) {
48 | if (point.y > 0) {
49 | // 与android保持一致 避免为0之后多次通知
50 | if (offset != 0) {
51 | offset = 0;
52 | _onChangeOffset(@{@"offset": @(offset)});
53 | }
54 | } else {
55 | // 取绝对值, 与android保持一致
56 | offset = fabs(point.y);
57 | _onChangeOffset(@{@"offset": @(offset)});
58 | }
59 | }
60 | }
61 | }
62 |
63 | #pragma mark 监听scrollView的contentSize改变
64 | - (void)scrollViewContentSizeDidChange:(NSDictionary *)change {
65 | [super scrollViewContentSizeDidChange:change];
66 | }
67 |
68 | #pragma mark 监听scrollView的拖拽状态改变
69 | - (void)scrollViewPanStateDidChange:(NSDictionary *)change {
70 | [super scrollViewPanStateDidChange:change];
71 |
72 | }
73 |
74 | #pragma mark 监听控件的刷新状态
75 | - (void)setState:(MJRefreshState)state {
76 | MJRefreshCheckState;
77 | if (_onChangeState) {
78 | _onChangeState(@{@"state": @(state)});
79 | }
80 | }
81 |
82 | #pragma mark 监听拖拽比例(控件被拖出来的比例)
83 | - (void)setPullingPercent:(CGFloat)pullingPercent {
84 | [super setPullingPercent:pullingPercent];
85 | // 这里获取的offset不准确,而且下滑在上滑会导致值不更新
86 | }
87 |
88 | @synthesize refreshing;
89 |
90 | @synthesize onRefresh;
91 |
92 | @end
93 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@byron-react-native/refresh-control",
3 | "title": "React Native Refresh Control",
4 | "version": "1.2.5",
5 | "description": "React Native Refresh Control",
6 | "main": "index.js",
7 | "private": false,
8 | "files": [
9 | "README.md",
10 | "android",
11 | "index.js",
12 | "index.d.ts",
13 | "assets",
14 | "ios",
15 | "react-native-refresh-control.podspec",
16 | "!android/**/build/*"
17 | ],
18 | "scripts": {
19 | "test": "echo \"Error: no test specified\" && exit 1",
20 | "push": "npm publish --access public"
21 | },
22 | "repository": {
23 | "type": "git",
24 | "url": "git+https://github.com/472647301/react-native-refresh-control.git",
25 | "baseUrl": "https://github.com/472647301/react-native-refresh-control"
26 | },
27 | "keywords": [
28 | "react-native"
29 | ],
30 | "author": {
31 | "name": "byron",
32 | "email": "byron.zhuwenbo@gmail.com"
33 | },
34 | "license": "MIT",
35 | "licenseFilename": "LICENSE",
36 | "readmeFilename": "README.md",
37 | "peerDependencies": {
38 | "react": ">=16.8.1",
39 | "react-native": ">=0.60.0-rc.0 <1.0.x"
40 | },
41 | "devDependencies": {
42 | "react": "^16.9.0",
43 | "react-native": "^0.61.5"
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/react-native-refresh-control.podspec:
--------------------------------------------------------------------------------
1 | # react-native-refresh-control.podspec
2 |
3 | require "json"
4 |
5 | package = JSON.parse(File.read(File.join(__dir__, "package.json")))
6 |
7 | Pod::Spec.new do |s|
8 | s.name = "react-native-refresh-control"
9 | s.version = package["version"]
10 | s.summary = package["description"]
11 | s.description = <<-DESC
12 | react-native-refresh-control
13 | DESC
14 | s.homepage = "https://github.com/472647301/react-native-refresh-control"
15 | # brief license entry:
16 | s.license = "MIT"
17 | # optional - use expanded license entry instead:
18 | # s.license = { :type => "MIT", :file => "LICENSE" }
19 | s.authors = { "byron" => "byron.zhuwenbo@gmail.com" }
20 | s.platforms = { :ios => "9.0" }
21 | s.source = { :git => "https://github.com/472647301/react-native-refresh-control.git", :tag => "#{s.version}" }
22 |
23 | s.source_files = "ios/**/*.{h,c,cc,cpp,m,mm,swift}"
24 | s.requires_arc = true
25 |
26 | s.dependency "React"
27 | # ...
28 | # s.dependency "..."
29 | end
30 |
31 |
--------------------------------------------------------------------------------