├── .gitignore ├── .npmignore ├── LICENSE ├── README.md ├── android ├── build.gradle └── src │ └── main │ ├── AndroidManifest.xml │ └── java │ └── com │ └── remobile │ └── toast │ ├── RCTToastPackage.java │ └── Toast.java ├── index.js ├── ios ├── RCTToast.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ └── contents.xcworkspacedata └── RCTToast │ ├── Toast+UIView.h │ ├── Toast+UIView.m │ └── Toast.m ├── package.json └── screencasts └── ios.gif /.gitignore: -------------------------------------------------------------------------------- 1 | *.[aod] 2 | *.DS_Store 3 | .DS_Store 4 | *Thumbs.db 5 | *.iml 6 | .gradle 7 | .idea 8 | node_modules 9 | npm-debug.log 10 | /android/build 11 | /ios/**/*xcuserdata* 12 | /ios/**/*xcshareddata* 13 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | *.DS_Store 2 | .DS_Store 3 | *Thumbs.db 4 | .gradle 5 | .idea 6 | *.iml 7 | npm-debug.log 8 | node_modules 9 | screencasts 10 | /android/build 11 | /ios/**/*xcuserdata* 12 | /ios/**/*xcshareddata* 13 | 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | (The MIT License) 2 | 3 | Copyright (c) 2015-2016 YunJiang.Fang <42550564@qq.com> 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | 'Software'), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 21 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 22 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # React Native Toast (remobile) 2 | A android like toast for react-native support for ios and android 3 | 4 | ## Installation 5 | ```sh 6 | npm install @remobile/react-native-toast --save 7 | ``` 8 | 9 | ### Installation (iOS) 10 | * Drag RCTToast.xcodeproj to your project on Xcode. 11 | * 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. 12 | * Look for Header Search Paths and make sure it contains both $(SRCROOT)/../../../react-native/React as recursive. 13 | 14 | ### Installation (Android) 15 | ```gradle 16 | ... 17 | include ':react-native-toast' 18 | project(':react-native-toast').projectDir = new File(settingsDir, '../node_modules/@remobile/react-native-toast/android') 19 | ``` 20 | 21 | * In `android/app/build.gradle` 22 | 23 | ```gradle 24 | ... 25 | dependencies { 26 | ... 27 | compile project(':react-native-toast') 28 | } 29 | ``` 30 | 31 | * register module (in MainApplication.java) 32 | 33 | ```java 34 | ...... 35 | import com.remobile.toast.RCTToastPackage; // <--- import 36 | 37 | ...... 38 | 39 | @Override 40 | protected List getPackages() { 41 | ...... 42 | new RCTToastPackage(), // <------ add here 43 | ...... 44 | } 45 | 46 | ``` 47 | ``` 48 | 49 | ### Screencasts 50 | ![ios](https://github.com/remobile/react-native-toast/blob/master/screencasts/ios.gif) 51 | 52 | ## Usage 53 | 54 | ### Example 55 | ```js 56 | var React = require('react'); 57 | var ReactNative = require('react-native'); 58 | var { 59 | StyleSheet, 60 | View, 61 | Image 62 | } = ReactNative; 63 | 64 | var Toast = require('react-native-toast'); 65 | var Button = require('@remobile/react-native-simple-button'); 66 | 67 | module.exports = React.createClass({ 68 | render() { 69 | return ( 70 | 71 | 74 | 77 | 80 | 83 | 86 | 89 | 92 | 93 | ); 94 | }, 95 | }); 96 | 97 | 98 | var styles = StyleSheet.create({ 99 | container: { 100 | flex: 1, 101 | justifyContent: 'space-around', 102 | alignItems: 'center', 103 | backgroundColor: 'transparent', 104 | paddingVertical:150, 105 | } 106 | }); 107 | ``` 108 | 109 | ### HELP 110 | * look https://github.com/EddyVerbruggen/Toast-PhoneGap-Plugin 111 | 112 | 113 | ### thanks 114 | * this project come from https://github.com/EddyVerbruggen/Toast-PhoneGap-Plugin 115 | 116 | ### see detail use 117 | * https://github.com/remobile/react-native-template 118 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | android { 4 | compileSdkVersion 23 5 | buildToolsVersion "23.0.1" 6 | 7 | defaultConfig { 8 | minSdkVersion 16 9 | targetSdkVersion 22 10 | versionCode 1 11 | versionName "1.0" 12 | } 13 | buildTypes { 14 | release { 15 | minifyEnabled false 16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 17 | } 18 | } 19 | } 20 | 21 | dependencies { 22 | compile fileTree(dir: 'libs', include: ['*.jar']) 23 | compile 'com.android.support:appcompat-v7:23.0.1' 24 | compile 'com.facebook.react:react-native:+' 25 | } 26 | -------------------------------------------------------------------------------- /android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /android/src/main/java/com/remobile/toast/RCTToastPackage.java: -------------------------------------------------------------------------------- 1 | package com.remobile.toast; 2 | 3 | import java.util.Arrays; 4 | import java.util.Collections; 5 | import java.util.List; 6 | 7 | import com.facebook.react.ReactPackage; 8 | import com.facebook.react.bridge.JavaScriptModule; 9 | import com.facebook.react.bridge.NativeModule; 10 | import com.facebook.react.bridge.ReactApplicationContext; 11 | import com.facebook.react.uimanager.ViewManager; 12 | 13 | public class RCTToastPackage implements ReactPackage { 14 | @Override 15 | public List createNativeModules(ReactApplicationContext reactContext) { 16 | return Arrays.asList( 17 | new Toast(reactContext) 18 | ); 19 | } 20 | 21 | public List> createJSModules() { 22 | return Collections.emptyList(); 23 | } 24 | 25 | @Override 26 | public List createViewManagers(ReactApplicationContext reactContext) { 27 | return Arrays.asList(); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /android/src/main/java/com/remobile/toast/Toast.java: -------------------------------------------------------------------------------- 1 | package com.remobile.toast; 2 | 3 | import android.view.Gravity; 4 | 5 | import com.facebook.common.logging.FLog; 6 | import com.facebook.react.bridge.*; 7 | 8 | public class Toast extends ReactContextBaseJavaModule implements LifecycleEventListener { 9 | 10 | private android.widget.Toast mostRecentToast; 11 | 12 | // note that webView.isPaused() is not Xwalk compatible, so tracking it poor-man style 13 | private boolean isPaused; 14 | 15 | public Toast(ReactApplicationContext reactContext) { 16 | super(reactContext); 17 | } 18 | 19 | @Override 20 | public String getName() { 21 | return "RCTToast"; 22 | } 23 | 24 | @ReactMethod 25 | public void show(ReadableMap options) throws Exception { 26 | if (this.isPaused) { 27 | return; 28 | } 29 | 30 | 31 | final String message = options.getString("message"); 32 | final String duration = options.getString("duration"); 33 | final String position = options.getString("position"); 34 | final int addPixelsY = options.hasKey("addPixelsY") ? options.getInt("addPixelsY") : 0; 35 | 36 | UiThreadUtil.runOnUiThread(new Runnable() { 37 | public void run() { 38 | android.widget.Toast toast = android.widget.Toast.makeText( 39 | getReactApplicationContext(), 40 | message, 41 | "short".equals(duration) ? android.widget.Toast.LENGTH_SHORT : android.widget.Toast.LENGTH_LONG); 42 | 43 | if ("top".equals(position)) { 44 | toast.setGravity(Gravity.TOP | Gravity.CENTER_HORIZONTAL, 0, 20 + addPixelsY); 45 | } else if ("bottom".equals(position)) { 46 | toast.setGravity(Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL, 0, 20 - addPixelsY); 47 | } else if ("center".equals(position)) { 48 | toast.setGravity(Gravity.CENTER_VERTICAL | Gravity.CENTER_HORIZONTAL, 0, addPixelsY); 49 | } else { 50 | FLog.e("RCTToast", "invalid position. valid options are 'top', 'center' and 'bottom'"); 51 | return; 52 | } 53 | 54 | toast.show(); 55 | mostRecentToast = toast; 56 | } 57 | }); 58 | } 59 | 60 | @ReactMethod 61 | public void hide() throws Exception { 62 | if (mostRecentToast != null) { 63 | mostRecentToast.cancel(); 64 | } 65 | } 66 | 67 | @Override 68 | public void initialize() { 69 | getReactApplicationContext().addLifecycleEventListener(this); 70 | } 71 | 72 | @Override 73 | public void onHostPause() { 74 | if (mostRecentToast != null) { 75 | mostRecentToast.cancel(); 76 | } 77 | this.isPaused = true; 78 | } 79 | 80 | @Override 81 | public void onHostResume() { 82 | this.isPaused = false; 83 | } 84 | 85 | @Override 86 | public void onHostDestroy() { 87 | this.isPaused = true; 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const ReactNative = require('react-native'); 4 | const { 5 | NativeModules, 6 | } = ReactNative; 7 | 8 | const RCTToast = NativeModules.Toast; 9 | const Toast = {}; 10 | 11 | const optionsBuilder = function () { 12 | // defaults 13 | let message = null; 14 | let duration = 'short'; 15 | let position = 'center'; 16 | let addPixelsY = 0; 17 | 18 | return { 19 | withMessage: function (m) { 20 | message = m; 21 | return this; 22 | }, 23 | 24 | withDuration: function (d) { 25 | duration = d; 26 | return this; 27 | }, 28 | 29 | withPosition: function (p) { 30 | position = p; 31 | return this; 32 | }, 33 | 34 | withAddPixelsY: function (y) { 35 | addPixelsY = y; 36 | return this; 37 | }, 38 | 39 | build: function () { 40 | return { 41 | message: message, 42 | duration: duration, 43 | position: position, 44 | addPixelsY: addPixelsY, 45 | }; 46 | }, 47 | }; 48 | }; 49 | 50 | const showWithOptions = function (options) { 51 | RCTToast.show(options); 52 | }; 53 | 54 | const showToast = function (message, duration, position) { 55 | showWithOptions( 56 | optionsBuilder() 57 | .withMessage(message || '未知数据') 58 | .withDuration(duration) 59 | .withPosition(position) 60 | .build() 61 | ); 62 | }; 63 | 64 | Toast.showShortTop = function (message) { 65 | showToast(message, 'short', 'top'); 66 | }; 67 | 68 | Toast.showShortCenter = function (message) { 69 | showToast(message, 'short', 'center'); 70 | }; 71 | 72 | Toast.showShortBottom = function (message) { 73 | showToast(message, 'short', 'bottom'); 74 | }; 75 | 76 | Toast.showLongTop = function (message) { 77 | showToast(message, 'long', 'top'); 78 | }; 79 | 80 | Toast.showLongCenter = function (message) { 81 | showToast(message, 'long', 'center'); 82 | }; 83 | 84 | Toast.showLongBottom = function (message) { 85 | showToast(message, 'long', 'bottom'); 86 | }; 87 | 88 | Toast.show = function (message) { 89 | showToast(message, 'short', 'bottom'); 90 | }; 91 | 92 | Toast.hide = function () { 93 | RCTToast.hide(); 94 | }; 95 | 96 | module.exports = Toast; 97 | -------------------------------------------------------------------------------- /ios/RCTToast.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 327633601BFAAE28004DA88E /* Toast.m in Sources */ = {isa = PBXBuildFile; fileRef = 3276335D1BFAAE28004DA88E /* Toast.m */; }; 11 | 327633611BFAAE28004DA88E /* Toast+UIView.m in Sources */ = {isa = PBXBuildFile; fileRef = 3276335F1BFAAE28004DA88E /* Toast+UIView.m */; }; 12 | /* End PBXBuildFile section */ 13 | 14 | /* Begin PBXCopyFilesBuildPhase section */ 15 | 327633401BFAAD7E004DA88E /* CopyFiles */ = { 16 | isa = PBXCopyFilesBuildPhase; 17 | buildActionMask = 2147483647; 18 | dstPath = "include/$(PRODUCT_NAME)"; 19 | dstSubfolderSpec = 16; 20 | files = ( 21 | ); 22 | runOnlyForDeploymentPostprocessing = 0; 23 | }; 24 | /* End PBXCopyFilesBuildPhase section */ 25 | 26 | /* Begin PBXFileReference section */ 27 | 327633421BFAAD7E004DA88E /* libRCTToast.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRCTToast.a; sourceTree = BUILT_PRODUCTS_DIR; }; 28 | 3276335D1BFAAE28004DA88E /* Toast.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Toast.m; sourceTree = ""; }; 29 | 3276335E1BFAAE28004DA88E /* Toast+UIView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "Toast+UIView.h"; sourceTree = ""; }; 30 | 3276335F1BFAAE28004DA88E /* Toast+UIView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "Toast+UIView.m"; sourceTree = ""; }; 31 | /* End PBXFileReference section */ 32 | 33 | /* Begin PBXFrameworksBuildPhase section */ 34 | 3276333F1BFAAD7E004DA88E /* Frameworks */ = { 35 | isa = PBXFrameworksBuildPhase; 36 | buildActionMask = 2147483647; 37 | files = ( 38 | ); 39 | runOnlyForDeploymentPostprocessing = 0; 40 | }; 41 | /* End PBXFrameworksBuildPhase section */ 42 | 43 | /* Begin PBXGroup section */ 44 | 327633391BFAAD7E004DA88E = { 45 | isa = PBXGroup; 46 | children = ( 47 | 327633441BFAAD7E004DA88E /* RCTToast */, 48 | 327633431BFAAD7E004DA88E /* Products */, 49 | ); 50 | sourceTree = ""; 51 | }; 52 | 327633431BFAAD7E004DA88E /* Products */ = { 53 | isa = PBXGroup; 54 | children = ( 55 | 327633421BFAAD7E004DA88E /* libRCTToast.a */, 56 | ); 57 | name = Products; 58 | sourceTree = ""; 59 | }; 60 | 327633441BFAAD7E004DA88E /* RCTToast */ = { 61 | isa = PBXGroup; 62 | children = ( 63 | 3276335D1BFAAE28004DA88E /* Toast.m */, 64 | 3276335E1BFAAE28004DA88E /* Toast+UIView.h */, 65 | 3276335F1BFAAE28004DA88E /* Toast+UIView.m */, 66 | ); 67 | path = RCTToast; 68 | sourceTree = ""; 69 | }; 70 | /* End PBXGroup section */ 71 | 72 | /* Begin PBXNativeTarget section */ 73 | 327633411BFAAD7E004DA88E /* RCTToast */ = { 74 | isa = PBXNativeTarget; 75 | buildConfigurationList = 327633561BFAAD7E004DA88E /* Build configuration list for PBXNativeTarget "RCTToast" */; 76 | buildPhases = ( 77 | 3276333E1BFAAD7E004DA88E /* Sources */, 78 | 3276333F1BFAAD7E004DA88E /* Frameworks */, 79 | 327633401BFAAD7E004DA88E /* CopyFiles */, 80 | ); 81 | buildRules = ( 82 | ); 83 | dependencies = ( 84 | ); 85 | name = RCTToast; 86 | productName = RCTToast; 87 | productReference = 327633421BFAAD7E004DA88E /* libRCTToast.a */; 88 | productType = "com.apple.product-type.library.static"; 89 | }; 90 | /* End PBXNativeTarget section */ 91 | 92 | /* Begin PBXProject section */ 93 | 3276333A1BFAAD7E004DA88E /* Project object */ = { 94 | isa = PBXProject; 95 | attributes = { 96 | LastUpgradeCheck = 0640; 97 | ORGANIZATIONNAME = remobile; 98 | TargetAttributes = { 99 | 327633411BFAAD7E004DA88E = { 100 | CreatedOnToolsVersion = 6.4; 101 | }; 102 | }; 103 | }; 104 | buildConfigurationList = 3276333D1BFAAD7E004DA88E /* Build configuration list for PBXProject "RCTToast" */; 105 | compatibilityVersion = "Xcode 3.2"; 106 | developmentRegion = English; 107 | hasScannedForEncodings = 0; 108 | knownRegions = ( 109 | en, 110 | ); 111 | mainGroup = 327633391BFAAD7E004DA88E; 112 | productRefGroup = 327633431BFAAD7E004DA88E /* Products */; 113 | projectDirPath = ""; 114 | projectRoot = ""; 115 | targets = ( 116 | 327633411BFAAD7E004DA88E /* RCTToast */, 117 | ); 118 | }; 119 | /* End PBXProject section */ 120 | 121 | /* Begin PBXSourcesBuildPhase section */ 122 | 3276333E1BFAAD7E004DA88E /* Sources */ = { 123 | isa = PBXSourcesBuildPhase; 124 | buildActionMask = 2147483647; 125 | files = ( 126 | 327633611BFAAE28004DA88E /* Toast+UIView.m in Sources */, 127 | 327633601BFAAE28004DA88E /* Toast.m in Sources */, 128 | ); 129 | runOnlyForDeploymentPostprocessing = 0; 130 | }; 131 | /* End PBXSourcesBuildPhase section */ 132 | 133 | /* Begin XCBuildConfiguration section */ 134 | 327633541BFAAD7E004DA88E /* Debug */ = { 135 | isa = XCBuildConfiguration; 136 | buildSettings = { 137 | ALWAYS_SEARCH_USER_PATHS = NO; 138 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 139 | CLANG_CXX_LIBRARY = "libc++"; 140 | CLANG_ENABLE_MODULES = YES; 141 | CLANG_ENABLE_OBJC_ARC = YES; 142 | CLANG_WARN_BOOL_CONVERSION = YES; 143 | CLANG_WARN_CONSTANT_CONVERSION = YES; 144 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 145 | CLANG_WARN_EMPTY_BODY = YES; 146 | CLANG_WARN_ENUM_CONVERSION = YES; 147 | CLANG_WARN_INT_CONVERSION = YES; 148 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 149 | CLANG_WARN_UNREACHABLE_CODE = YES; 150 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 151 | COPY_PHASE_STRIP = NO; 152 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 153 | ENABLE_STRICT_OBJC_MSGSEND = YES; 154 | GCC_C_LANGUAGE_STANDARD = gnu99; 155 | GCC_DYNAMIC_NO_PIC = NO; 156 | GCC_NO_COMMON_BLOCKS = YES; 157 | GCC_OPTIMIZATION_LEVEL = 0; 158 | GCC_PREPROCESSOR_DEFINITIONS = ( 159 | "DEBUG=1", 160 | "$(inherited)", 161 | ); 162 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 163 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 164 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 165 | GCC_WARN_UNDECLARED_SELECTOR = YES; 166 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 167 | GCC_WARN_UNUSED_FUNCTION = YES; 168 | GCC_WARN_UNUSED_VARIABLE = YES; 169 | HEADER_SEARCH_PATHS = ( 170 | "$(inherited)", 171 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, 172 | "$(SRCROOT)/../../../react-native/React/**", 173 | "$(SRCROOT)/../../../../node_modules/react-native/React/**", 174 | ); 175 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 176 | MTL_ENABLE_DEBUG_INFO = YES; 177 | ONLY_ACTIVE_ARCH = YES; 178 | SDKROOT = iphoneos; 179 | }; 180 | name = Debug; 181 | }; 182 | 327633551BFAAD7E004DA88E /* Release */ = { 183 | isa = XCBuildConfiguration; 184 | buildSettings = { 185 | ALWAYS_SEARCH_USER_PATHS = NO; 186 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 187 | CLANG_CXX_LIBRARY = "libc++"; 188 | CLANG_ENABLE_MODULES = YES; 189 | CLANG_ENABLE_OBJC_ARC = YES; 190 | CLANG_WARN_BOOL_CONVERSION = YES; 191 | CLANG_WARN_CONSTANT_CONVERSION = YES; 192 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 193 | CLANG_WARN_EMPTY_BODY = YES; 194 | CLANG_WARN_ENUM_CONVERSION = YES; 195 | CLANG_WARN_INT_CONVERSION = YES; 196 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 197 | CLANG_WARN_UNREACHABLE_CODE = YES; 198 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 199 | COPY_PHASE_STRIP = NO; 200 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 201 | ENABLE_NS_ASSERTIONS = NO; 202 | ENABLE_STRICT_OBJC_MSGSEND = YES; 203 | GCC_C_LANGUAGE_STANDARD = gnu99; 204 | GCC_NO_COMMON_BLOCKS = YES; 205 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 206 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 207 | GCC_WARN_UNDECLARED_SELECTOR = YES; 208 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 209 | GCC_WARN_UNUSED_FUNCTION = YES; 210 | GCC_WARN_UNUSED_VARIABLE = YES; 211 | HEADER_SEARCH_PATHS = ( 212 | "$(inherited)", 213 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, 214 | "$(SRCROOT)/../../../react-native/React/**", 215 | "$(SRCROOT)/../../../../node_modules/react-native/React/**", 216 | ); 217 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 218 | MTL_ENABLE_DEBUG_INFO = NO; 219 | SDKROOT = iphoneos; 220 | VALIDATE_PRODUCT = YES; 221 | }; 222 | name = Release; 223 | }; 224 | 327633571BFAAD7E004DA88E /* Debug */ = { 225 | isa = XCBuildConfiguration; 226 | buildSettings = { 227 | HEADER_SEARCH_PATHS = ( 228 | "$(inherited)", 229 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, 230 | ); 231 | OTHER_LDFLAGS = "-ObjC"; 232 | PRODUCT_NAME = "$(TARGET_NAME)"; 233 | SKIP_INSTALL = YES; 234 | }; 235 | name = Debug; 236 | }; 237 | 327633581BFAAD7E004DA88E /* Release */ = { 238 | isa = XCBuildConfiguration; 239 | buildSettings = { 240 | HEADER_SEARCH_PATHS = ( 241 | "$(inherited)", 242 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, 243 | ); 244 | OTHER_LDFLAGS = "-ObjC"; 245 | PRODUCT_NAME = "$(TARGET_NAME)"; 246 | SKIP_INSTALL = YES; 247 | }; 248 | name = Release; 249 | }; 250 | /* End XCBuildConfiguration section */ 251 | 252 | /* Begin XCConfigurationList section */ 253 | 3276333D1BFAAD7E004DA88E /* Build configuration list for PBXProject "RCTToast" */ = { 254 | isa = XCConfigurationList; 255 | buildConfigurations = ( 256 | 327633541BFAAD7E004DA88E /* Debug */, 257 | 327633551BFAAD7E004DA88E /* Release */, 258 | ); 259 | defaultConfigurationIsVisible = 0; 260 | defaultConfigurationName = Release; 261 | }; 262 | 327633561BFAAD7E004DA88E /* Build configuration list for PBXNativeTarget "RCTToast" */ = { 263 | isa = XCConfigurationList; 264 | buildConfigurations = ( 265 | 327633571BFAAD7E004DA88E /* Debug */, 266 | 327633581BFAAD7E004DA88E /* Release */, 267 | ); 268 | defaultConfigurationIsVisible = 0; 269 | defaultConfigurationName = Release; 270 | }; 271 | /* End XCConfigurationList section */ 272 | }; 273 | rootObject = 3276333A1BFAAD7E004DA88E /* Project object */; 274 | } 275 | -------------------------------------------------------------------------------- /ios/RCTToast.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ios/RCTToast/Toast+UIView.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | @interface UIView (Toast) 5 | 6 | // each makeToast method creates a view and displays it as toast 7 | - (void)makeToast:(NSString *)message; 8 | - (void)makeToast:(NSString *)message duration:(NSTimeInterval)interval position:(id)position; 9 | - (void)makeToast:(NSString *)message duration:(NSTimeInterval)duration position:(id)position addPixelsY:(int)addPixelsY; 10 | - (void)makeToast:(NSString *)message duration:(NSTimeInterval)interval position:(id)position image:(UIImage *)image; 11 | - (void)makeToast:(NSString *)message duration:(NSTimeInterval)interval position:(id)position title:(NSString *)title; 12 | - (void)makeToast:(NSString *)message duration:(NSTimeInterval)interval position:(id)position title:(NSString *)title image:(UIImage *)image; 13 | 14 | - (void)hideToast; 15 | 16 | // displays toast with an activity spinner 17 | - (void)makeToastActivity; 18 | - (void)makeToastActivity:(id)position; 19 | - (void)hideToastActivity; 20 | 21 | // the showToast methods display any view as toast 22 | - (void)showToast:(UIView *)toast; 23 | - (void)showToast:(UIView *)toast duration:(NSTimeInterval)interval position:(id)point ; 24 | - (void)showToast:(UIView *)toast duration:(NSTimeInterval)interval position:(id)point addedPixelsY:(int)addPixelsY; 25 | 26 | @end 27 | -------------------------------------------------------------------------------- /ios/RCTToast/Toast+UIView.m: -------------------------------------------------------------------------------- 1 | #import "Toast+UIView.h" 2 | #import 3 | #import 4 | 5 | /* 6 | * CONFIGURE THESE VALUES TO ADJUST LOOK & FEEL, 7 | * DISPLAY DURATION, ETC. 8 | */ 9 | 10 | // general appearance 11 | static const CGFloat CSToastMaxWidth = 0.8; // 80% of parent view width 12 | static const CGFloat CSToastMaxHeight = 0.8; // 80% of parent view height 13 | static const CGFloat CSToastHorizontalPadding = 10.0; 14 | static const CGFloat CSToastVerticalPadding = 10.0; 15 | static const CGFloat CSToastTopBottomOffset = 20.0; 16 | static const CGFloat CSToastCornerRadius = 5.0; 17 | static const CGFloat CSToastOpacity = 0.8; 18 | static const CGFloat CSToastFontSize = 16.0; 19 | static const CGFloat CSToastMaxTitleLines = 0; 20 | static const CGFloat CSToastMaxMessageLines = 0; 21 | static const NSTimeInterval CSToastFadeDuration = 0.2; 22 | 23 | // shadow appearance 24 | static const CGFloat CSToastShadowOpacity = 0.8; 25 | static const CGFloat CSToastShadowRadius = 6.0; 26 | static const CGSize CSToastShadowOffset = { 4.0, 4.0 }; 27 | static const BOOL CSToastDisplayShadow = YES; 28 | 29 | // display duration and position 30 | static const NSString * CSToastDefaultPosition = @"bottom"; 31 | static const NSTimeInterval CSToastDefaultDuration = 1.5; 32 | 33 | // image view size 34 | static const CGFloat CSToastImageViewWidth = 80.0; 35 | static const CGFloat CSToastImageViewHeight = 80.0; 36 | 37 | // activity 38 | static const CGFloat CSToastActivityWidth = 100.0; 39 | static const CGFloat CSToastActivityHeight = 100.0; 40 | static const NSString * CSToastActivityDefaultPosition = @"center"; 41 | 42 | // interaction 43 | static const BOOL CSToastHidesOnTap = YES; // excludes activity views 44 | 45 | // associative reference keys 46 | static const NSString * CSToastTimerKey = @"CSToastTimerKey"; 47 | static const NSString * CSToastActivityViewKey = @"CSToastActivityViewKey"; 48 | 49 | static UIView *prevToast = NULL; 50 | 51 | @interface UIView (ToastPrivate) 52 | 53 | - (void)hideToast:(UIView *)toast; 54 | - (void)toastTimerDidFinish:(NSTimer *)timer; 55 | - (void)handleToastTapped:(UITapGestureRecognizer *)recognizer; 56 | - (CGPoint)centerPointForPosition:(id)position withToast:(UIView *)toast withAddedPixelsY:(int) addPixelsY; 57 | - (UIView *)viewForMessage:(NSString *)message title:(NSString *)title image:(UIImage *)image; 58 | - (CGSize)sizeForString:(NSString *)string font:(UIFont *)font constrainedToSize:(CGSize)constrainedSize lineBreakMode:(NSLineBreakMode)lineBreakMode; 59 | 60 | @end 61 | 62 | 63 | @implementation UIView (Toast) 64 | 65 | #pragma mark - Toast Methods 66 | 67 | - (void)makeToast:(NSString *)message { 68 | [self makeToast:message duration:CSToastDefaultDuration position:CSToastDefaultPosition]; 69 | } 70 | 71 | - (void)makeToast:(NSString *)message duration:(NSTimeInterval)duration position:(id)position { 72 | UIView *toast = [self viewForMessage:message title:nil image:nil]; 73 | [self showToast:toast duration:duration position:position]; 74 | } 75 | 76 | - (void)makeToast:(NSString *)message duration:(NSTimeInterval)duration position:(id)position addPixelsY:(int)addPixelsY { 77 | UIView *toast = [self viewForMessage:message title:nil image:nil]; 78 | [self showToast:toast duration:duration position:position addedPixelsY:addPixelsY]; 79 | } 80 | 81 | - (void)makeToast:(NSString *)message duration:(NSTimeInterval)duration position:(id)position title:(NSString *)title { 82 | UIView *toast = [self viewForMessage:message title:title image:nil]; 83 | [self showToast:toast duration:duration position:position]; 84 | } 85 | 86 | - (void)makeToast:(NSString *)message duration:(NSTimeInterval)duration position:(id)position image:(UIImage *)image { 87 | UIView *toast = [self viewForMessage:message title:nil image:image]; 88 | [self showToast:toast duration:duration position:position]; 89 | } 90 | 91 | - (void)makeToast:(NSString *)message duration:(NSTimeInterval)duration position:(id)position title:(NSString *)title image:(UIImage *)image { 92 | UIView *toast = [self viewForMessage:message title:title image:image]; 93 | [self showToast:toast duration:duration position:position]; 94 | } 95 | 96 | - (void)showToast:(UIView *)toast { 97 | [self showToast:toast duration:CSToastDefaultDuration position:CSToastDefaultPosition]; 98 | } 99 | 100 | - (void)showToast:(UIView *)toast duration:(NSTimeInterval)duration position:(id)point { 101 | [self showToast:toast duration:CSToastDefaultDuration position:CSToastDefaultPosition addedPixelsY:0]; 102 | } 103 | 104 | - (void)showToast:(UIView *)toast duration:(NSTimeInterval)duration position:(id)point addedPixelsY:(int) addPixelsY { 105 | [self hideToast]; 106 | prevToast = toast; 107 | toast.center = [self centerPointForPosition:point withToast:toast withAddedPixelsY:addPixelsY]; 108 | toast.alpha = 0.0; 109 | 110 | if (CSToastHidesOnTap) { 111 | UITapGestureRecognizer *recognizer = [[UITapGestureRecognizer alloc] initWithTarget:toast action:@selector(handleToastTapped:)]; 112 | [toast addGestureRecognizer:recognizer]; 113 | toast.userInteractionEnabled = YES; 114 | toast.exclusiveTouch = YES; 115 | } 116 | 117 | // make sure that if InAppBrowser is active, we're still showing Toasts on top of it 118 | UIViewController *vc = [self getTopMostViewController]; 119 | UIView *v = [vc view]; 120 | [v addSubview:toast]; 121 | 122 | [UIView animateWithDuration:CSToastFadeDuration 123 | delay:0.0 124 | options:(UIViewAnimationOptionCurveEaseOut | UIViewAnimationOptionAllowUserInteraction) 125 | animations:^{ 126 | toast.alpha = CSToastOpacity; 127 | } completion:^(BOOL finished) { 128 | NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:duration target:self selector:@selector(toastTimerDidFinish:) userInfo:toast repeats:NO]; 129 | // associate the timer with the toast view 130 | objc_setAssociatedObject (toast, &CSToastTimerKey, timer, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 131 | }]; 132 | 133 | } 134 | 135 | - (UIViewController*) getTopMostViewController { 136 | UIViewController *presentingViewController = [[[UIApplication sharedApplication] delegate] window].rootViewController; 137 | while (presentingViewController.presentedViewController != nil) { 138 | presentingViewController = presentingViewController.presentedViewController; 139 | } 140 | return presentingViewController; 141 | } 142 | 143 | - (void)hideToast { 144 | if (prevToast){ 145 | [self hideToast:prevToast]; 146 | } 147 | } 148 | 149 | - (void)hideToast:(UIView *)toast { 150 | [UIView animateWithDuration:CSToastFadeDuration 151 | delay:0.0 152 | options:(UIViewAnimationOptionCurveEaseIn | UIViewAnimationOptionBeginFromCurrentState) 153 | animations:^{ 154 | toast.alpha = 0.0; 155 | } completion:^(BOOL finished) { 156 | [toast removeFromSuperview]; 157 | }]; 158 | } 159 | 160 | #pragma mark - Events 161 | 162 | - (void)toastTimerDidFinish:(NSTimer *)timer { 163 | [self hideToast:(UIView *)timer.userInfo]; 164 | } 165 | 166 | - (void)handleToastTapped:(UITapGestureRecognizer *)recognizer { 167 | NSTimer *timer = (NSTimer *)objc_getAssociatedObject(self, &CSToastTimerKey); 168 | [timer invalidate]; 169 | 170 | [self hideToast:recognizer.view]; 171 | } 172 | 173 | #pragma mark - Toast Activity Methods 174 | 175 | - (void)makeToastActivity { 176 | [self makeToastActivity:CSToastActivityDefaultPosition]; 177 | } 178 | 179 | - (void)makeToastActivity:(id)position { 180 | // sanity 181 | UIView *existingActivityView = (UIView *)objc_getAssociatedObject(self, &CSToastActivityViewKey); 182 | if (existingActivityView != nil) return; 183 | 184 | UIView *activityView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, CSToastActivityWidth, CSToastActivityHeight)]; 185 | activityView.center = [self centerPointForPosition:position withToast:activityView withAddedPixelsY:0]; 186 | activityView.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:CSToastOpacity]; 187 | activityView.alpha = 0.0; 188 | activityView.autoresizingMask = (UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin); 189 | activityView.layer.cornerRadius = CSToastCornerRadius; 190 | 191 | if (CSToastDisplayShadow) { 192 | activityView.layer.shadowColor = [UIColor blackColor].CGColor; 193 | activityView.layer.shadowOpacity = CSToastShadowOpacity; 194 | activityView.layer.shadowRadius = CSToastShadowRadius; 195 | activityView.layer.shadowOffset = CSToastShadowOffset; 196 | } 197 | 198 | UIActivityIndicatorView *activityIndicatorView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge]; 199 | activityIndicatorView.center = CGPointMake(activityView.bounds.size.width / 2, activityView.bounds.size.height / 2); 200 | [activityView addSubview:activityIndicatorView]; 201 | [activityIndicatorView startAnimating]; 202 | 203 | // associate the activity view with self 204 | objc_setAssociatedObject (self, &CSToastActivityViewKey, activityView, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 205 | 206 | [self addSubview:activityView]; 207 | 208 | [UIView animateWithDuration:CSToastFadeDuration 209 | delay:0.0 210 | options:UIViewAnimationOptionCurveEaseOut 211 | animations:^{ 212 | activityView.alpha = 1.0; 213 | } completion:nil]; 214 | } 215 | 216 | - (void)hideToastActivity { 217 | UIView *existingActivityView = (UIView *)objc_getAssociatedObject(self, &CSToastActivityViewKey); 218 | if (existingActivityView != nil) { 219 | [UIView animateWithDuration:CSToastFadeDuration 220 | delay:0.0 221 | options:(UIViewAnimationOptionCurveEaseIn | UIViewAnimationOptionBeginFromCurrentState) 222 | animations:^{ 223 | existingActivityView.alpha = 0.0; 224 | } completion:^(BOOL finished) { 225 | [existingActivityView removeFromSuperview]; 226 | objc_setAssociatedObject (self, &CSToastActivityViewKey, nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 227 | }]; 228 | } 229 | } 230 | 231 | #pragma mark - Helpers 232 | 233 | - (CGPoint)centerPointForPosition:(id)point withToast:(UIView *)toast withAddedPixelsY:(int) addPixelsY { 234 | if([point isKindOfClass:[NSString class]]) { 235 | // convert string literals @"top", @"bottom", @"center", or any point wrapped in an NSValue object into a CGPoint 236 | if([point caseInsensitiveCompare:@"top"] == NSOrderedSame) { 237 | return CGPointMake(self.bounds.size.width/2, (toast.frame.size.height / 2) + addPixelsY + CSToastVerticalPadding + CSToastTopBottomOffset); 238 | } else if([point caseInsensitiveCompare:@"bottom"] == NSOrderedSame) { 239 | return CGPointMake(self.bounds.size.width/2, (self.bounds.size.height - (toast.frame.size.height / 2)) - CSToastVerticalPadding - CSToastTopBottomOffset + addPixelsY); 240 | } else if([point caseInsensitiveCompare:@"center"] == NSOrderedSame) { 241 | return CGPointMake(self.bounds.size.width / 2, (self.bounds.size.height / 2) + addPixelsY); 242 | } 243 | } else if ([point isKindOfClass:[NSValue class]]) { 244 | return [point CGPointValue]; 245 | } 246 | 247 | NSLog(@"Warning: Invalid position for toast."); 248 | return [self centerPointForPosition:CSToastDefaultPosition withToast:toast withAddedPixelsY:addPixelsY]; 249 | } 250 | 251 | - (CGSize)sizeForString:(NSString *)string font:(UIFont *)font constrainedToSize:(CGSize)constrainedSize lineBreakMode:(NSLineBreakMode)lineBreakMode { 252 | if ([string respondsToSelector:@selector(boundingRectWithSize:options:attributes:context:)]) { 253 | NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init]; 254 | paragraphStyle.lineBreakMode = lineBreakMode; 255 | NSDictionary *attributes = @{NSFontAttributeName:font, NSParagraphStyleAttributeName:paragraphStyle}; 256 | CGRect boundingRect = [string boundingRectWithSize:constrainedSize options:NSStringDrawingUsesLineFragmentOrigin attributes:attributes context:nil]; 257 | return CGSizeMake(ceilf(boundingRect.size.width), ceilf(boundingRect.size.height)); 258 | } 259 | 260 | #pragma clang diagnostic push 261 | #pragma clang diagnostic ignored "-Wdeprecated-declarations" 262 | return [string sizeWithFont:font constrainedToSize:constrainedSize lineBreakMode:lineBreakMode]; 263 | #pragma clang diagnostic pop 264 | } 265 | 266 | - (UIView *)viewForMessage:(NSString *)message title:(NSString *)title image:(UIImage *)image { 267 | // sanity 268 | if((message == nil) && (title == nil) && (image == nil)) return nil; 269 | 270 | // dynamically build a toast view with any combination of message, title, & image. 271 | UILabel *messageLabel = nil; 272 | UILabel *titleLabel = nil; 273 | UIImageView *imageView = nil; 274 | 275 | // create the parent view 276 | UIView *wrapperView = [[UIView alloc] init]; 277 | wrapperView.autoresizingMask = (UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin); 278 | wrapperView.layer.cornerRadius = CSToastCornerRadius; 279 | 280 | if (CSToastDisplayShadow) { 281 | wrapperView.layer.shadowColor = [UIColor blackColor].CGColor; 282 | wrapperView.layer.shadowOpacity = CSToastShadowOpacity; 283 | wrapperView.layer.shadowRadius = CSToastShadowRadius; 284 | wrapperView.layer.shadowOffset = CSToastShadowOffset; 285 | } 286 | 287 | wrapperView.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:CSToastOpacity]; 288 | 289 | if(image != nil) { 290 | imageView = [[UIImageView alloc] initWithImage:image]; 291 | imageView.contentMode = UIViewContentModeScaleAspectFit; 292 | imageView.frame = CGRectMake(CSToastHorizontalPadding, CSToastVerticalPadding, CSToastImageViewWidth, CSToastImageViewHeight); 293 | } 294 | 295 | CGFloat imageWidth, imageHeight, imageLeft; 296 | 297 | // the imageView frame values will be used to size & position the other views 298 | if(imageView != nil) { 299 | imageWidth = imageView.bounds.size.width; 300 | imageHeight = imageView.bounds.size.height; 301 | imageLeft = CSToastHorizontalPadding; 302 | } else { 303 | imageWidth = imageHeight = imageLeft = 0.0; 304 | } 305 | 306 | if (title != nil) { 307 | titleLabel = [[UILabel alloc] init]; 308 | titleLabel.numberOfLines = CSToastMaxTitleLines; 309 | titleLabel.font = [UIFont boldSystemFontOfSize:CSToastFontSize]; 310 | titleLabel.textAlignment = NSTextAlignmentLeft; 311 | titleLabel.lineBreakMode = NSLineBreakByWordWrapping; 312 | titleLabel.textColor = [UIColor whiteColor]; 313 | titleLabel.backgroundColor = [UIColor clearColor]; 314 | titleLabel.alpha = 1.0; 315 | titleLabel.text = title; 316 | 317 | // size the title label according to the length of the text 318 | CGSize maxSizeTitle = CGSizeMake((self.bounds.size.width * CSToastMaxWidth) - imageWidth, self.bounds.size.height * CSToastMaxHeight); 319 | CGSize expectedSizeTitle = [self sizeForString:title font:titleLabel.font constrainedToSize:maxSizeTitle lineBreakMode:titleLabel.lineBreakMode]; 320 | titleLabel.frame = CGRectMake(0.0, 0.0, expectedSizeTitle.width, expectedSizeTitle.height); 321 | } 322 | 323 | if (message != nil) { 324 | messageLabel = [[UILabel alloc] init]; 325 | messageLabel.numberOfLines = CSToastMaxMessageLines; 326 | messageLabel.font = [UIFont systemFontOfSize:CSToastFontSize]; 327 | messageLabel.lineBreakMode = NSLineBreakByWordWrapping; 328 | messageLabel.textColor = [UIColor whiteColor]; 329 | messageLabel.backgroundColor = [UIColor clearColor]; 330 | messageLabel.alpha = 1.0; 331 | messageLabel.text = message; 332 | 333 | // size the message label according to the length of the text 334 | CGSize maxSizeMessage = CGSizeMake((self.bounds.size.width * CSToastMaxWidth) - imageWidth, self.bounds.size.height * CSToastMaxHeight); 335 | CGSize expectedSizeMessage = [self sizeForString:message font:messageLabel.font constrainedToSize:maxSizeMessage lineBreakMode:messageLabel.lineBreakMode]; 336 | messageLabel.frame = CGRectMake(0.0, 0.0, expectedSizeMessage.width, expectedSizeMessage.height); 337 | } 338 | 339 | // titleLabel frame values 340 | CGFloat titleWidth, titleHeight, titleTop, titleLeft; 341 | 342 | if(titleLabel != nil) { 343 | titleWidth = titleLabel.bounds.size.width; 344 | titleHeight = titleLabel.bounds.size.height; 345 | titleTop = CSToastVerticalPadding; 346 | titleLeft = imageLeft + imageWidth + CSToastHorizontalPadding; 347 | } else { 348 | titleWidth = titleHeight = titleTop = titleLeft = 0.0; 349 | } 350 | 351 | // messageLabel frame values 352 | CGFloat messageWidth, messageHeight, messageLeft, messageTop; 353 | 354 | if(messageLabel != nil) { 355 | messageWidth = messageLabel.bounds.size.width; 356 | messageHeight = messageLabel.bounds.size.height; 357 | messageLeft = imageLeft + imageWidth + CSToastHorizontalPadding; 358 | messageTop = titleTop + titleHeight + CSToastVerticalPadding; 359 | } else { 360 | messageWidth = messageHeight = messageLeft = messageTop = 0.0; 361 | } 362 | 363 | CGFloat longerWidth = MAX(titleWidth, messageWidth); 364 | CGFloat longerLeft = MAX(titleLeft, messageLeft); 365 | 366 | // wrapper width uses the longerWidth or the image width, whatever is larger. same logic applies to the wrapper height 367 | CGFloat wrapperWidth = MAX((imageWidth + (CSToastHorizontalPadding * 2)), (longerLeft + longerWidth + CSToastHorizontalPadding)); 368 | CGFloat wrapperHeight = MAX((messageTop + messageHeight + CSToastVerticalPadding), (imageHeight + (CSToastVerticalPadding * 2))); 369 | 370 | wrapperView.frame = CGRectMake(0.0, 0.0, wrapperWidth, wrapperHeight); 371 | 372 | if(titleLabel != nil) { 373 | titleLabel.frame = CGRectMake(titleLeft, titleTop, titleWidth, titleHeight); 374 | [wrapperView addSubview:titleLabel]; 375 | } 376 | 377 | if(messageLabel != nil) { 378 | messageLabel.frame = CGRectMake(messageLeft, messageTop, messageWidth, messageHeight); 379 | [wrapperView addSubview:messageLabel]; 380 | } 381 | 382 | if(imageView != nil) { 383 | [wrapperView addSubview:imageView]; 384 | } 385 | 386 | return wrapperView; 387 | } 388 | 389 | @end 390 | -------------------------------------------------------------------------------- /ios/RCTToast/Toast.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | #import 4 | #import "Toast+UIView.h" 5 | 6 | 7 | @interface Toast : NSObject 8 | @end 9 | 10 | @implementation Toast 11 | 12 | RCT_EXPORT_MODULE(Toast) 13 | 14 | 15 | RCT_EXPORT_METHOD(show:(NSDictionary *)options) { 16 | NSString *message = [options objectForKey:@"message"]; 17 | NSString *duration = [options objectForKey:@"duration"]; 18 | NSString *position = [options objectForKey:@"position"]; 19 | NSNumber *addPixelsY = [options objectForKey:@"addPixelsY"]; 20 | 21 | if (![position isEqual: @"top"] && ![position isEqual: @"center"] && ![position isEqual: @"bottom"]) { 22 | RCTLogError(@"invalid position. valid options are 'top', 'center' and 'bottom'"); 23 | return; 24 | } 25 | 26 | NSInteger durationInt; 27 | if ([duration isEqual: @"short"]) { 28 | durationInt = 2; 29 | } else if ([duration isEqual: @"long"]) { 30 | durationInt = 5; 31 | } else { 32 | RCTLogError(@"invalid duration. valid options are 'short' and 'long'"); 33 | return; 34 | } 35 | 36 | dispatch_async(dispatch_get_main_queue(), ^{ 37 | [[[[UIApplication sharedApplication]windows]firstObject] makeToast:message duration:durationInt position:position addPixelsY:addPixelsY == nil ? 0 : [addPixelsY intValue]]; 38 | }); 39 | } 40 | 41 | RCT_EXPORT_METHOD(hide) { 42 | dispatch_async(dispatch_get_main_queue(), ^{ 43 | [[[[UIApplication sharedApplication]windows]firstObject] hideToast]; 44 | }); 45 | } 46 | 47 | @end 48 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@remobile/react-native-toast", 3 | "version": "1.0.7", 4 | "description": "A android like toast for react-native support for ios and android", 5 | "main": "index.js", 6 | "author": { 7 | "name": "YunJiang.Fang", 8 | "email": "42550564@qq.com" 9 | }, 10 | "license": "MIT", 11 | "keywords": [ 12 | "react-native", 13 | "react-component", 14 | "ios", 15 | "android", 16 | "toast", 17 | "position", 18 | "remobile", 19 | "mobile" 20 | ], 21 | "homepage": "https://github.com/remobile/react-native-toast", 22 | "bugs": { 23 | "url": "https://github.com/remobile/react-native-toast/issues" 24 | }, 25 | "repository": { 26 | "type": "git", 27 | "url": "git://github.com/remobile/react-native-toast.git" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /screencasts/ios.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/remobile/react-native-toast/bb1e9a36dcf5167cf017b10cd0148822588e7fe3/screencasts/ios.gif --------------------------------------------------------------------------------