Skip to content

Instantly share code, notes, and snippets.

@mjm918
Last active September 11, 2019 13:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mjm918/4dadbd1d1bad14e33390bf6993ce9983 to your computer and use it in GitHub Desktop.
Save mjm918/4dadbd1d1bad14e33390bf6993ce9983 to your computer and use it in GitHub Desktop.
React Native Banner View (Android & iOS native module)
//
// Banner.h
//
// Created by Mohammad Julfikar on 08/09/2019.
// Copyright © 2019 Facebook. All rights reserved.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface Banner : UIView{
void (^_completionHandler)(int done);
}
- (void) show:(NSString*)messageTxt type:(NSString*)messageType onComplete:(void(^)(int)) handler;
@end
NS_ASSUME_NONNULL_END
//
// Banner.m
//
// Created by Mohammad Julfikar on 08/09/2019.
// Copyright © 2019 Facebook. All rights reserved.
//
#import "Banner.h"
@implementation Banner
- (void) show:(NSString*) messageTxt type:(NSString*)messageType onComplete:(void(^)(int))handler{
BOOL isIphoneX = [self checkIPHONE];
CGFloat width = [[UIScreen mainScreen]bounds].size.width;
CGFloat height = isIphoneX ? 90 : 70;
UIView * banner = [[UIView alloc] initWithFrame:CGRectMake(0, -height, width, height)];
banner.userInteractionEnabled = NO;
[banner setBackgroundColor:[UIColor colorWithRed:1.0f/255.0f green:189.0f/255.0f blue:213.0f/255.0f alpha:1.0]];
if([messageType isEqualToString:@"warning"]){
[banner setBackgroundColor:[UIColor colorWithRed:255.0f/255.0f green:174.0f/255.0f blue:67.0f/255.0f alpha:1.0]];
}
if([messageType isEqualToString:@"success"]){
[banner setBackgroundColor:[UIColor colorWithRed:87.0f/255.0f green:187.0f/255.0f blue:87.0f/255.0f alpha:1.0]];
}
if([messageType isEqualToString:@"danger"]){
[banner setBackgroundColor:[UIColor colorWithRed:255.0f/255.0f green:85.0f/255.0f blue:45.0f/255.0f alpha:1.0]];
}
UIImageView *logo = [[UIImageView alloc] init];
[logo setImage:[UIImage imageNamed:@"bannerView.png"]];
[logo.heightAnchor constraintEqualToConstant:40].active = true;
[logo.widthAnchor constraintEqualToConstant:40].active = true;
CABasicAnimation *theAnimation;
theAnimation=[CABasicAnimation animationWithKeyPath:@"transform.scale"];
theAnimation.duration=0.4;
theAnimation.repeatCount=HUGE_VALF;
theAnimation.autoreverses=YES;
theAnimation.fromValue=[NSNumber numberWithFloat:1.0];
theAnimation.toValue=[NSNumber numberWithFloat:0.7];
theAnimation.timingFunction=[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
[logo.layer addAnimation:theAnimation forKey:@"animateOpacity"];
UILabel *message = [[UILabel alloc] init];
[message setBackgroundColor:[UIColor clearColor]];
[message setText:messageTxt];
[message adjustsFontSizeToFitWidth];
[message setFont:[UIFont systemFontOfSize:15]];
[message setTextColor:[UIColor whiteColor]];
message.numberOfLines = 2;
message.lineBreakMode = NSLineBreakByTruncatingTail;
[message.heightAnchor constraintEqualToConstant:height].active = true;
[message.widthAnchor constraintEqualToConstant:width-60].active = true;
UIStackView *stackView = [[UIStackView alloc] initWithFrame:CGRectMake(0, 0, width, height-30)];
stackView.axis = UILayoutConstraintAxisHorizontal;
stackView.alignment = UIStackViewAlignmentCenter;
[stackView addArrangedSubview:logo];
[stackView addArrangedSubview:message];
stackView.translatesAutoresizingMaskIntoConstraints = false;
[banner addSubview:stackView];
[stackView.centerYAnchor constraintEqualToAnchor:banner.centerYAnchor].active = true;
stackView.bounds = CGRectInset(stackView.frame, 0, isIphoneX ? -20 : -15);
UIWindow* mainWindow = [[UIApplication sharedApplication] keyWindow];
UIView* view = mainWindow.subviews[0];
[view addSubview:banner];
banner.layer.shadowRadius = 1.5f;
banner.layer.shadowColor = [UIColor colorWithRed:0.f/255.f green:0.f/255.f blue:0.f/255.f alpha:1.f].CGColor;
banner.layer.shadowOffset = CGSizeMake(0.0f, 0.0f);
banner.layer.shadowOpacity = 0.5f;
banner.layer.masksToBounds = NO;
UIEdgeInsets shadowInsets = UIEdgeInsetsMake(0, 0, -1.5f, 0);
UIBezierPath *shadowPath = [UIBezierPath bezierPathWithRect:UIEdgeInsetsInsetRect(banner.bounds, shadowInsets)];
banner.layer.shadowPath = shadowPath.CGPath;
_completionHandler = [handler copy];
[UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
CGRect oldPosition = [banner frame];
oldPosition.origin.y = 0;
[banner setFrame:oldPosition];
} completion:^(BOOL finished) {
[UIView animateWithDuration:0.3 delay:1.5f options:UIViewAnimationOptionCurveEaseInOut animations:^{
CGRect oldPosition = [banner frame];
oldPosition.origin.y = -height;
[banner setFrame:oldPosition];
} completion:^(BOOL finished) {
[banner removeFromSuperview];
_completionHandler(1);
_completionHandler = nil;
}];
}];
}
- (BOOL) checkIPHONE{
BOOL isIphoneX = NO;
if([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
switch ((int)[[UIScreen mainScreen] nativeBounds].size.height) {
case 2436:
isIphoneX = YES;
break;
case 2688:
isIphoneX = YES;
break;
case 1792:
isIphoneX = YES;
break;
default:
isIphoneX = NO;
break;
}
}
return isIphoneX;
}
@end
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<scale
android:duration="400"
android:fromXScale="1"
android:fromYScale="1"
android:pivotX="50%"
android:pivotY="50%"
android:repeatCount="infinite"
android:repeatMode="reverse"
android:toXScale="0.75"
android:toYScale="0.75"
android:interpolator="@android:interpolator/bounce" />
<scale
android:duration="200"
android:fromXScale="1"
android:fromYScale="1"
android:pivotX="50%"
android:pivotY="50%"
android:repeatCount="infinite"
android:repeatMode="reverse"
android:toXScale="1.25"
android:toYScale="1.25"
android:interpolator="@android:interpolator/bounce" />
</set>
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape android:shape="rectangle">
<solid android:color="#BDBDBD"/>
<corners android:radius="5dp"/>
</shape>
</item>
<item
android:left="0dp"
android:right="0dp"
android:top="0dp"
android:bottom="2dp">
<shape android:shape="rectangle">
<solid android:color="#ffffff"/>
<corners android:radius="5dp"/>
</shape>
</item>
</layer-list>
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/shadow">
<RelativeLayout
android:padding="10dp"
android:id="@+id/toast"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/logo"
android:layout_width="30dp"
android:layout_height="30dp"
android:src="@mipmap/ic_launcher"/>
<TextView
android:id="@+id/tv_message"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_marginStart="20dp"
android:layout_marginEnd="0dp"
android:textSize="15sp"
android:layout_centerVertical="true"
android:layout_toEndOf="@+id/logo"
android:textColor="#fff"
android:text="Hello World"
android:layout_alignParentRight="true"
android:layout_marginLeft="20dp"
android:layout_marginRight="0dp"
android:layout_toRightOf="@+id/logo" />
</RelativeLayout>
</RelativeLayout>
//
// MessageBox.h
//
// Created by Mohammad Julfikar on 08/09/2019.
// Copyright © 2019 Facebook. All rights reserved.
//
#import <UIKit/UIKit.h>
#import <React/RCTBridgeModule.h>
#import "Banner.h"
NS_ASSUME_NONNULL_BEGIN
@interface MessageBox : NSObject<RCTBridgeModule>
@end
NS_ASSUME_NONNULL_END
public class MessageBox extends ReactContextBaseJavaModule {
private Context context;
private LayoutInflater inflater;
public MessageBox(@Nonnull ReactApplicationContext reactContext, LayoutInflater inflater) {
super(reactContext);
this.context = reactContext;
this.inflater = inflater;
}
@ReactMethod
public void show(String message, String messageType, final Callback callback){
final View toastView = inflater.inflate(R.layout.toast_view, null);
int color = R.color.toastPrimary;
if(messageType.equals("warning")){
color = R.color.toastWarning;
}
if(messageType.equals("success")){
color = R.color.toastSuccess;
}
if(messageType.equals("danger")){
color = R.color.toastDanger;
}
RelativeLayout background = toastView.findViewById(R.id.toast);
background.setBackgroundResource(color);
ImageView imageView = toastView.findViewById(R.id.logo);
imageView.startAnimation(AnimationUtils.loadAnimation(context, R.anim.pulse));
TextView textView = toastView.findViewById(R.id.tv_message);
textView.setText(message);
final Toast toast = new Toast(getCurrentActivity());
toast.setView(toastView);
toast.setDuration(Toast.LENGTH_SHORT);
toast.setGravity(Gravity.TOP | Gravity.FILL_HORIZONTAL, 0,0);
toast.show();
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
callback.invoke(1);
}
}, 2000);
}
@Nonnull
@Override
public String getName() {
return "MessageBox";
}
}
import {NativeModules} from 'react-native';
module.exports = NativeModules.MessageBox;
/*
Usage ----
import MessageBox from './MessageBox';
MessageBox.show("Your message","danger",()=>{
// triggers when message box is dismissed
});
*/
//
// MessageBox.m
//
// Created by Mohammad Julfikar on 08/09/2019.
// Copyright © 2019 Facebook. All rights reserved.
//
#import "MessageBox.h"
@implementation MessageBox
- (dispatch_queue_t)methodQueue
{
return dispatch_get_main_queue();
}
RCT_EXPORT_MODULE();
RCT_EXPORT_METHOD(show:(NSString *)messageTxt type:(NSString *)messageType onClose:(RCTResponseSenderBlock)callback)
{
Banner *banner = [[Banner alloc]init];
[banner show:messageTxt type:messageType onComplete:^(int done){
callback(@[]);
}];
NSLog(@"From Native View %@",messageTxt);
}
@end
public class RNMessageBox implements ReactPackage {
@Nonnull
@Override
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
List<NativeModule> modules = new ArrayList<>();
modules.add(new MessageBox(reactContext, LayoutInflater.from(reactContext)));
return modules;
}
@Nonnull
@Override
public List<ViewManager> createViewManagers(@Nonnull ReactApplicationContext reactContext) {
return Collections.emptyList();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment