├── .gitattributes ├── .gitignore ├── README.md ├── _config.yml ├── android ├── build.gradle └── src │ └── main │ ├── AndroidManifest.xml │ └── java │ └── com │ └── toast │ ├── RCTToastPackage.java │ └── Toast.java ├── index.js ├── ios ├── RNToastNative.m ├── RNToastNative.podspec ├── RNToastNative.xcodeproj │ └── project.pbxproj ├── UIView+Toast.h └── UIView+Toast.m ├── package.json └── test.gif /.gitattributes: -------------------------------------------------------------------------------- 1 | *.pbxproj -text -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # OSX 3 | # 4 | .DS_Store 5 | 6 | # node.js 7 | # 8 | node_modules/ 9 | npm-debug.log 10 | yarn-error.log 11 | 12 | 13 | # Xcode 14 | # 15 | build/ 16 | *.pbxuser 17 | !default.pbxuser 18 | *.mode1v3 19 | !default.mode1v3 20 | *.mode2v3 21 | !default.mode2v3 22 | *.perspectivev3 23 | !default.perspectivev3 24 | xcuserdata 25 | *.xccheckout 26 | *.moved-aside 27 | DerivedData 28 | *.hmap 29 | *.ipa 30 | *.xcuserstate 31 | project.xcworkspace 32 | 33 | 34 | # Android/IntelliJ 35 | # 36 | build/ 37 | .idea 38 | .gradle 39 | local.properties 40 | *.iml 41 | 42 | # BUCK 43 | buck-out/ 44 | \.buckd/ 45 | *.keystore 46 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![StackShare](https://img.shields.io/badge/tech-stack-0690fa.svg?style=flat)](https://stackshare.io/onemolegames/onemolegames) 2 | 3 | # react-native-toast-native 4 | React Native Toast is a toast component for both Android and iOS. it uses [scalessec/Toast](https://github.com/scalessec/Toast) for iOS; 5 | 6 | 7 | ## Demo: 8 | ![](test.gif) 9 | 10 | ## Getting started 11 | 12 | `$ npm install react-native-toast-native --save` 13 | 14 | ### Mostly automatic installation 15 | 16 | `$ react-native link react-native-toast-native` 17 | 18 | ### Manual installation 19 | 20 | 21 | #### iOS (CocoaPods) 22 | 23 | Add the following to your podfile: 24 | ``` 25 | pod 'RNToastNative', :path => '../node_modules/react-native-toast-native/ios` 26 | ``` 27 | 28 | #### iOS 29 | 30 | 1. In XCode, in the project navigator, right click `Libraries` ➜ `Add Files to [your project's name]` 31 | 2. Go to `node_modules` ➜ `react-native-toast-native` and add `RNToastNative.xcodeproj` 32 | 3. In XCode, in the project navigator, select your project. Add `libRNToastNative.a` to your project's `Build Phases` ➜ `Link Binary With Libraries` 33 | 4. Run your project (`Cmd+R`) 34 | 35 | #### Android 36 | 37 | 1. Open up `android/app/src/main/java/[...]/MainActivity.java` 38 | - Add `import com.toast.RCTToastPackage;` to the imports at the top of the file 39 | - Add `new RCTToastPackage()` to the list returned by the `getPackages()` method 40 | 2. Append the following lines to `android/settings.gradle`: 41 | ``` 42 | include ':react-native-toast-native' 43 | project(':react-native-toast-native').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-toast-native/android') 44 | ``` 45 | 3. Insert the following lines inside the dependencies block in `android/app/build.gradle`: 46 | ``` 47 | compile project(':react-native-toast-native') 48 | ``` 49 | 50 | ## Usage 51 | 52 | It's just the same as [ToastAndroid](http://facebook.github.io/react-native/docs/toastandroid.html) 53 | 54 | ```javascript 55 | import Toast from 'react-native-toast-native'; 56 | 57 | Toast.show(); // Default toast message is shown. 58 | Toast.show('This is a toast.'); // Specific message is shown with default duration("SHORT") and poistion("TOP") and styles. 59 | Toast.show('This is a long toast.',Toast.LONG); //Specific message and duration are shown with default position and styles. 60 | ``` 61 | It is higly customasible. Just provide a style object as a parameter. 62 | example: 63 | ```javascript 64 | import Toast from 'react-native-toast-native'; 65 | const styles={ 66 | width, 67 | height, 68 | backgroundColor, 69 | color, 70 | borderWidth, 71 | borderColor, 72 | borderRadius 73 | } 74 | 75 | Toast.show('This is a long toast.',Toast.SHORT,Toast.TOP,styles); 76 | // Customizable toast message is shown with specific message,duration and position. 77 | ``` 78 | 79 | 80 | ## Options 81 | You can style the toast with below options; 82 | Android: 83 | ```javascript 84 | { 85 | width, 86 | height, 87 | backgroundColor, 88 | color, 89 | borderWidth, 90 | paddingLeft, 91 | paddingRight, 92 | paddingBottom, 93 | paddingTop, 94 | fontSize, 95 | lineHeight, 96 | xOffset, 97 | yOffset, 98 | letterSpacing, 99 | fontWeight 100 | } 101 | ``` 102 | Ios: 103 | ```javascript 104 | { 105 | width, 106 | height, 107 | backgroundColor, 108 | color, 109 | borderWidth, 110 | borderColor, 111 | borderRadius 112 | } 113 | 114 | ``` 115 | 116 | 117 | if you want to make a customizable toast,you add an object like above to `show` and `showGravity` 118 | 119 | #### Example usage: 120 | 121 | ```javascript 122 | import Toast from 'react-native-toast-native'; 123 | import {Platform} from 'react-native'; 124 | const style={ 125 | backgroundColor: "#4ADDFB", 126 | width: 300, 127 | height: Platform.OS === ("ios") ? 50 : 100, 128 | color: "#ffffff", 129 | fontSize: 15, 130 | lineHeight: 2, 131 | lines: 4, 132 | borderRadius: 15, 133 | fontWeight: "bold", 134 | yOffset: 40 135 | }; 136 | Toast.show(message, Toast.SHORT, Toast.TOP,style); 137 | 138 | 139 | ``` 140 | 141 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-cayman -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | 2 | buildscript { 3 | repositories { 4 | jcenter() 5 | } 6 | 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:2.2.2' 9 | } 10 | } 11 | 12 | apply plugin: 'com.android.library' 13 | 14 | android { 15 | compileSdkVersion 23 16 | buildToolsVersion "23.0.1" 17 | 18 | defaultConfig { 19 | minSdkVersion 16 20 | targetSdkVersion 22 21 | versionCode 1 22 | versionName "1.0" 23 | } 24 | lintOptions { 25 | abortOnError false 26 | } 27 | } 28 | 29 | repositories { 30 | mavenCentral() 31 | } 32 | 33 | dependencies { 34 | compile 'com.facebook.react:react-native:+' 35 | } 36 | -------------------------------------------------------------------------------- /android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /android/src/main/java/com/toast/RCTToastPackage.java: -------------------------------------------------------------------------------- 1 | package com.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 | @Override 22 | public List createViewManagers(ReactApplicationContext reactContext) { 23 | return Arrays.asList(); 24 | } 25 | } -------------------------------------------------------------------------------- /android/src/main/java/com/toast/Toast.java: -------------------------------------------------------------------------------- 1 | package com.toast; 2 | 3 | import android.content.Context; 4 | import android.graphics.Color; 5 | import android.graphics.Typeface; 6 | import android.graphics.drawable.GradientDrawable; 7 | import android.text.Layout; 8 | import android.view.Gravity; 9 | import android.view.LayoutInflater; 10 | import android.view.View; 11 | import android.widget.LinearLayout; 12 | import android.widget.TextView; 13 | 14 | import com.facebook.common.logging.FLog; 15 | import com.facebook.react.bridge.*; 16 | 17 | import java.util.HashMap; 18 | import java.util.Map; 19 | import java.util.Objects; 20 | 21 | public class Toast extends ReactContextBaseJavaModule implements LifecycleEventListener { 22 | private static final String DURATION_SHORT_KEY = "SHORT"; 23 | private static final String DURATION_LONG_KEY = "LONG"; 24 | 25 | private static final String POSITION_TOP_KEY = "TOP"; 26 | private static final String POSITION_CENTER_KEY = "CENTER"; 27 | private static final String POSITION_BOTTOM_KEY = "BOTTOM"; 28 | 29 | private android.widget.Toast mostRecentToast; 30 | 31 | // note that webView.isPaused() is not Xwalk compatible, so tracking it poor-man style 32 | private boolean isPaused; 33 | 34 | public Toast(ReactApplicationContext reactContext) { 35 | super(reactContext); 36 | } 37 | 38 | @Override 39 | public String getName() { 40 | return "RCTToast"; 41 | } 42 | 43 | @ReactMethod 44 | public void show(final String message, final int duration, final int position, ReadableMap styles) throws Exception { 45 | 46 | if (this.isPaused) { 47 | return; 48 | } 49 | final String backgroundColor = styles.hasKey("backgroundColor") ? styles.getString("backgroundColor") : "#000000"; 50 | final String color = styles.hasKey("color") ? styles.getString("color") : "#ffffff"; 51 | final int width = styles.hasKey("width") ? styles.getInt("width") : 100; 52 | final int height = styles.hasKey("height") ? styles.getInt("height") : 200; 53 | final int paddingLeft = styles.hasKey("paddingLeft") ? styles.getInt("paddingLeft") : 0; 54 | final int paddingRight = styles.hasKey("paddingRight") ? styles.getInt("paddingRight") : 0; 55 | final int paddingTop = styles.hasKey("paddingTop") ? styles.getInt("paddingTop") : 0; 56 | final int paddingBottom = styles.hasKey("paddingBottom") ? styles.getInt("paddingTop") : 0; 57 | final int fontSize = styles.hasKey("fontSize") ? styles.getInt("fontSize") : 12; 58 | final int lineHeight = styles.hasKey("lineHeight") ? styles.getInt("lineHeight") : 10; 59 | final int cornerRadius = styles.hasKey("borderRadius") ? styles.getInt("borderRadius") : 5; 60 | final int lines = styles.hasKey("lines") ? styles.getInt("lines") : 3; 61 | final int borderWidth = styles.hasKey("borderWidth") ? styles.getInt("borderWidth") : 2; 62 | final int xOffset = styles.hasKey("xOffset") ? styles.getInt("xOffset") : 0; 63 | final int yOffset = styles.hasKey("yOffset") ? styles.getInt("yOffset") : 0; 64 | final float letterSpacing = styles.hasKey("letterSpacing") ? (float) styles.getDouble("letterSpacing") : 0; 65 | final String fontWeight = styles.hasKey("fontWeight") ? styles.getString("fontWeight") : "default"; 66 | UiThreadUtil.runOnUiThread(new Runnable() { 67 | public void run() { 68 | android.widget.Toast toast = android.widget.Toast.makeText( 69 | getReactApplicationContext(), 70 | message, 71 | duration); 72 | View view = toast.getView(); 73 | TextView text = (TextView) view.findViewById(android.R.id.message); 74 | GradientDrawable gd = new GradientDrawable(); 75 | gd.setStroke(borderWidth, Color.parseColor(backgroundColor)); 76 | gd.setColor(Color.parseColor(backgroundColor)); 77 | gd.setCornerRadius(cornerRadius); 78 | gd.setSize(width, height); 79 | view.setBackground(gd); 80 | try{ 81 | text.setTextColor(Color.parseColor(color)); 82 | text.setTextSize(fontSize); 83 | text.setLines(lines); 84 | text.setMaxLines(lines); 85 | text.setHeight(lineHeight); 86 | text.setPadding(paddingLeft, paddingTop, paddingRight, paddingBottom); 87 | text.setTypeface(Typeface.SANS_SERIF); 88 | if(fontWeight.equals("bold")){ 89 | text.setTypeface(Typeface.DEFAULT_BOLD); 90 | }else{ 91 | text.setTypeface(Typeface.DEFAULT); 92 | } 93 | text.setLetterSpacing(letterSpacing); 94 | } catch (NoSuchMethodError e) { 95 | // ignore 96 | } 97 | toast.setView(view); 98 | toast.setGravity(position, xOffset, yOffset); 99 | toast.show(); 100 | mostRecentToast = toast; 101 | } 102 | }); 103 | } 104 | 105 | @ReactMethod 106 | public void hide() throws Exception { 107 | if (mostRecentToast != null) { 108 | mostRecentToast.cancel(); 109 | } 110 | } 111 | 112 | @Override 113 | public Map getConstants() { 114 | final Map constants = new HashMap<>(); 115 | constants.put(DURATION_SHORT_KEY, android.widget.Toast.LENGTH_SHORT); 116 | constants.put(DURATION_LONG_KEY, android.widget.Toast.LENGTH_LONG); 117 | constants.put(POSITION_TOP_KEY, Gravity.TOP); 118 | constants.put(POSITION_CENTER_KEY, Gravity.CENTER); 119 | constants.put(POSITION_BOTTOM_KEY, Gravity.BOTTOM); 120 | return constants; 121 | } 122 | 123 | @Override 124 | public void initialize() { 125 | getReactApplicationContext().addLifecycleEventListener(this); 126 | } 127 | 128 | @Override 129 | public void onHostPause() { 130 | if (mostRecentToast != null) { 131 | mostRecentToast.cancel(); 132 | } 133 | this.isPaused = true; 134 | } 135 | 136 | @Override 137 | public void onHostResume() { 138 | this.isPaused = false; 139 | } 140 | 141 | @Override 142 | public void onHostDestroy() { 143 | this.isPaused = true; 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | import {NativeModules, ToastAndroid, Platform} from "react-native"; 2 | 3 | var RCTToastNative = Platform.OS === 'android' ? NativeModules.Toast : NativeModules.RNToastNative; 4 | 5 | var ToastNative = { 6 | //Toast duration constants 7 | SHORT: RCTToastNative.SHORT, 8 | LONG: RCTToastNative.LONG, 9 | 10 | // Toast gravity constants 11 | TOP: RCTToastNative.TOP, 12 | BOTTOM: RCTToastNative.BOTTOM, 13 | CENTER: RCTToastNative.CENTER, 14 | 15 | show: function (message, 16 | duration, 17 | position, 18 | styles) { 19 | RCTToastNative.show(message || "This is a toast message", duration || ToastNative.SHORT, position || ToastNative.TOP, styles || {}); 20 | } 21 | }; 22 | 23 | export default ToastNative; 24 | -------------------------------------------------------------------------------- /ios/RNToastNative.m: -------------------------------------------------------------------------------- 1 | // 2 | // RNToastNative 3 | // 4 | #import 5 | #import "RCTBridgeModule.h" 6 | #import "UIView+Toast.h" 7 | 8 | NSInteger const RNToastNativeBottomOffset = 40; 9 | double const RNToastNativeShortDuration = 3.0; 10 | double const RNToastNativeLongDuration = 5.0; 11 | NSInteger const RNToastNativeGravityBottom = 1; 12 | NSInteger const RNToastNativeGravityCenter = 2; 13 | NSInteger const RNToastNativeGravityTop = 3; 14 | 15 | @interface RNToastNative : NSObject 16 | @end 17 | 18 | @implementation RNToastNative { 19 | CGFloat _keyOffset; 20 | } 21 | 22 | - (instancetype)init { 23 | if (self = [super init]) { 24 | _keyOffset = 0; 25 | [[NSNotificationCenter defaultCenter] addObserver:self 26 | selector:@selector(keyboardWasShown:) 27 | name:UIKeyboardDidShowNotification 28 | object:nil]; 29 | [[NSNotificationCenter defaultCenter] addObserver:self 30 | selector:@selector(keyboardWillHiden:) 31 | name:UIKeyboardWillHideNotification 32 | object:nil]; 33 | } 34 | return self; 35 | } 36 | 37 | - (void)keyboardWasShown:(NSNotification *)notification { 38 | 39 | CGSize keyboardSize = [[[notification userInfo] objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size; 40 | 41 | int height = MIN(keyboardSize.height,keyboardSize.width); 42 | int width = MAX(keyboardSize.height,keyboardSize.width); 43 | 44 | _keyOffset = height; 45 | } 46 | 47 | - (void)keyboardWillHiden:(NSNotification *)notification { 48 | _keyOffset = 0; 49 | } 50 | 51 | 52 | RCT_EXPORT_MODULE() 53 | 54 | + (BOOL)requiresMainQueueSetup { 55 | return NO; 56 | } 57 | 58 | - (NSDictionary *)constantsToExport { 59 | return @{ 60 | @"SHORT": [NSNumber numberWithDouble:RNToastNativeShortDuration], 61 | @"LONG": [NSNumber numberWithDouble:RNToastNativeLongDuration], 62 | @"BOTTOM": [NSNumber numberWithInteger:RNToastNativeGravityBottom], 63 | @"CENTER": [NSNumber numberWithInteger:RNToastNativeGravityCenter], 64 | @"TOP": [NSNumber numberWithInteger:RNToastNativeGravityTop] 65 | }; 66 | } 67 | 68 | RCT_EXPORT_METHOD(show:(NSString *)msg duration:(double)duration gravity:(nonnull NSNumber *)gravity customStyle:(NSDictionary *)customStyle { 69 | [self _show:msg duration:duration gravity:gravity.intValue customStyle:customStyle]; 70 | }); 71 | 72 | RCT_EXPORT_METHOD(showWithGravity:(NSString *)msg duration:(double)duration gravity:(nonnull NSNumber *)gravity customStyle:(NSDictionary *)customStyle { 73 | [self _show:msg duration:duration gravity:gravity.intValue customStyle:customStyle]; 74 | }); 75 | 76 | - (UIViewController *)visibleViewController:(UIViewController *)rootViewController 77 | { 78 | if (rootViewController.presentedViewController == nil) 79 | { 80 | return rootViewController; 81 | } 82 | if ([rootViewController.presentedViewController isKindOfClass:[UINavigationController class]]) 83 | { 84 | UINavigationController *navigationController = (UINavigationController *)rootViewController.presentedViewController; 85 | UIViewController *lastViewController = [[navigationController viewControllers] lastObject]; 86 | 87 | return [self visibleViewController:lastViewController]; 88 | } 89 | if ([rootViewController.presentedViewController isKindOfClass:[UITabBarController class]]) 90 | { 91 | UITabBarController *tabBarController = (UITabBarController *)rootViewController.presentedViewController; 92 | UIViewController *selectedViewController = tabBarController.selectedViewController; 93 | 94 | return [self visibleViewController:selectedViewController]; 95 | } 96 | 97 | UIViewController *presentedViewController = (UIViewController *)rootViewController.presentedViewController; 98 | 99 | return [self visibleViewController:presentedViewController]; 100 | } 101 | 102 | - (void)_show:(NSString *)msg duration:(NSTimeInterval)duration gravity:(NSInteger)gravity customStyle:(NSDictionary *)customStyle { 103 | dispatch_async(dispatch_get_main_queue(), ^{ 104 | //UIView *root = [[[[[UIApplication sharedApplication] delegate] window] rootViewController] view]; 105 | UIViewController *ctrl = [self visibleViewController:[UIApplication sharedApplication].keyWindow.rootViewController]; 106 | UIView *root = [ctrl view]; 107 | CGRect bound = root.bounds; 108 | bound.size.height -= _keyOffset; 109 | if (bound.size.height > RNToastNativeBottomOffset*2) { 110 | bound.origin.y += RNToastNativeBottomOffset; 111 | bound.size.height -= RNToastNativeBottomOffset*2; 112 | } 113 | UIView *view = [[UIView alloc] initWithFrame:bound]; 114 | view.userInteractionEnabled = NO; 115 | [root addSubview:view]; 116 | UIView __weak *blockView = view; 117 | id position; 118 | if (gravity == RNToastNativeGravityTop) { 119 | position = CSToastPositionTop; 120 | } else if (gravity == RNToastNativeGravityCenter) { 121 | position = CSToastPositionCenter; 122 | } else { 123 | position = CSToastPositionBottom; 124 | } 125 | [view makeToast:msg 126 | duration:duration 127 | position:position 128 | customStyle:customStyle 129 | title:nil 130 | image:nil 131 | style:nil 132 | completion:^(BOOL didTap) { 133 | [blockView removeFromSuperview]; 134 | }]; 135 | }); 136 | } 137 | 138 | @end 139 | 140 | -------------------------------------------------------------------------------- /ios/RNToastNative.podspec: -------------------------------------------------------------------------------- 1 | 2 | Pod::Spec.new do |s| 3 | s.name = "RNToastNative" 4 | s.version = "1.0.0" 5 | s.summary = "RNToastNative" 6 | s.description = <<-DESC 7 | RNToastNative 8 | DESC 9 | s.homepage = "https://github.com/onemolegames/react-native-toast-native" 10 | s.license = "MIT" 11 | # s.license = { :type => "MIT", :file => "FILE_LICENSE" } 12 | s.author = { "author" => "author@domain.cn" } 13 | s.platform = :ios, "7.0" 14 | s.source = { :git => "https://github.com/author/RNToastNative.git", :tag => "master" } 15 | s.source_files = "*.{h,m}" 16 | s.requires_arc = true 17 | 18 | 19 | s.dependency "React" 20 | #s.dependency "others" 21 | 22 | end 23 | 24 | 25 | -------------------------------------------------------------------------------- /ios/RNToastNative.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | B3E7B58A1CC2AC0600A0062D /* RNToastNative.m in Sources */ = {isa = PBXBuildFile; fileRef = B3E7B5891CC2AC0600A0062D /* RNToastNative.m */; }; 11 | C101C4841EF901E60051ADA6 /* UIView+Toast.m in Sources */ = {isa = PBXBuildFile; fileRef = C101C4831EF901E60051ADA6 /* UIView+Toast.m */; }; 12 | /* End PBXBuildFile section */ 13 | 14 | /* Begin PBXCopyFilesBuildPhase section */ 15 | 58B511D91A9E6C8500147676 /* 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 | 134814201AA4EA6300B7C361 /* libRNToastNative.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRNToastNative.a; sourceTree = BUILT_PRODUCTS_DIR; }; 28 | B3E7B5891CC2AC0600A0062D /* RNToastNative.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNToastNative.m; sourceTree = ""; }; 29 | C101C4821EF901E60051ADA6 /* UIView+Toast.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIView+Toast.h"; sourceTree = ""; }; 30 | C101C4831EF901E60051ADA6 /* UIView+Toast.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIView+Toast.m"; sourceTree = ""; }; 31 | /* End PBXFileReference section */ 32 | 33 | /* Begin PBXFrameworksBuildPhase section */ 34 | 58B511D81A9E6C8500147676 /* Frameworks */ = { 35 | isa = PBXFrameworksBuildPhase; 36 | buildActionMask = 2147483647; 37 | files = ( 38 | ); 39 | runOnlyForDeploymentPostprocessing = 0; 40 | }; 41 | /* End PBXFrameworksBuildPhase section */ 42 | 43 | /* Begin PBXGroup section */ 44 | 134814211AA4EA7D00B7C361 /* Products */ = { 45 | isa = PBXGroup; 46 | children = ( 47 | 134814201AA4EA6300B7C361 /* libRNToastNative.a */, 48 | ); 49 | name = Products; 50 | sourceTree = ""; 51 | }; 52 | 58B511D21A9E6C8500147676 = { 53 | isa = PBXGroup; 54 | children = ( 55 | C101C4821EF901E60051ADA6 /* UIView+Toast.h */, 56 | C101C4831EF901E60051ADA6 /* UIView+Toast.m */, 57 | B3E7B5891CC2AC0600A0062D /* RNToastNative.m */, 58 | 134814211AA4EA7D00B7C361 /* Products */, 59 | ); 60 | sourceTree = ""; 61 | }; 62 | /* End PBXGroup section */ 63 | 64 | /* Begin PBXNativeTarget section */ 65 | 58B511DA1A9E6C8500147676 /* RNToastNative */ = { 66 | isa = PBXNativeTarget; 67 | buildConfigurationList = 58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "RNToastNative" */; 68 | buildPhases = ( 69 | 58B511D71A9E6C8500147676 /* Sources */, 70 | 58B511D81A9E6C8500147676 /* Frameworks */, 71 | 58B511D91A9E6C8500147676 /* CopyFiles */, 72 | ); 73 | buildRules = ( 74 | ); 75 | dependencies = ( 76 | ); 77 | name = RNToastNative; 78 | productName = RCTDataManager; 79 | productReference = 134814201AA4EA6300B7C361 /* libRNToastNative.a */; 80 | productType = "com.apple.product-type.library.static"; 81 | }; 82 | /* End PBXNativeTarget section */ 83 | 84 | /* Begin PBXProject section */ 85 | 58B511D31A9E6C8500147676 /* Project object */ = { 86 | isa = PBXProject; 87 | attributes = { 88 | LastUpgradeCheck = 0610; 89 | ORGANIZATIONNAME = Facebook; 90 | TargetAttributes = { 91 | 58B511DA1A9E6C8500147676 = { 92 | CreatedOnToolsVersion = 6.1.1; 93 | }; 94 | }; 95 | }; 96 | buildConfigurationList = 58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "RNToastNative" */; 97 | compatibilityVersion = "Xcode 3.2"; 98 | developmentRegion = English; 99 | hasScannedForEncodings = 0; 100 | knownRegions = ( 101 | en, 102 | ); 103 | mainGroup = 58B511D21A9E6C8500147676; 104 | productRefGroup = 58B511D21A9E6C8500147676; 105 | projectDirPath = ""; 106 | projectRoot = ""; 107 | targets = ( 108 | 58B511DA1A9E6C8500147676 /* RNToastNative */, 109 | ); 110 | }; 111 | /* End PBXProject section */ 112 | 113 | /* Begin PBXSourcesBuildPhase section */ 114 | 58B511D71A9E6C8500147676 /* Sources */ = { 115 | isa = PBXSourcesBuildPhase; 116 | buildActionMask = 2147483647; 117 | files = ( 118 | C101C4841EF901E60051ADA6 /* UIView+Toast.m in Sources */, 119 | B3E7B58A1CC2AC0600A0062D /* RNToastNative.m in Sources */, 120 | ); 121 | runOnlyForDeploymentPostprocessing = 0; 122 | }; 123 | /* End PBXSourcesBuildPhase section */ 124 | 125 | /* Begin XCBuildConfiguration section */ 126 | 58B511ED1A9E6C8500147676 /* Debug */ = { 127 | isa = XCBuildConfiguration; 128 | buildSettings = { 129 | ALWAYS_SEARCH_USER_PATHS = NO; 130 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 131 | CLANG_CXX_LIBRARY = "libc++"; 132 | CLANG_ENABLE_MODULES = YES; 133 | CLANG_ENABLE_OBJC_ARC = YES; 134 | CLANG_WARN_BOOL_CONVERSION = YES; 135 | CLANG_WARN_CONSTANT_CONVERSION = YES; 136 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 137 | CLANG_WARN_EMPTY_BODY = YES; 138 | CLANG_WARN_ENUM_CONVERSION = YES; 139 | CLANG_WARN_INT_CONVERSION = YES; 140 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 141 | CLANG_WARN_UNREACHABLE_CODE = YES; 142 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 143 | COPY_PHASE_STRIP = NO; 144 | ENABLE_STRICT_OBJC_MSGSEND = YES; 145 | GCC_C_LANGUAGE_STANDARD = gnu99; 146 | GCC_DYNAMIC_NO_PIC = NO; 147 | GCC_OPTIMIZATION_LEVEL = 0; 148 | GCC_PREPROCESSOR_DEFINITIONS = ( 149 | "DEBUG=1", 150 | "$(inherited)", 151 | ); 152 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 153 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 154 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 155 | GCC_WARN_UNDECLARED_SELECTOR = YES; 156 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 157 | GCC_WARN_UNUSED_FUNCTION = YES; 158 | GCC_WARN_UNUSED_VARIABLE = YES; 159 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 160 | MTL_ENABLE_DEBUG_INFO = YES; 161 | ONLY_ACTIVE_ARCH = YES; 162 | SDKROOT = iphoneos; 163 | }; 164 | name = Debug; 165 | }; 166 | 58B511EE1A9E6C8500147676 /* Release */ = { 167 | isa = XCBuildConfiguration; 168 | buildSettings = { 169 | ALWAYS_SEARCH_USER_PATHS = NO; 170 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 171 | CLANG_CXX_LIBRARY = "libc++"; 172 | CLANG_ENABLE_MODULES = YES; 173 | CLANG_ENABLE_OBJC_ARC = YES; 174 | CLANG_WARN_BOOL_CONVERSION = YES; 175 | CLANG_WARN_CONSTANT_CONVERSION = YES; 176 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 177 | CLANG_WARN_EMPTY_BODY = YES; 178 | CLANG_WARN_ENUM_CONVERSION = YES; 179 | CLANG_WARN_INT_CONVERSION = YES; 180 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 181 | CLANG_WARN_UNREACHABLE_CODE = YES; 182 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 183 | COPY_PHASE_STRIP = YES; 184 | ENABLE_NS_ASSERTIONS = NO; 185 | ENABLE_STRICT_OBJC_MSGSEND = YES; 186 | GCC_C_LANGUAGE_STANDARD = gnu99; 187 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 188 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 189 | GCC_WARN_UNDECLARED_SELECTOR = YES; 190 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 191 | GCC_WARN_UNUSED_FUNCTION = YES; 192 | GCC_WARN_UNUSED_VARIABLE = YES; 193 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 194 | MTL_ENABLE_DEBUG_INFO = NO; 195 | SDKROOT = iphoneos; 196 | VALIDATE_PRODUCT = YES; 197 | }; 198 | name = Release; 199 | }; 200 | 58B511F01A9E6C8500147676 /* Debug */ = { 201 | isa = XCBuildConfiguration; 202 | buildSettings = { 203 | HEADER_SEARCH_PATHS = ( 204 | "$(inherited)", 205 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, 206 | "$(SRCROOT)/../../../React/**", 207 | "$(SRCROOT)/../../react-native/React/**", 208 | ); 209 | LIBRARY_SEARCH_PATHS = "$(inherited)"; 210 | OTHER_LDFLAGS = "-ObjC"; 211 | PRODUCT_NAME = RNToastNative; 212 | SKIP_INSTALL = YES; 213 | }; 214 | name = Debug; 215 | }; 216 | 58B511F11A9E6C8500147676 /* Release */ = { 217 | isa = XCBuildConfiguration; 218 | buildSettings = { 219 | HEADER_SEARCH_PATHS = ( 220 | "$(inherited)", 221 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, 222 | "$(SRCROOT)/../../../React/**", 223 | "$(SRCROOT)/../../react-native/React/**", 224 | ); 225 | LIBRARY_SEARCH_PATHS = "$(inherited)"; 226 | OTHER_LDFLAGS = "-ObjC"; 227 | PRODUCT_NAME = RNToastNative; 228 | SKIP_INSTALL = YES; 229 | }; 230 | name = Release; 231 | }; 232 | /* End XCBuildConfiguration section */ 233 | 234 | /* Begin XCConfigurationList section */ 235 | 58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "RNToastNative" */ = { 236 | isa = XCConfigurationList; 237 | buildConfigurations = ( 238 | 58B511ED1A9E6C8500147676 /* Debug */, 239 | 58B511EE1A9E6C8500147676 /* Release */, 240 | ); 241 | defaultConfigurationIsVisible = 0; 242 | defaultConfigurationName = Release; 243 | }; 244 | 58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "RNToastNative" */ = { 245 | isa = XCConfigurationList; 246 | buildConfigurations = ( 247 | 58B511F01A9E6C8500147676 /* Debug */, 248 | 58B511F11A9E6C8500147676 /* Release */, 249 | ); 250 | defaultConfigurationIsVisible = 0; 251 | defaultConfigurationName = Release; 252 | }; 253 | /* End XCConfigurationList section */ 254 | }; 255 | rootObject = 58B511D31A9E6C8500147676 /* Project object */; 256 | } 257 | -------------------------------------------------------------------------------- /ios/UIView+Toast.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+Toast.h 3 | // Toast 4 | // 5 | // Copyright (c) 2011-2015 Charles Scalesse. 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a 8 | // copy of this software and associated documentation files (the 9 | // "Software"), to deal in the Software without restriction, including 10 | // without limitation the rights to use, copy, modify, merge, publish, 11 | // distribute, sublicense, and/or sell copies of the Software, and to 12 | // permit persons to whom the Software is furnished to do so, subject to 13 | // the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included 16 | // in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 19 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | // CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 23 | // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 24 | // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | 26 | #import 27 | 28 | extern const NSString * CSToastPositionTop; 29 | extern const NSString * CSToastPositionCenter; 30 | extern const NSString * CSToastPositionBottom; 31 | 32 | @class CSToastStyle; 33 | 34 | /** 35 | Toast is an Objective-C category that adds toast notifications to the UIView 36 | object class. It is intended to be simple, lightweight, and easy to use. Most 37 | toast notifications can be triggered with a single line of code. 38 | 39 | The `makeToast:` methods create a new view and then display it as toast. 40 | 41 | The `showToast:` methods display any view as toast. 42 | 43 | */ 44 | @interface UIView (Toast) 45 | 46 | /** 47 | Creates and presents a new toast view with a message and displays it with the 48 | default duration and position. Styled using the shared style. 49 | 50 | @param message The message to be displayed 51 | */ 52 | - (void)makeToast:(NSString *)message; 53 | 54 | /** 55 | Creates and presents a new toast view with a message. Duration and position 56 | can be set explicitly. Styled using the shared style. 57 | 58 | @param message The message to be displayed 59 | @param duration The toast duration 60 | @param position The toast's center point. Can be one of the predefined CSToastPosition 61 | constants or a `CGPoint` wrapped in an `NSValue` object. 62 | @param customStyle The toast customizable style 63 | */ 64 | - (void)makeToast:(NSString *)message 65 | duration:(NSTimeInterval)duration 66 | position:(id)position 67 | customStyle:(NSDictionary *)customStyle; 68 | 69 | /** 70 | Creates and presents a new toast view with a message. Duration, position, and 71 | style can be set explicitly. 72 | 73 | @param message The message to be displayed 74 | @param duration The toast duration 75 | @param position The toast's center point. Can be one of the predefined CSToastPosition 76 | constants or a `CGPoint` wrapped in an `NSValue` object. 77 | @param style The style. The shared style will be used when nil 78 | @param customStyle The style.The toast customizable style when not nil 79 | */ 80 | - (void)makeToast:(NSString *)message 81 | duration:(NSTimeInterval)duration 82 | position:(id)position 83 | customStyle:(NSDictionary *)customStyle 84 | style:(CSToastStyle *)style; 85 | 86 | /** 87 | Creates and presents a new toast view with a message, title, and image. Duration, 88 | position, and style can be set explicitly. The completion block executes when the 89 | toast view completes. `didTap` will be `YES` if the toast view was dismissed from 90 | a tap. 91 | 92 | @param message The message to be displayed 93 | @param duration The toast duration 94 | @param position The toast's center point. Can be one of the predefined CSToastPosition 95 | constants or a `CGPoint` wrapped in an `NSValue` object. 96 | @param title The title 97 | @param image The image 98 | @param style The style. The shared style will be used when nil 99 | @param customStyle The style.The toast customizable style when not nil 100 | @param completion The completion block, executed after the toast view disappears. 101 | didTap will be `YES` if the toast view was dismissed from a tap. 102 | */ 103 | - (void)makeToast:(NSString *)message 104 | duration:(NSTimeInterval)duration 105 | position:(id)position 106 | customStyle:(NSDictionary *)customStyle 107 | title:(NSString *)title 108 | image:(UIImage *)image 109 | style:(CSToastStyle *)style 110 | completion:(void(^)(BOOL didTap))completion; 111 | 112 | /** 113 | Creates a new toast view with any combination of message, title, and image. 114 | The look and feel is configured via the style. Unlike the `makeToast:` methods, 115 | this method does not present the toast view automatically. One of the showToast: 116 | methods must be used to present the resulting view. 117 | 118 | @warning if message, title, and image are all nil, this method will return nil. 119 | 120 | @param message The message to be displayed 121 | @param title The title 122 | @param image The image 123 | @param style The style. The shared style will be used when nil 124 | @param customStyle The style.The toast customizable style when not nil 125 | @return The newly created toast view 126 | */ 127 | - (UIView *)toastViewForMessage:(NSString *)message 128 | customStyle:(NSDictionary *)customStyle 129 | title:(NSString *)title 130 | image:(UIImage *)image 131 | style:(CSToastStyle *)style; 132 | 133 | /** 134 | Creates and displays a new toast activity indicator view at a specified position. 135 | 136 | @warning Only one toast activity indicator view can be presented per superview. Subsequent 137 | calls to `makeToastActivity:` will be ignored until hideToastActivity is called. 138 | 139 | @warning `makeToastActivity:` works independently of the showToast: methods. Toast activity 140 | views can be presented and dismissed while toast views are being displayed. `makeToastActivity:` 141 | has no effect on the queueing behavior of the showToast: methods. 142 | 143 | @param position The toast's center point. Can be one of the predefined CSToastPosition 144 | constants or a `CGPoint` wrapped in an `NSValue` object. 145 | */ 146 | - (void)makeToastActivity:(id)position; 147 | 148 | /** 149 | Dismisses the active toast activity indicator view. 150 | */ 151 | - (void)hideToastActivity; 152 | 153 | /** 154 | Displays any view as toast using the default duration and position. 155 | 156 | @param toast The view to be displayed as toast 157 | */ 158 | - (void)showToast:(UIView *)toast; 159 | 160 | /** 161 | Displays any view as toast at a provided position and duration. The completion block 162 | executes when the toast view completes. `didTap` will be `YES` if the toast view was 163 | dismissed from a tap. 164 | 165 | @param toast The view to be displayed as toast 166 | @param duration The notification duration 167 | @param position The toast's center point. Can be one of the predefined CSToastPosition 168 | constants or a `CGPoint` wrapped in an `NSValue` object. 169 | @param completion The completion block, executed after the toast view disappears. 170 | didTap will be `YES` if the toast view was dismissed from a tap. 171 | @param customStyle The toast customizable style. 172 | */ 173 | - (void)showToast:(UIView *)toast 174 | duration:(NSTimeInterval)duration 175 | position:(id)position 176 | customStyle:(NSDictionary *)customStyle 177 | completion:(void(^)(BOOL didTap))completion; 178 | 179 | @end 180 | 181 | /** 182 | `CSToastStyle` instances define the look and feel for toast views created via the 183 | `makeToast:` methods as well for toast views created directly with 184 | `toastViewForMessage:title:image:style:`. 185 | 186 | @warning `CSToastStyle` offers relatively simple styling options for the default 187 | toast view. If you require a toast view with more complex UI, it probably makes more 188 | sense to create your own custom UIView subclass and present it with the `showToast:` 189 | methods. 190 | */ 191 | @interface CSToastStyle : NSObject 192 | 193 | /** 194 | The background color. Default is `[UIColor blackColor]` at 80% opacity. 195 | */ 196 | @property (strong, nonatomic) UIColor *backgroundColor; 197 | 198 | /** 199 | The title color. Default is `[UIColor whiteColor]`. 200 | */ 201 | @property (strong, nonatomic) UIColor *titleColor; 202 | 203 | /** 204 | The message color. Default is `[UIColor whiteColor]`. 205 | */ 206 | @property (strong, nonatomic) UIColor *messageColor; 207 | 208 | /** 209 | A percentage value from 0.0 to 1.0, representing the maximum width of the toast 210 | view relative to it's superview. Default is 0.8 (80% of the superview's width). 211 | */ 212 | @property (assign, nonatomic) CGFloat maxWidthPercentage; 213 | 214 | /** 215 | A percentage value from 0.0 to 1.0, representing the maximum height of the toast 216 | view relative to it's superview. Default is 0.8 (80% of the superview's height). 217 | */ 218 | @property (assign, nonatomic) CGFloat maxHeightPercentage; 219 | 220 | /** 221 | The spacing from the horizontal edge of the toast view to the content. When an image 222 | is present, this is also used as the padding between the image and the text. 223 | Default is 10.0. 224 | */ 225 | @property (assign, nonatomic) CGFloat horizontalPadding; 226 | 227 | /** 228 | The spacing from the vertical edge of the toast view to the content. When a title 229 | is present, this is also used as the padding between the title and the message. 230 | Default is 10.0. 231 | */ 232 | @property (assign, nonatomic) CGFloat verticalPadding; 233 | 234 | /** 235 | The corner radius. Default is 10.0. 236 | */ 237 | @property (assign, nonatomic) CGFloat cornerRadius; 238 | 239 | /** 240 | The title font. Default is `[UIFont boldSystemFontOfSize:16.0]`. 241 | */ 242 | @property (strong, nonatomic) UIFont *titleFont; 243 | 244 | /** 245 | The message font. Default is `[UIFont systemFontOfSize:16.0]`. 246 | */ 247 | @property (strong, nonatomic) UIFont *messageFont; 248 | 249 | /** 250 | The title text alignment. Default is `NSTextAlignmentLeft`. 251 | */ 252 | @property (assign, nonatomic) NSTextAlignment titleAlignment; 253 | 254 | /** 255 | The message text alignment. Default is `NSTextAlignmentLeft`. 256 | */ 257 | @property (assign, nonatomic) NSTextAlignment messageAlignment; 258 | 259 | /** 260 | The maximum number of lines for the title. The default is 0 (no limit). 261 | */ 262 | @property (assign, nonatomic) NSInteger titleNumberOfLines; 263 | 264 | /** 265 | The maximum number of lines for the message. The default is 0 (no limit). 266 | */ 267 | @property (assign, nonatomic) NSInteger messageNumberOfLines; 268 | 269 | /** 270 | Enable or disable a shadow on the toast view. Default is `NO`. 271 | */ 272 | @property (assign, nonatomic) BOOL displayShadow; 273 | 274 | /** 275 | The shadow color. Default is `[UIColor blackColor]`. 276 | */ 277 | @property (strong, nonatomic) UIColor *shadowColor; 278 | 279 | /** 280 | A value from 0.0 to 1.0, representing the opacity of the shadow. 281 | Default is 0.8 (80% opacity). 282 | */ 283 | @property (assign, nonatomic) CGFloat shadowOpacity; 284 | 285 | /** 286 | The shadow radius. Default is 6.0. 287 | */ 288 | @property (assign, nonatomic) CGFloat shadowRadius; 289 | 290 | /** 291 | The shadow offset. The default is `CGSizeMake(4.0, 4.0)`. 292 | */ 293 | @property (assign, nonatomic) CGSize shadowOffset; 294 | 295 | /** 296 | The image size. The default is `CGSizeMake(80.0, 80.0)`. 297 | */ 298 | @property (assign, nonatomic) CGSize imageSize; 299 | 300 | /** 301 | The size of the toast activity view when `makeToastActivity:` is called. 302 | Default is `CGSizeMake(100.0, 100.0)`. 303 | */ 304 | @property (assign, nonatomic) CGSize activitySize; 305 | 306 | /** 307 | The fade in/out animation duration. Default is 0.2. 308 | */ 309 | @property (assign, nonatomic) NSTimeInterval fadeDuration; 310 | 311 | /** 312 | Creates a new instance of `CSToastStyle` with all the default values set. 313 | */ 314 | - (instancetype)initWithDefaultStyle NS_DESIGNATED_INITIALIZER; 315 | 316 | /** 317 | @warning Only the designated initializer should be used to create 318 | an instance of `CSToastStyle`. 319 | */ 320 | - (instancetype)init NS_UNAVAILABLE; 321 | 322 | @end 323 | 324 | /** 325 | `CSToastManager` provides general configuration options for all toast 326 | notifications. Backed by a singleton instance. 327 | */ 328 | @interface CSToastManager : NSObject 329 | 330 | /** 331 | Sets the shared style on the singleton. The shared style is used whenever 332 | a `makeToast:` method (or `toastViewForMessage:title:image:style:`) is called 333 | with with a nil style. By default, this is set to `CSToastStyle`'s default 334 | style. 335 | 336 | @param sharedStyle 337 | */ 338 | + (void)setSharedStyle:(CSToastStyle *)sharedStyle; 339 | 340 | /** 341 | Gets the shared style from the singlton. By default, this is 342 | `CSToastStyle`'s default style. 343 | 344 | @return the shared style 345 | */ 346 | + (CSToastStyle *)sharedStyle; 347 | 348 | /** 349 | Enables or disables tap to dismiss on toast views. Default is `YES`. 350 | 351 | @param allowTapToDismiss 352 | */ 353 | + (void)setTapToDismissEnabled:(BOOL)tapToDismissEnabled; 354 | 355 | /** 356 | Returns `YES` if tap to dismiss is enabled, otherwise `NO`. 357 | Default is `YES`. 358 | 359 | @return BOOL 360 | */ 361 | + (BOOL)isTapToDismissEnabled; 362 | 363 | /** 364 | Enables or disables queueing behavior for toast views. When `YES`, 365 | toast views will appear one after the other. When `NO`, multiple Toast 366 | views will appear at the same time (potentially overlapping depending 367 | on their positions). This has no effect on the toast activity view, 368 | which operates independently of normal toast views. Default is `YES`. 369 | 370 | @param queueEnabled 371 | */ 372 | + (void)setQueueEnabled:(BOOL)queueEnabled; 373 | 374 | /** 375 | Returns `YES` if the queue is enabled, otherwise `NO`. 376 | Default is `YES`. 377 | 378 | @return BOOL 379 | */ 380 | + (BOOL)isQueueEnabled; 381 | 382 | /** 383 | Sets the default duration. Used for the `makeToast:` and 384 | `showToast:` methods that don't require an explicit duration. 385 | Default is 3.0. 386 | 387 | @param duration The toast duration 388 | */ 389 | + (void)setDefaultDuration:(NSTimeInterval)duration; 390 | 391 | /** 392 | Returns the default duration. Default is 3.0. 393 | 394 | @return duration The toast duration 395 | */ 396 | + (NSTimeInterval)defaultDuration; 397 | 398 | /** 399 | Sets the default position. Used for the `makeToast:` and 400 | `showToast:` methods that don't require an explicit position. 401 | Default is `CSToastPositionBottom`. 402 | 403 | @param position The default center point. Can be one of the predefined 404 | CSToastPosition constants or a `CGPoint` wrapped in an `NSValue` object. 405 | */ 406 | + (void)setDefaultPosition:(id)position; 407 | 408 | /** 409 | Returns the default toast position. Default is `CSToastPositionBottom`. 410 | 411 | @return position The default center point. Will be one of the predefined 412 | CSToastPosition constants or a `CGPoint` wrapped in an `NSValue` object. 413 | */ 414 | + (id)defaultPosition; 415 | 416 | @end 417 | -------------------------------------------------------------------------------- /ios/UIView+Toast.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+Toast.m 3 | // Toast 4 | // 5 | // Copyright (c) 2011-2015 Charles Scalesse. 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a 8 | // copy of this software and associated documentation files (the 9 | // "Software"), to deal in the Software without restriction, including 10 | // without limitation the rights to use, copy, modify, merge, publish, 11 | // distribute, sublicense, and/or sell copies of the Software, and to 12 | // permit persons to whom the Software is furnished to do so, subject to 13 | // the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included 16 | // in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 19 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | // CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 23 | // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 24 | // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | 26 | #import "UIView+Toast.h" 27 | #import 28 | #import 29 | 30 | NSString * CSToastPositionTop = @"CSToastPositionTop"; 31 | NSString * CSToastPositionCenter = @"CSToastPositionCenter"; 32 | NSString * CSToastPositionBottom = @"CSToastPositionBottom"; 33 | 34 | // Keys for values associated with toast views 35 | static const NSString * CSToastTimerKey = @"CSToastTimerKey"; 36 | static const NSString * CSToastDurationKey = @"CSToastDurationKey"; 37 | static const NSString * CSToastPositionKey = @"CSToastPositionKey"; 38 | static const NSString * CSToastCompletionKey = @"CSToastCompletionKey"; 39 | 40 | // Keys for values associated with self 41 | static const NSString * CSToastActiveToastViewKey = @"CSToastActiveToastViewKey"; 42 | static const NSString * CSToastActivityViewKey = @"CSToastActivityViewKey"; 43 | static const NSString * CSToastQueueKey = @"CSToastQueueKey"; 44 | 45 | @interface UIView (ToastPrivate) 46 | 47 | /** 48 | These private methods are being prefixed with "cs_" to reduce the likelihood of non-obvious 49 | naming conflicts with other UIView methods. 50 | 51 | @discussion Should the public API also use the cs_ prefix? Technically it should, but it 52 | results in code that is less legible. The current public method names seem unlikely to cause 53 | conflicts so I think we should favor the cleaner API for now. 54 | */ 55 | - (void)cs_showToast:(UIView *)toast duration:(NSTimeInterval)duration position:(id)position; 56 | - (void)cs_hideToast:(UIView *)toast; 57 | - (void)cs_hideToast:(UIView *)toast fromTap:(BOOL)fromTap; 58 | - (void)cs_toastTimerDidFinish:(NSTimer *)timer; 59 | - (void)cs_handleToastTapped:(UITapGestureRecognizer *)recognizer; 60 | - (CGPoint)cs_centerPointForPosition:(id)position withToast:(UIView *)toast; 61 | - (NSMutableArray *)cs_toastQueue; 62 | 63 | @end 64 | 65 | @implementation UIView (Toast) 66 | 67 | #pragma mark - Make Toast Methods 68 | 69 | - (void)makeToast:(NSString *)message { 70 | [self makeToast:message duration:[CSToastManager defaultDuration] position:[CSToastManager defaultPosition] customStyle:nil style:nil]; 71 | } 72 | 73 | - (void)makeToast:(NSString *)message duration:(NSTimeInterval)duration position:(id)position customStyle:(NSDictionary *)customStyle { 74 | [self makeToast:message duration:duration position:position customStyle:customStyle style:nil]; 75 | } 76 | 77 | - (void)makeToast:(NSString *)message duration:(NSTimeInterval)duration position:(id)position customStyle:(NSDictionary *)customStyle style:(CSToastStyle *)style { 78 | UIView *toast = [self toastViewForMessage:message customStyle:customStyle title:nil image:nil style:style]; 79 | [self showToast:toast duration:duration position:position customStyle:customStyle completion:nil]; 80 | } 81 | 82 | - (void)makeToast:(NSString *)message duration:(NSTimeInterval)duration position:(id)position customStyle:(NSDictionary *)customStyle title:(NSString *)title image:(UIImage *)image style:(CSToastStyle *)style completion:(void(^)(BOOL didTap))completion { 83 | UIView *toast = [self toastViewForMessage:message customStyle:customStyle title:title image:image style:style]; 84 | [self showToast:toast duration:duration position:position customStyle:customStyle completion:completion]; 85 | } 86 | 87 | #pragma mark - Show Toast Methods 88 | 89 | - (void)showToast:(UIView *)toast { 90 | [self showToast:toast duration:[CSToastManager defaultDuration] position:[CSToastManager defaultPosition] customStyle:nil completion:nil]; 91 | } 92 | 93 | - (void)showToast:(UIView *)toast duration:(NSTimeInterval)duration position:(id)position customStyle:(NSDictionary *)customStyle completion:(void(^)(BOOL didTap))completion { 94 | // sanity 95 | if (toast == nil) return; 96 | 97 | // store the completion block on the toast view 98 | objc_setAssociatedObject(toast, &CSToastCompletionKey, completion, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 99 | 100 | if ([CSToastManager isQueueEnabled] && objc_getAssociatedObject(self, &CSToastActiveToastViewKey) != nil) { 101 | // we're about to queue this toast view so we need to store the duration and position as well 102 | objc_setAssociatedObject(toast, &CSToastDurationKey, @(duration), OBJC_ASSOCIATION_RETAIN_NONATOMIC); 103 | objc_setAssociatedObject(toast, &CSToastPositionKey, position, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 104 | 105 | // enqueue 106 | [self.cs_toastQueue addObject:toast]; 107 | } else { 108 | // present 109 | [self cs_showToast:toast duration:duration position:position customStyle:customStyle]; 110 | } 111 | } 112 | 113 | #pragma mark - Private Show/Hide Methods 114 | 115 | - (void)cs_showToast:(UIView *)toast duration:(NSTimeInterval)duration position:(id)position customStyle:(NSDictionary *)customStyle { 116 | toast.center = [self cs_centerPointForPosition:position withToast:toast]; 117 | toast.alpha = 0.0; 118 | 119 | if ([CSToastManager isTapToDismissEnabled]) { 120 | UITapGestureRecognizer *recognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(cs_handleToastTapped:)]; 121 | [toast addGestureRecognizer:recognizer]; 122 | toast.userInteractionEnabled = YES; 123 | toast.exclusiveTouch = YES; 124 | } 125 | 126 | // set the active toast 127 | objc_setAssociatedObject(self, &CSToastActiveToastViewKey, toast, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 128 | 129 | [self addSubview:toast]; 130 | 131 | [UIView animateWithDuration:[[CSToastManager sharedStyle] fadeDuration] 132 | delay:0.0 133 | options:(UIViewAnimationOptionCurveEaseOut | UIViewAnimationOptionAllowUserInteraction) 134 | animations:^{ 135 | toast.alpha = 1.0; 136 | } completion:^(BOOL finished) { 137 | NSTimer *timer = [NSTimer timerWithTimeInterval:duration target:self selector:@selector(cs_toastTimerDidFinish:) userInfo:toast repeats:NO]; 138 | [[NSRunLoop mainRunLoop] addTimer:timer forMode:NSRunLoopCommonModes]; 139 | objc_setAssociatedObject(toast, &CSToastTimerKey, timer, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 140 | }]; 141 | } 142 | 143 | - (void)cs_hideToast:(UIView *)toast { 144 | [self cs_hideToast:toast fromTap:NO]; 145 | } 146 | 147 | - (void)cs_hideToast:(UIView *)toast fromTap:(BOOL)fromTap { 148 | [UIView animateWithDuration:[[CSToastManager sharedStyle] fadeDuration] 149 | delay:0.0 150 | options:(UIViewAnimationOptionCurveEaseIn | UIViewAnimationOptionBeginFromCurrentState) 151 | animations:^{ 152 | toast.alpha = 0.0; 153 | } completion:^(BOOL finished) { 154 | [toast removeFromSuperview]; 155 | 156 | // clear the active toast 157 | objc_setAssociatedObject(self, &CSToastActiveToastViewKey, nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 158 | 159 | // execute the completion block, if necessary 160 | void (^completion)(BOOL didTap) = objc_getAssociatedObject(toast, &CSToastCompletionKey); 161 | if (completion) { 162 | completion(fromTap); 163 | } 164 | 165 | if ([self.cs_toastQueue count] > 0) { 166 | // dequeue 167 | UIView *nextToast = [[self cs_toastQueue] firstObject]; 168 | [[self cs_toastQueue] removeObjectAtIndex:0]; 169 | 170 | // present the next toast 171 | NSTimeInterval duration = [objc_getAssociatedObject(nextToast, &CSToastDurationKey) doubleValue]; 172 | id position = objc_getAssociatedObject(nextToast, &CSToastPositionKey); 173 | [self cs_showToast:nextToast duration:duration position:position]; 174 | } 175 | }]; 176 | } 177 | /* 178 | This method created because of hex color convert to UIColor type. 179 | */ 180 | - (UIColor *)stringColorConvertoUIColor:(NSString *)color { 181 | unsigned int c; 182 | if ([color characterAtIndex:0] == '#') { 183 | [[NSScanner scannerWithString:[color substringFromIndex:1]] scanHexInt:&c]; 184 | } else { 185 | [[NSScanner scannerWithString:color] scanHexInt:&c]; 186 | } 187 | return [UIColor colorWithRed:((c & 0xff0000) >> 16)/255.0 green:((c & 0xff00) >> 8)/255.0 blue:(c & 0xff)/255.0 alpha:1.0]; 188 | } 189 | 190 | #pragma mark - View Construction 191 | 192 | - (UIView *)toastViewForMessage:(NSString *)message customStyle:(NSDictionary *)customStyle title:(NSString *)title image:(UIImage *)image style:(CSToastStyle *)style { 193 | // sanity 194 | if(message == nil && title == nil && image == nil && customStyle == nil) return nil; 195 | 196 | // default to the shared style 197 | if (style == nil) { 198 | style = [CSToastManager sharedStyle]; 199 | } 200 | 201 | // dynamically build a toast view with any combination of message, title, & image 202 | UILabel *messageLabel = nil; 203 | UILabel *titleLabel = nil; 204 | UIImageView *imageView = nil; 205 | UIColor *textColor; 206 | struct CGColor *borderColor=nil; 207 | struct CGColor *backgroundColor=nil; 208 | float borderWidth=1.0; 209 | float borderRadius=1.0; 210 | float width=0.0; 211 | float height=0.0; 212 | UIView *wrapperView = [[UIView alloc] init]; 213 | wrapperView.autoresizingMask = (UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin); 214 | wrapperView.layer.cornerRadius = style.cornerRadius; 215 | 216 | if (style.displayShadow) { 217 | wrapperView.layer.shadowColor = style.shadowColor.CGColor; 218 | wrapperView.layer.shadowOpacity = style.shadowOpacity; 219 | wrapperView.layer.shadowRadius = style.shadowRadius; 220 | wrapperView.layer.shadowOffset = style.shadowOffset; 221 | } 222 | 223 | wrapperView.backgroundColor = [UIColor clearColor]; 224 | 225 | if(image != nil) { 226 | imageView = [[UIImageView alloc] initWithImage:image]; 227 | imageView.contentMode = UIViewContentModeScaleAspectFit; 228 | imageView.frame = CGRectMake(style.horizontalPadding, style.verticalPadding, style.imageSize.width, style.imageSize.height); 229 | } 230 | 231 | CGRect imageRect = CGRectZero; 232 | 233 | if(imageView != nil) { 234 | imageRect.origin.x = style.horizontalPadding; 235 | imageRect.origin.y = style.verticalPadding; 236 | imageRect.size.width = imageView.bounds.size.width; 237 | imageRect.size.height = imageView.bounds.size.height; 238 | } 239 | 240 | if (title != nil) { 241 | titleLabel = [[UILabel alloc] init]; 242 | titleLabel.numberOfLines = style.titleNumberOfLines; 243 | titleLabel.font = style.titleFont; 244 | titleLabel.textAlignment = style.titleAlignment; 245 | titleLabel.lineBreakMode = NSLineBreakByTruncatingTail; 246 | titleLabel.textColor = style.titleColor; 247 | titleLabel.backgroundColor = [UIColor clearColor]; 248 | titleLabel.alpha = 1.0; 249 | titleLabel.text = title; 250 | 251 | // size the title label according to the length of the text 252 | CGSize maxSizeTitle = CGSizeMake((self.bounds.size.width * style.maxWidthPercentage) - imageRect.size.width, self.bounds.size.height * style.maxHeightPercentage); 253 | CGSize expectedSizeTitle = [titleLabel sizeThatFits:maxSizeTitle]; 254 | // UILabel can return a size larger than the max size when the number of lines is 1 255 | expectedSizeTitle = CGSizeMake(MIN(maxSizeTitle.width, expectedSizeTitle.width), MIN(maxSizeTitle.height, expectedSizeTitle.height)); 256 | titleLabel.frame = CGRectMake(0.0, 0.0, expectedSizeTitle.width, expectedSizeTitle.height); 257 | } 258 | if(customStyle && customStyle[@"color"]){ 259 | textColor=[self stringColorConvertoUIColor:customStyle[@"color"]]; 260 | } 261 | if(customStyle && !customStyle[@"color"]){ 262 | textColor=style.messageColor; 263 | } 264 | if(customStyle && customStyle[@"borderColor"]){ 265 | borderColor=[self stringColorConvertoUIColor:customStyle[@"borderColor"]].CGColor; 266 | } 267 | if(customStyle && !customStyle[@"borderColor"]){ 268 | borderColor=[UIColor clearColor].CGColor; 269 | } 270 | if(customStyle && customStyle[@"borderWidth"]){ 271 | borderWidth=(float)[customStyle[@"borderWidth"] floatValue]; 272 | } 273 | if(customStyle && !customStyle[@"borderWidth"]){ 274 | borderWidth=0; 275 | } 276 | if(customStyle && customStyle[@"borderRadius"]){ 277 | borderRadius=(float)[customStyle[@"borderRadius"] floatValue]; 278 | } 279 | if(customStyle && !customStyle[@"borderRadius"]){ 280 | borderRadius=0; 281 | } 282 | if(customStyle && customStyle[@"backgroundColor"]){ 283 | backgroundColor=[self stringColorConvertoUIColor:customStyle[@"backgroundColor"]].CGColor; 284 | } 285 | if(customStyle && !customStyle[@"backgroundColor"]){ 286 | backgroundColor=style.backgroundColor.CGColor; 287 | } 288 | if(customStyle && customStyle[@"width"]){ 289 | width=[customStyle[@"width"] floatValue]; 290 | } 291 | 292 | if(!customStyle){ 293 | textColor=style.messageColor; 294 | borderColor=[UIColor clearColor].CGColor; 295 | borderWidth=0; 296 | borderRadius=0; 297 | backgroundColor=style.backgroundColor.CGColor; 298 | } 299 | if (message != nil) { 300 | messageLabel=[[UILabel alloc] init]; 301 | messageLabel.numberOfLines = style.messageNumberOfLines; 302 | messageLabel.font = style.messageFont; 303 | messageLabel.textAlignment =style.messageAlignment; 304 | messageLabel.lineBreakMode = NSLineBreakByTruncatingTail; 305 | messageLabel.textColor =textColor; 306 | messageLabel.layer.borderColor = borderColor; 307 | messageLabel.layer.borderWidth=borderWidth; 308 | messageLabel.layer.cornerRadius=borderRadius; 309 | messageLabel.layer.backgroundColor = backgroundColor; 310 | messageLabel.backgroundColor = [UIColor clearColor]; 311 | messageLabel.alpha = 1.0; 312 | messageLabel.text = message; 313 | 314 | CGSize maxSizeMessage = CGSizeMake((self.bounds.size.width * style.maxWidthPercentage) - imageRect.size.width, self.bounds.size.height * style.maxHeightPercentage); 315 | CGSize expectedSizeMessage = [messageLabel sizeThatFits:maxSizeMessage]; 316 | // UILabel can return a size larger than the max size when the number of lines is 1 317 | expectedSizeMessage = CGSizeMake(MIN(maxSizeMessage.width, expectedSizeMessage.width), MIN(maxSizeMessage.height, expectedSizeMessage.height)); 318 | messageLabel.frame = CGRectMake(0.0, 0.0, expectedSizeMessage.width, expectedSizeMessage.height); 319 | } 320 | 321 | CGRect titleRect = CGRectZero; 322 | 323 | if(customStyle && !customStyle[@"width"]){ 324 | width=messageLabel.bounds.size.width; 325 | } 326 | if(customStyle && customStyle[@"height"]){ 327 | height=[customStyle[@"height"] floatValue]; 328 | } 329 | if(customStyle && !customStyle[@"height"]){ 330 | height=messageLabel.bounds.size.height; 331 | } 332 | if(!customStyle){ 333 | width=messageLabel.bounds.size.width; 334 | height=messageLabel.bounds.size.height; 335 | } 336 | 337 | if(titleLabel != nil) { 338 | titleRect.origin.x = imageRect.origin.x + imageRect.size.width + style.horizontalPadding; 339 | titleRect.origin.y = style.verticalPadding; 340 | titleRect.size.width = titleLabel.bounds.size.width; 341 | titleRect.size.height = titleLabel.bounds.size.height; 342 | } 343 | 344 | CGRect messageRect = CGRectZero; 345 | 346 | if(messageLabel != nil) { 347 | messageRect.origin.x = imageRect.origin.x + imageRect.size.width + style.horizontalPadding; 348 | messageRect.origin.y = titleRect.origin.y + titleRect.size.height + style.verticalPadding; 349 | messageRect.size.width = width; 350 | messageRect.size.height = height; 351 | } 352 | 353 | CGFloat longerWidth = MAX(titleRect.size.width, messageRect.size.width); 354 | CGFloat longerX = MAX(titleRect.origin.x, messageRect.origin.x); 355 | 356 | // Wrapper width uses the longerWidth or the image width, whatever is larger. Same logic applies to the wrapper height. 357 | CGFloat wrapperWidth = MAX((imageRect.size.width + (style.horizontalPadding * 2.0)), (longerX + longerWidth + style.horizontalPadding)); 358 | CGFloat wrapperHeight = MAX((messageRect.origin.y + messageRect.size.height + style.verticalPadding), (imageRect.size.height + (style.verticalPadding * 2.0))); 359 | 360 | wrapperView.frame = CGRectMake(0.0, 0.0, wrapperWidth, wrapperHeight); 361 | 362 | if(titleLabel != nil) { 363 | titleLabel.frame = titleRect; 364 | [wrapperView addSubview:titleLabel]; 365 | } 366 | 367 | if(messageLabel != nil) { 368 | messageLabel.frame = messageRect; 369 | [wrapperView addSubview:messageLabel]; 370 | } 371 | 372 | if(imageView != nil) { 373 | [wrapperView addSubview:imageView]; 374 | } 375 | 376 | return wrapperView; 377 | } 378 | 379 | #pragma mark - Queue 380 | 381 | - (NSMutableArray *)cs_toastQueue { 382 | NSMutableArray *cs_toastQueue = objc_getAssociatedObject(self, &CSToastQueueKey); 383 | if (cs_toastQueue == nil) { 384 | cs_toastQueue = [[NSMutableArray alloc] init]; 385 | objc_setAssociatedObject(self, &CSToastQueueKey, cs_toastQueue, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 386 | } 387 | return cs_toastQueue; 388 | } 389 | 390 | #pragma mark - Events 391 | 392 | - (void)cs_toastTimerDidFinish:(NSTimer *)timer { 393 | [self cs_hideToast:(UIView *)timer.userInfo]; 394 | } 395 | 396 | - (void)cs_handleToastTapped:(UITapGestureRecognizer *)recognizer { 397 | UIView *toast = recognizer.view; 398 | NSTimer *timer = (NSTimer *)objc_getAssociatedObject(toast, &CSToastTimerKey); 399 | [timer invalidate]; 400 | 401 | [self cs_hideToast:toast fromTap:YES]; 402 | } 403 | 404 | #pragma mark - Activity Methods 405 | 406 | - (void)makeToastActivity:(id)position { 407 | // sanity 408 | UIView *existingActivityView = (UIView *)objc_getAssociatedObject(self, &CSToastActivityViewKey); 409 | if (existingActivityView != nil) return; 410 | 411 | CSToastStyle *style = [CSToastManager sharedStyle]; 412 | 413 | UIView *activityView = [[UIView alloc] initWithFrame:CGRectMake(0.0, 0.0, style.activitySize.width, style.activitySize.height)]; 414 | activityView.center = [self cs_centerPointForPosition:position withToast:activityView]; 415 | activityView.backgroundColor = style.backgroundColor; 416 | activityView.alpha = 0.0; 417 | activityView.autoresizingMask = (UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin); 418 | activityView.layer.cornerRadius = style.cornerRadius; 419 | 420 | if (style.displayShadow) { 421 | activityView.layer.shadowColor = style.shadowColor.CGColor; 422 | activityView.layer.shadowOpacity = style.shadowOpacity; 423 | activityView.layer.shadowRadius = style.shadowRadius; 424 | activityView.layer.shadowOffset = style.shadowOffset; 425 | } 426 | 427 | UIActivityIndicatorView *activityIndicatorView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge]; 428 | activityIndicatorView.center = CGPointMake(activityView.bounds.size.width / 2, activityView.bounds.size.height / 2); 429 | [activityView addSubview:activityIndicatorView]; 430 | [activityIndicatorView startAnimating]; 431 | 432 | // associate the activity view with self 433 | objc_setAssociatedObject (self, &CSToastActivityViewKey, activityView, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 434 | 435 | [self addSubview:activityView]; 436 | 437 | [UIView animateWithDuration:style.fadeDuration 438 | delay:0.0 439 | options:UIViewAnimationOptionCurveEaseOut 440 | animations:^{ 441 | activityView.alpha = 1.0; 442 | } completion:nil]; 443 | } 444 | 445 | - (void)hideToastActivity { 446 | UIView *existingActivityView = (UIView *)objc_getAssociatedObject(self, &CSToastActivityViewKey); 447 | if (existingActivityView != nil) { 448 | [UIView animateWithDuration:[[CSToastManager sharedStyle] fadeDuration] 449 | delay:0.0 450 | options:(UIViewAnimationOptionCurveEaseIn | UIViewAnimationOptionBeginFromCurrentState) 451 | animations:^{ 452 | existingActivityView.alpha = 0.0; 453 | } completion:^(BOOL finished) { 454 | [existingActivityView removeFromSuperview]; 455 | objc_setAssociatedObject (self, &CSToastActivityViewKey, nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 456 | }]; 457 | } 458 | } 459 | 460 | #pragma mark - Helpers 461 | 462 | - (CGPoint)cs_centerPointForPosition:(id)point withToast:(UIView *)toast { 463 | CSToastStyle *style = [CSToastManager sharedStyle]; 464 | 465 | if([point isKindOfClass:[NSString class]]) { 466 | if([point caseInsensitiveCompare:CSToastPositionTop] == NSOrderedSame) { 467 | return CGPointMake(self.bounds.size.width/2, (toast.frame.size.height / 2) + style.verticalPadding); 468 | } else if([point caseInsensitiveCompare:CSToastPositionCenter] == NSOrderedSame) { 469 | return CGPointMake(self.bounds.size.width / 2, self.bounds.size.height / 2); 470 | } 471 | } else if ([point isKindOfClass:[NSValue class]]) { 472 | return [point CGPointValue]; 473 | } 474 | 475 | // default to bottom 476 | return CGPointMake(self.bounds.size.width/2, (self.bounds.size.height - (toast.frame.size.height / 2)) - style.verticalPadding); 477 | } 478 | 479 | @end 480 | 481 | @implementation CSToastStyle 482 | 483 | #pragma mark - Constructors 484 | 485 | - (instancetype)initWithDefaultStyle { 486 | self = [super init]; 487 | if (self) { 488 | self.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.8]; 489 | self.titleColor = [UIColor whiteColor]; 490 | self.messageColor = [UIColor whiteColor]; 491 | self.maxWidthPercentage = 0.8; 492 | self.maxHeightPercentage = 0.8; 493 | self.horizontalPadding = 10.0; 494 | self.verticalPadding = 10.0; 495 | self.cornerRadius = 10.0; 496 | self.titleFont = [UIFont boldSystemFontOfSize:16.0]; 497 | self.messageFont = [UIFont systemFontOfSize:16.0]; 498 | self.titleAlignment = NSTextAlignmentCenter; 499 | self.messageAlignment = NSTextAlignmentCenter; 500 | self.titleNumberOfLines = 0; 501 | self.messageNumberOfLines = 0; 502 | self.displayShadow = NO; 503 | self.shadowOpacity = 0.8; 504 | self.shadowRadius = 6.0; 505 | self.shadowOffset = CGSizeMake(4.0, 4.0); 506 | self.imageSize = CGSizeMake(80.0, 80.0); 507 | self.activitySize = CGSizeMake(100.0, 100.0); 508 | self.fadeDuration = 0.2; 509 | } 510 | return self; 511 | } 512 | 513 | - (void)setMaxWidthPercentage:(CGFloat)maxWidthPercentage { 514 | _maxWidthPercentage = MAX(MIN(maxWidthPercentage, 1.0), 0.0); 515 | } 516 | 517 | - (void)setMaxHeightPercentage:(CGFloat)maxHeightPercentage { 518 | _maxHeightPercentage = MAX(MIN(maxHeightPercentage, 1.0), 0.0); 519 | } 520 | 521 | - (instancetype)init NS_UNAVAILABLE { 522 | return nil; 523 | } 524 | 525 | @end 526 | 527 | @interface CSToastManager () 528 | 529 | @property (strong, nonatomic) CSToastStyle *sharedStyle; 530 | @property (assign, nonatomic, getter=isTapToDismissEnabled) BOOL tapToDismissEnabled; 531 | @property (assign, nonatomic, getter=isQueueEnabled) BOOL queueEnabled; 532 | @property (assign, nonatomic) NSTimeInterval defaultDuration; 533 | @property (strong, nonatomic) id defaultPosition; 534 | 535 | @end 536 | 537 | @implementation CSToastManager 538 | 539 | #pragma mark - Constructors 540 | 541 | + (instancetype)sharedManager { 542 | static CSToastManager *_sharedManager = nil; 543 | static dispatch_once_t oncePredicate; 544 | dispatch_once(&oncePredicate, ^{ 545 | _sharedManager = [[self alloc] init]; 546 | }); 547 | 548 | return _sharedManager; 549 | } 550 | 551 | - (instancetype)init { 552 | self = [super init]; 553 | if (self) { 554 | self.sharedStyle = [[CSToastStyle alloc] initWithDefaultStyle]; 555 | self.tapToDismissEnabled = YES; 556 | self.queueEnabled = YES; 557 | self.defaultDuration = 3.0; 558 | self.defaultPosition = CSToastPositionBottom; 559 | } 560 | return self; 561 | } 562 | 563 | #pragma mark - Singleton Methods 564 | 565 | + (void)setSharedStyle:(CSToastStyle *)sharedStyle { 566 | [[self sharedManager] setSharedStyle:sharedStyle]; 567 | } 568 | 569 | + (CSToastStyle *)sharedStyle { 570 | return [[self sharedManager] sharedStyle]; 571 | } 572 | 573 | + (void)setTapToDismissEnabled:(BOOL)tapToDismissEnabled { 574 | [[self sharedManager] setTapToDismissEnabled:tapToDismissEnabled]; 575 | } 576 | 577 | + (BOOL)isTapToDismissEnabled { 578 | return [[self sharedManager] isTapToDismissEnabled]; 579 | } 580 | 581 | + (void)setQueueEnabled:(BOOL)queueEnabled { 582 | [[self sharedManager] setQueueEnabled:queueEnabled]; 583 | } 584 | 585 | + (BOOL)isQueueEnabled { 586 | return [[self sharedManager] isQueueEnabled]; 587 | } 588 | 589 | + (void)setDefaultDuration:(NSTimeInterval)duration { 590 | [[self sharedManager] setDefaultDuration:duration]; 591 | } 592 | 593 | + (NSTimeInterval)defaultDuration { 594 | return [[self sharedManager] defaultDuration]; 595 | } 596 | 597 | + (void)setDefaultPosition:(id)position { 598 | if ([position isKindOfClass:[NSString class]] || [position isKindOfClass:[NSValue class]]) { 599 | [[self sharedManager] setDefaultPosition:position]; 600 | } 601 | } 602 | 603 | + (id)defaultPosition { 604 | return [[self sharedManager] defaultPosition]; 605 | } 606 | 607 | @end 608 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | 2 | { 3 | "name": "react-native-toast-native", 4 | "version": "1.2.1", 5 | "description": "Native toast for react-native. In Android it's just native toast, in iOS it's https://github.com/scalessec/Toast", 6 | "main": "index.js", 7 | "scripts": { 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "keywords": [ 11 | "react-native", 12 | "native", 13 | "toast" 14 | ], 15 | "repository": { 16 | "type": "git", 17 | "url": "https://github.com/onemolegames/react-native-toast-native" 18 | }, 19 | "author": "yasemincidem", 20 | "license": "MIT", 21 | "peerDependencies": { 22 | "react": "*", 23 | "react-native": "*" 24 | } 25 | } -------------------------------------------------------------------------------- /test.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onemolegames/react-native-toast-native/e18d75dd1dd173700a5a6abdc9284bd6da2705bb/test.gif --------------------------------------------------------------------------------