niushimeng 9 月之前
當前提交
2f0b8ade92

+ 12 - 0
.gitignore

@@ -0,0 +1,12 @@
+*.[aod]
+*.DS_Store
+.DS_Store
+*Thumbs.db
+*.iml
+.gradle
+.idea
+node_modules
+npm-debug.log
+/android/build
+/ios/**/*xcuserdata*
+/ios/**/*xcshareddata*

+ 13 - 0
.npmignore

@@ -0,0 +1,13 @@
+*.DS_Store
+.DS_Store
+*Thumbs.db
+.gradle
+.idea
+*.iml
+npm-debug.log
+node_modules
+screencasts
+/android/build
+/ios/**/*xcuserdata*
+/ios/**/*xcshareddata*
+

+ 22 - 0
LICENSE

@@ -0,0 +1,22 @@
+(The MIT License)
+
+Copyright (c) 2015-2016 YunJiang.Fang <42550564@qq.com>
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

+ 117 - 0
README.md

@@ -0,0 +1,117 @@
+# React Native Toast (remobile)
+A android like toast for react-native support for ios and android
+
+## Installation
+```sh
+npm install @remobile/react-native-toast --save
+```
+
+### Installation (iOS)
+* Drag RCTToast.xcodeproj to your project on Xcode.
+* Click on your main project file (the one that represents the .xcodeproj) select Build Phases and drag libRCTToast.a from the Products folder inside the RCTToast.xcodeproj.
+* Look for Header Search Paths and make sure it contains both $(SRCROOT)/../../../react-native/React as recursive.
+
+### Installation (Android)
+```gradle
+...
+include ':react-native-toast'
+project(':react-native-toast').projectDir = new File(settingsDir, '../node_modules/@remobile/react-native-toast/android')
+```
+
+* In `android/app/build.gradle`
+
+```gradle
+...
+dependencies {
+    ...
+    compile project(':react-native-toast')
+}
+```
+
+* register module (in MainApplication.java)
+
+```java
+......
+import com.remobile.toast.RCTToastPackage;  // <--- import
+
+......
+
+@Override
+protected List<ReactPackage> getPackages() {
+   ......
+   new RCTToastPackage(),            // <------ add here
+   ......
+}
+
+```
+```
+
+### Screencasts
+![ios](https://github.com/remobile/react-native-toast/blob/master/screencasts/ios.gif)
+
+## Usage
+
+### Example
+```js
+var React = require('react');
+var ReactNative = require('react-native');
+var {
+    StyleSheet,
+    View,
+    Image
+} = ReactNative;
+
+var Toast = require('react-native-toast');
+var Button = require('@remobile/react-native-simple-button');
+
+module.exports = React.createClass({
+    render() {
+        return (
+            <View style={styles.container}>
+                <Button onPress={Toast.show.bind(null, "this is a message")}>
+                    show
+                </Button>
+                <Button onPress={Toast.showShortTop.bind(null, "this is a message")}>
+                    showShortTop
+                </Button>
+                <Button onPress={Toast.showShortCenter.bind(null, "this is a message")}>
+                    showShortCenter
+                </Button>
+                <Button onPress={Toast.showShortBottom.bind(null, "this is a message")}>
+                    showShortBottom
+                </Button>
+                <Button onPress={Toast.showLongTop.bind(null, "this is a message")}>
+                    showLongTop
+                </Button>
+                <Button onPress={Toast.showLongCenter.bind(null, "this is a message")}>
+                    showLongCenter
+                </Button>
+                <Button onPress={Toast.showLongBottom.bind(null, "this is a message")}>
+                    showLongBottom
+                </Button>
+            </View>
+        );
+    },
+});
+
+
+var styles = StyleSheet.create({
+    container: {
+        flex: 1,
+        justifyContent: 'space-around',
+        alignItems: 'center',
+        backgroundColor: 'transparent',
+        paddingVertical:150,
+    }
+});
+```
+
+### HELP
+* look https://github.com/EddyVerbruggen/Toast-PhoneGap-Plugin
+
+
+### thanks
+* this project come from https://github.com/EddyVerbruggen/Toast-PhoneGap-Plugin
+
+### see detail use
+* https://github.com/remobile/react-native-template

+ 25 - 0
android/build.gradle

@@ -0,0 +1,25 @@
+apply plugin: 'com.android.library'
+
+android {
+    compileSdkVersion 23
+    buildToolsVersion "23.0.1"
+
+    defaultConfig {
+        minSdkVersion 16
+        targetSdkVersion 22
+        versionCode 1
+        versionName "1.0"
+    }
+    buildTypes {
+        release {
+            minifyEnabled false
+            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+        }
+    }
+}
+
+dependencies {
+    compile fileTree(dir: 'libs', include: ['*.jar'])
+    compile 'com.android.support:appcompat-v7:23.0.1'
+    compile 'com.facebook.react:react-native:+'
+}

+ 3 - 0
android/src/main/AndroidManifest.xml

@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.remobile.toast">
+</manifest>

+ 29 - 0
android/src/main/java/com/remobile/toast/RCTToastPackage.java

@@ -0,0 +1,29 @@
+package com.remobile.toast;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import com.facebook.react.ReactPackage;
+import com.facebook.react.bridge.JavaScriptModule;
+import com.facebook.react.bridge.NativeModule;
+import com.facebook.react.bridge.ReactApplicationContext;
+import com.facebook.react.uimanager.ViewManager;
+
+public class RCTToastPackage implements ReactPackage {
+    @Override
+    public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
+        return Arrays.<NativeModule>asList(
+                new Toast(reactContext)
+        );
+    }
+
+    public List<Class<? extends JavaScriptModule>> createJSModules() {
+        return Collections.emptyList();
+    }
+
+    @Override
+    public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
+        return Arrays.<ViewManager>asList();
+    }
+}

+ 89 - 0
android/src/main/java/com/remobile/toast/Toast.java

@@ -0,0 +1,89 @@
+package com.remobile.toast;
+
+import android.view.Gravity;
+
+import com.facebook.common.logging.FLog;
+import com.facebook.react.bridge.*;
+
+public class Toast extends ReactContextBaseJavaModule implements LifecycleEventListener {
+
+    private android.widget.Toast mostRecentToast;
+
+    // note that webView.isPaused() is not Xwalk compatible, so tracking it poor-man style
+    private boolean isPaused;
+
+    public Toast(ReactApplicationContext reactContext) {
+        super(reactContext);
+    }
+
+    @Override
+    public String getName() {
+        return "RCTToast";
+    }
+
+    @ReactMethod
+    public void show(ReadableMap options) throws Exception {
+        if (this.isPaused) {
+            return;
+        }
+
+
+        final String message = options.getString("message");
+        final String duration = options.getString("duration");
+        final String position = options.getString("position");
+        final int addPixelsY = options.hasKey("addPixelsY") ? options.getInt("addPixelsY") : 0;
+
+        UiThreadUtil.runOnUiThread(new Runnable() {
+            public void run() {
+                android.widget.Toast toast = android.widget.Toast.makeText(
+                        getReactApplicationContext(),
+                        message,
+                        "short".equals(duration) ? android.widget.Toast.LENGTH_SHORT : android.widget.Toast.LENGTH_LONG);
+
+                if ("top".equals(position)) {
+                    toast.setGravity(Gravity.TOP | Gravity.CENTER_HORIZONTAL, 0, 20 + addPixelsY);
+                } else if ("bottom".equals(position)) {
+                    toast.setGravity(Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL, 0, 20 - addPixelsY);
+                } else if ("center".equals(position)) {
+                    toast.setGravity(Gravity.CENTER_VERTICAL | Gravity.CENTER_HORIZONTAL, 0, addPixelsY);
+                } else {
+                    FLog.e("RCTToast", "invalid position. valid options are 'top', 'center' and 'bottom'");
+                    return;
+                }
+
+                toast.show();
+                mostRecentToast = toast;
+            }
+        });
+    }
+
+    @ReactMethod
+    public void hide() throws Exception {
+        if (mostRecentToast != null) {
+            mostRecentToast.cancel();
+        }
+    }
+
+    @Override
+    public void initialize() {
+        getReactApplicationContext().addLifecycleEventListener(this);
+    }
+
+    @Override
+    public void onHostPause() {
+        if (mostRecentToast != null) {
+            mostRecentToast.cancel();
+        }
+        this.isPaused = true;
+    }
+
+    @Override
+    public void onHostResume() {
+        this.isPaused = false;
+    }
+
+    @Override
+    public void onHostDestroy() {
+        this.isPaused = true;
+    }
+}

+ 96 - 0
index.js

@@ -0,0 +1,96 @@
+'use strict';
+
+const ReactNative = require('react-native');
+const {
+    NativeModules,
+} = ReactNative;
+
+const RCTToast = NativeModules.Toast;
+const Toast = {};
+
+const optionsBuilder = function () {
+  // defaults
+    let message = null;
+    let duration = 'short';
+    let position = 'center';
+    let addPixelsY = 0;
+
+    return {
+        withMessage: function (m) {
+            message = m;
+            return this;
+        },
+
+        withDuration: function (d) {
+            duration = d;
+            return this;
+        },
+
+        withPosition: function (p) {
+            position = p;
+            return this;
+        },
+
+        withAddPixelsY: function (y) {
+            addPixelsY = y;
+            return this;
+        },
+
+        build: function () {
+            return {
+                message: message,
+                duration: duration,
+                position: position,
+                addPixelsY: addPixelsY,
+            };
+        },
+    };
+};
+
+const showWithOptions = function (options) {
+    RCTToast.show(options);
+};
+
+const showToast = function (message, duration, position) {
+    showWithOptions(
+      optionsBuilder()
+          .withMessage(message || '未知数据')
+          .withDuration(duration)
+          .withPosition(position)
+          .build()
+      );
+};
+
+Toast.showShortTop = function (message) {
+    showToast(message, 'short', 'top');
+};
+
+Toast.showShortCenter = function (message) {
+    showToast(message, 'short', 'center');
+};
+
+Toast.showShortBottom = function (message) {
+    showToast(message, 'short', 'bottom');
+};
+
+Toast.showLongTop = function (message) {
+    showToast(message, 'long', 'top');
+};
+
+Toast.showLongCenter = function (message) {
+    showToast(message, 'long', 'center');
+};
+
+Toast.showLongBottom = function (message) {
+    showToast(message, 'long', 'bottom');
+};
+
+Toast.show = function (message) {
+    showToast(message, 'short', 'bottom');
+};
+
+Toast.hide = function () {
+    RCTToast.hide();
+};
+
+module.exports = Toast;

+ 274 - 0
ios/RCTToast.xcodeproj/project.pbxproj

@@ -0,0 +1,274 @@
+// !$*UTF8*$!
+{
+	archiveVersion = 1;
+	classes = {
+	};
+	objectVersion = 46;
+	objects = {
+
+/* Begin PBXBuildFile section */
+		327633601BFAAE28004DA88E /* Toast.m in Sources */ = {isa = PBXBuildFile; fileRef = 3276335D1BFAAE28004DA88E /* Toast.m */; };
+		327633611BFAAE28004DA88E /* Toast+UIView.m in Sources */ = {isa = PBXBuildFile; fileRef = 3276335F1BFAAE28004DA88E /* Toast+UIView.m */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXCopyFilesBuildPhase section */
+		327633401BFAAD7E004DA88E /* CopyFiles */ = {
+			isa = PBXCopyFilesBuildPhase;
+			buildActionMask = 2147483647;
+			dstPath = "include/$(PRODUCT_NAME)";
+			dstSubfolderSpec = 16;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXCopyFilesBuildPhase section */
+
+/* Begin PBXFileReference section */
+		327633421BFAAD7E004DA88E /* libRCTToast.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRCTToast.a; sourceTree = BUILT_PRODUCTS_DIR; };
+		3276335D1BFAAE28004DA88E /* Toast.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Toast.m; sourceTree = "<group>"; };
+		3276335E1BFAAE28004DA88E /* Toast+UIView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "Toast+UIView.h"; sourceTree = "<group>"; };
+		3276335F1BFAAE28004DA88E /* Toast+UIView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "Toast+UIView.m"; sourceTree = "<group>"; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+		3276333F1BFAAD7E004DA88E /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+		327633391BFAAD7E004DA88E = {
+			isa = PBXGroup;
+			children = (
+				327633441BFAAD7E004DA88E /* RCTToast */,
+				327633431BFAAD7E004DA88E /* Products */,
+			);
+			sourceTree = "<group>";
+		};
+		327633431BFAAD7E004DA88E /* Products */ = {
+			isa = PBXGroup;
+			children = (
+				327633421BFAAD7E004DA88E /* libRCTToast.a */,
+			);
+			name = Products;
+			sourceTree = "<group>";
+		};
+		327633441BFAAD7E004DA88E /* RCTToast */ = {
+			isa = PBXGroup;
+			children = (
+				3276335D1BFAAE28004DA88E /* Toast.m */,
+				3276335E1BFAAE28004DA88E /* Toast+UIView.h */,
+				3276335F1BFAAE28004DA88E /* Toast+UIView.m */,
+			);
+			path = RCTToast;
+			sourceTree = "<group>";
+		};
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+		327633411BFAAD7E004DA88E /* RCTToast */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = 327633561BFAAD7E004DA88E /* Build configuration list for PBXNativeTarget "RCTToast" */;
+			buildPhases = (
+				3276333E1BFAAD7E004DA88E /* Sources */,
+				3276333F1BFAAD7E004DA88E /* Frameworks */,
+				327633401BFAAD7E004DA88E /* CopyFiles */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+			);
+			name = RCTToast;
+			productName = RCTToast;
+			productReference = 327633421BFAAD7E004DA88E /* libRCTToast.a */;
+			productType = "com.apple.product-type.library.static";
+		};
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+		3276333A1BFAAD7E004DA88E /* Project object */ = {
+			isa = PBXProject;
+			attributes = {
+				LastUpgradeCheck = 0640;
+				ORGANIZATIONNAME = remobile;
+				TargetAttributes = {
+					327633411BFAAD7E004DA88E = {
+						CreatedOnToolsVersion = 6.4;
+					};
+				};
+			};
+			buildConfigurationList = 3276333D1BFAAD7E004DA88E /* Build configuration list for PBXProject "RCTToast" */;
+			compatibilityVersion = "Xcode 3.2";
+			developmentRegion = English;
+			hasScannedForEncodings = 0;
+			knownRegions = (
+				en,
+			);
+			mainGroup = 327633391BFAAD7E004DA88E;
+			productRefGroup = 327633431BFAAD7E004DA88E /* Products */;
+			projectDirPath = "";
+			projectRoot = "";
+			targets = (
+				327633411BFAAD7E004DA88E /* RCTToast */,
+			);
+		};
+/* End PBXProject section */
+
+/* Begin PBXSourcesBuildPhase section */
+		3276333E1BFAAD7E004DA88E /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				327633611BFAAE28004DA88E /* Toast+UIView.m in Sources */,
+				327633601BFAAE28004DA88E /* Toast.m in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXSourcesBuildPhase section */
+
+/* Begin XCBuildConfiguration section */
+		327633541BFAAD7E004DA88E /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+				CLANG_CXX_LIBRARY = "libc++";
+				CLANG_ENABLE_MODULES = YES;
+				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_WARN_BOOL_CONVERSION = YES;
+				CLANG_WARN_CONSTANT_CONVERSION = YES;
+				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+				CLANG_WARN_EMPTY_BODY = YES;
+				CLANG_WARN_ENUM_CONVERSION = YES;
+				CLANG_WARN_INT_CONVERSION = YES;
+				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+				CLANG_WARN_UNREACHABLE_CODE = YES;
+				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+				COPY_PHASE_STRIP = NO;
+				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
+				GCC_C_LANGUAGE_STANDARD = gnu99;
+				GCC_DYNAMIC_NO_PIC = NO;
+				GCC_NO_COMMON_BLOCKS = YES;
+				GCC_OPTIMIZATION_LEVEL = 0;
+				GCC_PREPROCESSOR_DEFINITIONS = (
+					"DEBUG=1",
+					"$(inherited)",
+				);
+				GCC_SYMBOLS_PRIVATE_EXTERN = NO;
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+				GCC_WARN_UNDECLARED_SELECTOR = YES;
+				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+				GCC_WARN_UNUSED_FUNCTION = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				HEADER_SEARCH_PATHS = (
+					"$(inherited)",
+					/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
+					"$(SRCROOT)/../../../react-native/React/**",
+					"$(SRCROOT)/../../../../node_modules/react-native/React/**",
+				);
+				IPHONEOS_DEPLOYMENT_TARGET = 7.0;
+				MTL_ENABLE_DEBUG_INFO = YES;
+				ONLY_ACTIVE_ARCH = YES;
+				SDKROOT = iphoneos;
+			};
+			name = Debug;
+		};
+		327633551BFAAD7E004DA88E /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+				CLANG_CXX_LIBRARY = "libc++";
+				CLANG_ENABLE_MODULES = YES;
+				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_WARN_BOOL_CONVERSION = YES;
+				CLANG_WARN_CONSTANT_CONVERSION = YES;
+				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+				CLANG_WARN_EMPTY_BODY = YES;
+				CLANG_WARN_ENUM_CONVERSION = YES;
+				CLANG_WARN_INT_CONVERSION = YES;
+				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+				CLANG_WARN_UNREACHABLE_CODE = YES;
+				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+				COPY_PHASE_STRIP = NO;
+				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+				ENABLE_NS_ASSERTIONS = NO;
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
+				GCC_C_LANGUAGE_STANDARD = gnu99;
+				GCC_NO_COMMON_BLOCKS = YES;
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+				GCC_WARN_UNDECLARED_SELECTOR = YES;
+				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+				GCC_WARN_UNUSED_FUNCTION = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				HEADER_SEARCH_PATHS = (
+					"$(inherited)",
+					/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
+					"$(SRCROOT)/../../../react-native/React/**",
+					"$(SRCROOT)/../../../../node_modules/react-native/React/**",
+				);
+				IPHONEOS_DEPLOYMENT_TARGET = 7.0;
+				MTL_ENABLE_DEBUG_INFO = NO;
+				SDKROOT = iphoneos;
+				VALIDATE_PRODUCT = YES;
+			};
+			name = Release;
+		};
+		327633571BFAAD7E004DA88E /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				HEADER_SEARCH_PATHS = (
+					"$(inherited)",
+					/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
+				);
+				OTHER_LDFLAGS = "-ObjC";
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				SKIP_INSTALL = YES;
+			};
+			name = Debug;
+		};
+		327633581BFAAD7E004DA88E /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				HEADER_SEARCH_PATHS = (
+					"$(inherited)",
+					/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
+				);
+				OTHER_LDFLAGS = "-ObjC";
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				SKIP_INSTALL = YES;
+			};
+			name = Release;
+		};
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+		3276333D1BFAAD7E004DA88E /* Build configuration list for PBXProject "RCTToast" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				327633541BFAAD7E004DA88E /* Debug */,
+				327633551BFAAD7E004DA88E /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+		327633561BFAAD7E004DA88E /* Build configuration list for PBXNativeTarget "RCTToast" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				327633571BFAAD7E004DA88E /* Debug */,
+				327633581BFAAD7E004DA88E /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+/* End XCConfigurationList section */
+	};
+	rootObject = 3276333A1BFAAD7E004DA88E /* Project object */;
+}

+ 7 - 0
ios/RCTToast.xcodeproj/project.xcworkspace/contents.xcworkspacedata

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Workspace
+   version = "1.0">
+   <FileRef
+      location = "self:RCTToast.xcodeproj">
+   </FileRef>
+</Workspace>

+ 26 - 0
ios/RCTToast/Toast+UIView.h

@@ -0,0 +1,26 @@
+#import <UIKit/UIKit.h>
+#import <Foundation/Foundation.h>
+
+@interface UIView (Toast)
+
+// each makeToast method creates a view and displays it as toast
+- (void)makeToast:(NSString *)message;
+- (void)makeToast:(NSString *)message duration:(NSTimeInterval)interval position:(id)position;
+- (void)makeToast:(NSString *)message duration:(NSTimeInterval)duration position:(id)position addPixelsY:(int)addPixelsY;
+- (void)makeToast:(NSString *)message duration:(NSTimeInterval)interval position:(id)position image:(UIImage *)image;
+- (void)makeToast:(NSString *)message duration:(NSTimeInterval)interval position:(id)position title:(NSString *)title;
+- (void)makeToast:(NSString *)message duration:(NSTimeInterval)interval position:(id)position title:(NSString *)title image:(UIImage *)image;
+
+- (void)hideToast;
+
+// displays toast with an activity spinner
+- (void)makeToastActivity;
+- (void)makeToastActivity:(id)position;
+- (void)hideToastActivity;
+
+// the showToast methods display any view as toast
+- (void)showToast:(UIView *)toast;
+- (void)showToast:(UIView *)toast duration:(NSTimeInterval)interval position:(id)point ;
+- (void)showToast:(UIView *)toast duration:(NSTimeInterval)interval position:(id)point addedPixelsY:(int)addPixelsY;
+
+@end

+ 389 - 0
ios/RCTToast/Toast+UIView.m

@@ -0,0 +1,389 @@
+#import "Toast+UIView.h"
+#import <QuartzCore/QuartzCore.h>
+#import <objc/runtime.h>
+
+/*
+ *  CONFIGURE THESE VALUES TO ADJUST LOOK & FEEL,
+ *  DISPLAY DURATION, ETC.
+ */
+
+// general appearance
+static const CGFloat CSToastMaxWidth            = 0.8;      // 80% of parent view width
+static const CGFloat CSToastMaxHeight           = 0.8;      // 80% of parent view height
+static const CGFloat CSToastHorizontalPadding   = 10.0;
+static const CGFloat CSToastVerticalPadding     = 10.0;
+static const CGFloat CSToastTopBottomOffset     = 20.0;
+static const CGFloat CSToastCornerRadius        = 5.0;
+static const CGFloat CSToastOpacity             = 0.8;
+static const CGFloat CSToastFontSize            = 16.0;
+static const CGFloat CSToastMaxTitleLines       = 0;
+static const CGFloat CSToastMaxMessageLines     = 0;
+static const NSTimeInterval CSToastFadeDuration = 0.2;
+
+// shadow appearance
+static const CGFloat CSToastShadowOpacity       = 0.8;
+static const CGFloat CSToastShadowRadius        = 6.0;
+static const CGSize  CSToastShadowOffset        = { 4.0, 4.0 };
+static const BOOL    CSToastDisplayShadow       = YES;
+
+// display duration and position
+static const NSString * CSToastDefaultPosition  = @"bottom";
+static const NSTimeInterval CSToastDefaultDuration  = 1.5;
+
+// image view size
+static const CGFloat CSToastImageViewWidth      = 80.0;
+static const CGFloat CSToastImageViewHeight     = 80.0;
+
+// activity
+static const CGFloat CSToastActivityWidth       = 100.0;
+static const CGFloat CSToastActivityHeight      = 100.0;
+static const NSString * CSToastActivityDefaultPosition = @"center";
+
+// interaction
+static const BOOL CSToastHidesOnTap             = YES;     // excludes activity views
+
+// associative reference keys
+static const NSString * CSToastTimerKey         = @"CSToastTimerKey";
+static const NSString * CSToastActivityViewKey  = @"CSToastActivityViewKey";
+
+static UIView *prevToast = NULL;
+
+@interface UIView (ToastPrivate)
+
+- (void)hideToast:(UIView *)toast;
+- (void)toastTimerDidFinish:(NSTimer *)timer;
+- (void)handleToastTapped:(UITapGestureRecognizer *)recognizer;
+- (CGPoint)centerPointForPosition:(id)position withToast:(UIView *)toast withAddedPixelsY:(int) addPixelsY;
+- (UIView *)viewForMessage:(NSString *)message title:(NSString *)title image:(UIImage *)image;
+- (CGSize)sizeForString:(NSString *)string font:(UIFont *)font constrainedToSize:(CGSize)constrainedSize lineBreakMode:(NSLineBreakMode)lineBreakMode;
+
+@end
+
+
+@implementation UIView (Toast)
+
+#pragma mark - Toast Methods
+
+- (void)makeToast:(NSString *)message {
+    [self makeToast:message duration:CSToastDefaultDuration position:CSToastDefaultPosition];
+}
+
+- (void)makeToast:(NSString *)message duration:(NSTimeInterval)duration position:(id)position {
+    UIView *toast = [self viewForMessage:message title:nil image:nil];
+    [self showToast:toast duration:duration position:position];
+}
+
+- (void)makeToast:(NSString *)message duration:(NSTimeInterval)duration position:(id)position addPixelsY:(int)addPixelsY {
+  UIView *toast = [self viewForMessage:message title:nil image:nil];
+  [self showToast:toast duration:duration position:position addedPixelsY:addPixelsY];
+}
+
+- (void)makeToast:(NSString *)message duration:(NSTimeInterval)duration position:(id)position title:(NSString *)title {
+    UIView *toast = [self viewForMessage:message title:title image:nil];
+    [self showToast:toast duration:duration position:position];
+}
+
+- (void)makeToast:(NSString *)message duration:(NSTimeInterval)duration position:(id)position image:(UIImage *)image {
+    UIView *toast = [self viewForMessage:message title:nil image:image];
+    [self showToast:toast duration:duration position:position];
+}
+
+- (void)makeToast:(NSString *)message duration:(NSTimeInterval)duration  position:(id)position title:(NSString *)title image:(UIImage *)image {
+    UIView *toast = [self viewForMessage:message title:title image:image];
+    [self showToast:toast duration:duration position:position];
+}
+
+- (void)showToast:(UIView *)toast {
+    [self showToast:toast duration:CSToastDefaultDuration position:CSToastDefaultPosition];
+}
+
+- (void)showToast:(UIView *)toast duration:(NSTimeInterval)duration position:(id)point {
+  [self showToast:toast duration:CSToastDefaultDuration position:CSToastDefaultPosition addedPixelsY:0];
+}
+
+- (void)showToast:(UIView *)toast duration:(NSTimeInterval)duration position:(id)point addedPixelsY:(int) addPixelsY  {
+    [self hideToast];
+    prevToast = toast;
+    toast.center = [self centerPointForPosition:point withToast:toast withAddedPixelsY:addPixelsY];
+    toast.alpha = 0.0;
+
+    if (CSToastHidesOnTap) {
+        UITapGestureRecognizer *recognizer = [[UITapGestureRecognizer alloc] initWithTarget:toast action:@selector(handleToastTapped:)];
+        [toast addGestureRecognizer:recognizer];
+        toast.userInteractionEnabled = YES;
+        toast.exclusiveTouch = YES;
+    }
+
+    // make sure that if InAppBrowser is active, we're still showing Toasts on top of it
+    UIViewController *vc = [self getTopMostViewController];
+    UIView *v = [vc view];
+    [v addSubview:toast];
+
+    [UIView animateWithDuration:CSToastFadeDuration
+                          delay:0.0
+                        options:(UIViewAnimationOptionCurveEaseOut | UIViewAnimationOptionAllowUserInteraction)
+                     animations:^{
+                         toast.alpha = CSToastOpacity;
+                     } completion:^(BOOL finished) {
+                         NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:duration target:self selector:@selector(toastTimerDidFinish:) userInfo:toast repeats:NO];
+                         // associate the timer with the toast view
+                         objc_setAssociatedObject (toast, &CSToastTimerKey, timer, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
+                     }];
+
+}
+
+- (UIViewController*) getTopMostViewController {
+  UIViewController *presentingViewController = [[[UIApplication sharedApplication] delegate] window].rootViewController;
+  while (presentingViewController.presentedViewController != nil) {
+    presentingViewController = presentingViewController.presentedViewController;
+  }
+  return presentingViewController;
+}
+
+- (void)hideToast {
+  if (prevToast){
+    [self hideToast:prevToast];
+  }
+}
+
+- (void)hideToast:(UIView *)toast {
+    [UIView animateWithDuration:CSToastFadeDuration
+                          delay:0.0
+                        options:(UIViewAnimationOptionCurveEaseIn | UIViewAnimationOptionBeginFromCurrentState)
+                     animations:^{
+                         toast.alpha = 0.0;
+                     } completion:^(BOOL finished) {
+                         [toast removeFromSuperview];
+                     }];
+}
+
+#pragma mark - Events
+
+- (void)toastTimerDidFinish:(NSTimer *)timer {
+    [self hideToast:(UIView *)timer.userInfo];
+}
+
+- (void)handleToastTapped:(UITapGestureRecognizer *)recognizer {
+    NSTimer *timer = (NSTimer *)objc_getAssociatedObject(self, &CSToastTimerKey);
+    [timer invalidate];
+
+    [self hideToast:recognizer.view];
+}
+
+#pragma mark - Toast Activity Methods
+
+- (void)makeToastActivity {
+    [self makeToastActivity:CSToastActivityDefaultPosition];
+}
+
+- (void)makeToastActivity:(id)position {
+    // sanity
+    UIView *existingActivityView = (UIView *)objc_getAssociatedObject(self, &CSToastActivityViewKey);
+    if (existingActivityView != nil) return;
+
+    UIView *activityView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, CSToastActivityWidth, CSToastActivityHeight)];
+    activityView.center = [self centerPointForPosition:position withToast:activityView withAddedPixelsY:0];
+    activityView.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:CSToastOpacity];
+    activityView.alpha = 0.0;
+    activityView.autoresizingMask = (UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin);
+    activityView.layer.cornerRadius = CSToastCornerRadius;
+
+    if (CSToastDisplayShadow) {
+        activityView.layer.shadowColor = [UIColor blackColor].CGColor;
+        activityView.layer.shadowOpacity = CSToastShadowOpacity;
+        activityView.layer.shadowRadius = CSToastShadowRadius;
+        activityView.layer.shadowOffset = CSToastShadowOffset;
+    }
+
+    UIActivityIndicatorView *activityIndicatorView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
+    activityIndicatorView.center = CGPointMake(activityView.bounds.size.width / 2, activityView.bounds.size.height / 2);
+    [activityView addSubview:activityIndicatorView];
+    [activityIndicatorView startAnimating];
+
+    // associate the activity view with self
+    objc_setAssociatedObject (self, &CSToastActivityViewKey, activityView, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
+
+    [self addSubview:activityView];
+
+    [UIView animateWithDuration:CSToastFadeDuration
+                          delay:0.0
+                        options:UIViewAnimationOptionCurveEaseOut
+                     animations:^{
+                         activityView.alpha = 1.0;
+                     } completion:nil];
+}
+
+- (void)hideToastActivity {
+    UIView *existingActivityView = (UIView *)objc_getAssociatedObject(self, &CSToastActivityViewKey);
+    if (existingActivityView != nil) {
+        [UIView animateWithDuration:CSToastFadeDuration
+                              delay:0.0
+                            options:(UIViewAnimationOptionCurveEaseIn | UIViewAnimationOptionBeginFromCurrentState)
+                         animations:^{
+                             existingActivityView.alpha = 0.0;
+                         } completion:^(BOOL finished) {
+                             [existingActivityView removeFromSuperview];
+                             objc_setAssociatedObject (self, &CSToastActivityViewKey, nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
+                         }];
+    }
+}
+
+#pragma mark - Helpers
+
+- (CGPoint)centerPointForPosition:(id)point withToast:(UIView *)toast withAddedPixelsY:(int) addPixelsY {
+    if([point isKindOfClass:[NSString class]]) {
+        // convert string literals @"top", @"bottom", @"center", or any point wrapped in an NSValue object into a CGPoint
+        if([point caseInsensitiveCompare:@"top"] == NSOrderedSame) {
+            return CGPointMake(self.bounds.size.width/2, (toast.frame.size.height / 2) + addPixelsY + CSToastVerticalPadding + CSToastTopBottomOffset);
+        } else if([point caseInsensitiveCompare:@"bottom"] == NSOrderedSame) {
+            return CGPointMake(self.bounds.size.width/2, (self.bounds.size.height - (toast.frame.size.height / 2)) - CSToastVerticalPadding - CSToastTopBottomOffset + addPixelsY);
+        } else if([point caseInsensitiveCompare:@"center"] == NSOrderedSame) {
+            return CGPointMake(self.bounds.size.width / 2, (self.bounds.size.height / 2) + addPixelsY);
+        }
+    } else if ([point isKindOfClass:[NSValue class]]) {
+        return [point CGPointValue];
+    }
+
+    NSLog(@"Warning: Invalid position for toast.");
+    return [self centerPointForPosition:CSToastDefaultPosition withToast:toast withAddedPixelsY:addPixelsY];
+}
+
+- (CGSize)sizeForString:(NSString *)string font:(UIFont *)font constrainedToSize:(CGSize)constrainedSize lineBreakMode:(NSLineBreakMode)lineBreakMode {
+    if ([string respondsToSelector:@selector(boundingRectWithSize:options:attributes:context:)]) {
+        NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
+        paragraphStyle.lineBreakMode = lineBreakMode;
+        NSDictionary *attributes = @{NSFontAttributeName:font, NSParagraphStyleAttributeName:paragraphStyle};
+        CGRect boundingRect = [string boundingRectWithSize:constrainedSize options:NSStringDrawingUsesLineFragmentOrigin attributes:attributes context:nil];
+        return CGSizeMake(ceilf(boundingRect.size.width), ceilf(boundingRect.size.height));
+    }
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+    return [string sizeWithFont:font constrainedToSize:constrainedSize lineBreakMode:lineBreakMode];
+#pragma clang diagnostic pop
+}
+
+- (UIView *)viewForMessage:(NSString *)message title:(NSString *)title image:(UIImage *)image {
+    // sanity
+    if((message == nil) && (title == nil) && (image == nil)) return nil;
+
+    // dynamically build a toast view with any combination of message, title, & image.
+    UILabel *messageLabel = nil;
+    UILabel *titleLabel = nil;
+    UIImageView *imageView = nil;
+    
+    // create the parent view
+    UIView *wrapperView = [[UIView alloc] init];
+    wrapperView.autoresizingMask = (UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin);
+    wrapperView.layer.cornerRadius = CSToastCornerRadius;
+    
+    if (CSToastDisplayShadow) {
+        wrapperView.layer.shadowColor = [UIColor blackColor].CGColor;
+        wrapperView.layer.shadowOpacity = CSToastShadowOpacity;
+        wrapperView.layer.shadowRadius = CSToastShadowRadius;
+        wrapperView.layer.shadowOffset = CSToastShadowOffset;
+    }
+
+    wrapperView.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:CSToastOpacity];
+    
+    if(image != nil) {
+        imageView = [[UIImageView alloc] initWithImage:image];
+        imageView.contentMode = UIViewContentModeScaleAspectFit;
+        imageView.frame = CGRectMake(CSToastHorizontalPadding, CSToastVerticalPadding, CSToastImageViewWidth, CSToastImageViewHeight);
+    }
+    
+    CGFloat imageWidth, imageHeight, imageLeft;
+    
+    // the imageView frame values will be used to size & position the other views
+    if(imageView != nil) {
+        imageWidth = imageView.bounds.size.width;
+        imageHeight = imageView.bounds.size.height;
+        imageLeft = CSToastHorizontalPadding;
+    } else {
+        imageWidth = imageHeight = imageLeft = 0.0;
+    }
+    
+    if (title != nil) {
+        titleLabel = [[UILabel alloc] init];
+        titleLabel.numberOfLines = CSToastMaxTitleLines;
+        titleLabel.font = [UIFont boldSystemFontOfSize:CSToastFontSize];
+        titleLabel.textAlignment = NSTextAlignmentLeft;
+        titleLabel.lineBreakMode = NSLineBreakByWordWrapping;
+        titleLabel.textColor = [UIColor whiteColor];
+        titleLabel.backgroundColor = [UIColor clearColor];
+        titleLabel.alpha = 1.0;
+        titleLabel.text = title;
+        
+        // size the title label according to the length of the text
+        CGSize maxSizeTitle = CGSizeMake((self.bounds.size.width * CSToastMaxWidth) - imageWidth, self.bounds.size.height * CSToastMaxHeight);
+        CGSize expectedSizeTitle = [self sizeForString:title font:titleLabel.font constrainedToSize:maxSizeTitle lineBreakMode:titleLabel.lineBreakMode];
+        titleLabel.frame = CGRectMake(0.0, 0.0, expectedSizeTitle.width, expectedSizeTitle.height);
+    }
+    
+    if (message != nil) {
+        messageLabel = [[UILabel alloc] init];
+        messageLabel.numberOfLines = CSToastMaxMessageLines;
+        messageLabel.font = [UIFont systemFontOfSize:CSToastFontSize];
+        messageLabel.lineBreakMode = NSLineBreakByWordWrapping;
+        messageLabel.textColor = [UIColor whiteColor];
+        messageLabel.backgroundColor = [UIColor clearColor];
+        messageLabel.alpha = 1.0;
+        messageLabel.text = message;
+        
+        // size the message label according to the length of the text
+        CGSize maxSizeMessage = CGSizeMake((self.bounds.size.width * CSToastMaxWidth) - imageWidth, self.bounds.size.height * CSToastMaxHeight);
+        CGSize expectedSizeMessage = [self sizeForString:message font:messageLabel.font constrainedToSize:maxSizeMessage lineBreakMode:messageLabel.lineBreakMode];
+        messageLabel.frame = CGRectMake(0.0, 0.0, expectedSizeMessage.width, expectedSizeMessage.height);
+    }
+    
+    // titleLabel frame values
+    CGFloat titleWidth, titleHeight, titleTop, titleLeft;
+    
+    if(titleLabel != nil) {
+        titleWidth = titleLabel.bounds.size.width;
+        titleHeight = titleLabel.bounds.size.height;
+        titleTop = CSToastVerticalPadding;
+        titleLeft = imageLeft + imageWidth + CSToastHorizontalPadding;
+    } else {
+        titleWidth = titleHeight = titleTop = titleLeft = 0.0;
+    }
+    
+    // messageLabel frame values
+    CGFloat messageWidth, messageHeight, messageLeft, messageTop;
+
+    if(messageLabel != nil) {
+        messageWidth = messageLabel.bounds.size.width;
+        messageHeight = messageLabel.bounds.size.height;
+        messageLeft = imageLeft + imageWidth + CSToastHorizontalPadding;
+        messageTop = titleTop + titleHeight + CSToastVerticalPadding;
+    } else {
+        messageWidth = messageHeight = messageLeft = messageTop = 0.0;
+    }
+
+    CGFloat longerWidth = MAX(titleWidth, messageWidth);
+    CGFloat longerLeft = MAX(titleLeft, messageLeft);
+    
+    // wrapper width uses the longerWidth or the image width, whatever is larger. same logic applies to the wrapper height
+    CGFloat wrapperWidth = MAX((imageWidth + (CSToastHorizontalPadding * 2)), (longerLeft + longerWidth + CSToastHorizontalPadding));    
+    CGFloat wrapperHeight = MAX((messageTop + messageHeight + CSToastVerticalPadding), (imageHeight + (CSToastVerticalPadding * 2)));
+                         
+    wrapperView.frame = CGRectMake(0.0, 0.0, wrapperWidth, wrapperHeight);
+    
+    if(titleLabel != nil) {
+        titleLabel.frame = CGRectMake(titleLeft, titleTop, titleWidth, titleHeight);
+        [wrapperView addSubview:titleLabel];
+    }
+    
+    if(messageLabel != nil) {
+        messageLabel.frame = CGRectMake(messageLeft, messageTop, messageWidth, messageHeight);
+        [wrapperView addSubview:messageLabel];
+    }
+    
+    if(imageView != nil) {
+        [wrapperView addSubview:imageView];
+    }
+        
+    return wrapperView;
+}
+
+@end

+ 47 - 0
ios/RCTToast/Toast.m

@@ -0,0 +1,47 @@
+#import <UIKit/UIKit.h>
+#import <React/RCTLog.h>
+#import <React/RCTBridgeModule.h>
+#import "Toast+UIView.h"
+
+
+@interface Toast : NSObject <RCTBridgeModule>
+@end
+
+@implementation Toast
+
+RCT_EXPORT_MODULE(Toast)
+
+
+RCT_EXPORT_METHOD(show:(NSDictionary *)options) {
+    NSString *message  = [options objectForKey:@"message"];
+    NSString *duration = [options objectForKey:@"duration"];
+    NSString *position = [options objectForKey:@"position"];
+    NSNumber *addPixelsY = [options objectForKey:@"addPixelsY"];
+    
+    if (![position isEqual: @"top"] && ![position isEqual: @"center"] && ![position isEqual: @"bottom"]) {
+        RCTLogError(@"invalid position. valid options are 'top', 'center' and 'bottom'");
+        return;
+    }
+    
+    NSInteger durationInt;
+    if ([duration isEqual: @"short"]) {
+        durationInt = 2;
+    } else if ([duration isEqual: @"long"]) {
+        durationInt = 5;
+    } else {
+        RCTLogError(@"invalid duration. valid options are 'short' and 'long'");
+        return;
+    }
+    
+    dispatch_async(dispatch_get_main_queue(), ^{
+        [[[[UIApplication sharedApplication]windows]firstObject] makeToast:message duration:durationInt position:position addPixelsY:addPixelsY == nil ? 0 : [addPixelsY intValue]];
+    });
+}
+
+RCT_EXPORT_METHOD(hide) {
+    dispatch_async(dispatch_get_main_queue(), ^{
+        [[[[UIApplication sharedApplication]windows]firstObject] hideToast];
+    });
+}
+
+@end

+ 29 - 0
package.json

@@ -0,0 +1,29 @@
+{
+    "name": "react-native-toast",
+    "version": "1.0.7",
+    "description": "A android like toast for react-native support for ios and android",
+    "main": "index.js",
+    "author": {
+        "name": "YunJiang.Fang",
+        "email": "42550564@qq.com"
+    },
+    "license": "MIT",
+    "keywords": [
+        "react-native",
+        "react-component",
+        "ios",
+        "android",
+        "toast",
+        "position",
+        "remobile",
+        "mobile"
+    ],
+    "homepage": "https://github.com/remobile/react-native-toast",
+    "bugs": {
+        "url": "https://github.com/remobile/react-native-toast/issues"
+    },
+    "repository": {
+        "type": "git",
+        "url": "git://github.com/remobile/react-native-toast.git"
+    }
+}

+ 16 - 0
react-native-toast.podspec

@@ -0,0 +1,16 @@
+require "json"
+package = JSON.parse(File.read('package.json'))
+
+Pod::Spec.new do |s|
+  s.name             = package['name']
+  s.version          = package['version']
+  s.summary          = package['description']
+  s.requires_arc = true
+  s.license      = 'MIT'
+  s.homepage     = 'n/a'
+  s.source       = { :git => "https://github.com/chenmo230/react-native-toast" }
+  s.author       = 'BA'
+  s.source_files = 'ios/**/*.{h,m}'
+  s.platform     = :ios, "8.0"
+  s.dependency 'React-Core'
+end

二進制
screencasts/ios.gif