JToast.m

//
//  Free. No warranty. Do what you want with it.
// Created by Jakar some time in 2014
//

#import "JToast.h"
//See https://github.com/JakarCo/ios-util for KeyboardStateListener
#import "KeyboardStateListener.h"
//See https://github.com/jivadevoe/NSTimer-Blocks for NSTimer+Blocks
#import "NSTimer+Blocks.h"
#import "JToastQueue.h"

@interface JToast(){
}
@property (nonatomic, strong) UIView *toast;
@property (nonatomic, strong) UILabel *label;
@property (nonatomic, weak) UIView *parent;

//@property (nonatomic, readonly) NSInteger duration;
@property (nonatomic, strong, readonly) NSString *message;
@property (nonatomic, readonly) ToastPosition position;

@end

@implementation JToast


const CGFloat TOAST_TRANSITION_LENGTH = 0.5f; //for use with [UIView animateWithDuration]

const NSInteger TOAST_DURATION_SHORT = 2;
const NSInteger TOAST_DURATION_MEDIUM = 3;
const NSInteger TOAST_DURATION_LONG = 4;

const CGFloat MIN_DIFF_FOR_ANIM_LENGTH = 0.1f;

const CGFloat TOAST_ALPHA_DEFAULT = 0.90f;


-(void) tapped:(UIGestureRecognizer *)g{
    [JToastQueue removeToast:self ifDisplayed:YES];    
}
-(void) setDefaults{
    _toast = [[UIView alloc] init];
    UITapGestureRecognizer *g = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapped:)];
    [_toast addGestureRecognizer:g];
    _label = [[UILabel alloc] init];
    
    _toast.backgroundColor = [UIColor blackColor];
    _toast.layer.cornerRadius = 5;
    _toast.layer.borderColor = [UIColor blackColor].CGColor;
    _toast.layer.borderWidth = 1;
    _toast.layer.shadowColor = [UIColor blackColor].CGColor;
    _toast.layer.shadowRadius = 5;
    _toast.layer.shadowOpacity = 1.0;
    _toast.layer.shadowOffset = CGSizeMake(0, 2);
    
    

    _label.numberOfLines = 0;
    _label.textAlignment = NSTextAlignmentCenter;
    _label.lineBreakMode = NSLineBreakByWordWrapping;
    UIFont *font = [UIFont systemFontOfSize:[UIFont smallSystemFontSize]];
    _label.textColor = [UIColor whiteColor];
    _label.font = font;
}
-(id)initWithTopWindow{
    
    if (self = [super init]){
        [self setDefaults];
        _parent = [[[[UIApplication sharedApplication] keyWindow] subviews] lastObject];
        if (_parent.isHidden){
            _parent = [[[UIApplication sharedApplication].windows sortedArrayUsingComparator:^NSComparisonResult(UIWindow *win1, UIWindow *win2) {
                return win1.windowLevel - win2.windowLevel;
            }] lastObject];
        }
    }
    return self;
}
-(id)initInTopViewFromChild:(UIViewController *)child{ //this will take a child view and get it's topmost parent
    if (self=[super init]){

        UIViewController *v = child;
        while (v.parentViewController!=nil){
            v = v.parentViewController;
        }
        _parent = v.view;
        
        [self setDefaults];
    }
    return self;
}
-(id)initInParentView:(UIView *)parent{
    
    if (self=[super init]){
        _parent = parent;
        
        [self setDefaults];
    }
    return self;
}
-(JToast *)toastWithMessage:(NSString *)message forToastDuration:(ToastDuration)duration atPosition:(ToastPosition)position{
    CGFloat animLength;
    CGFloat multiplier = 1.0;
    switch (duration) {
        case ToastDurationCalculatedLong:
            multiplier = 1.5;
        case ToastDurationCalculated:
            animLength = ((message.length/6)+1)/(200/60); //6 = avg word length of 5 + 1 space :: +1 is because there will be 1 more word than number of spaces :: 200/60 is words per second
            animLength = animLength*multiplier; //Add 50% to the length so reading is not rushed
            animLength += TOAST_TRANSITION_LENGTH; //add the transition length since it can't be read very well while it's transitioning.
            if (animLength<2.0)animLength=TOAST_DURATION_SHORT;
            break;
        case ToastDurationShort:
            animLength = TOAST_DURATION_SHORT;
            break;
        case ToastDurationMedium:
            animLength = TOAST_DURATION_MEDIUM;
            break;
        case ToastDurationLong:
            animLength = TOAST_DURATION_LONG;
            break;
    }
    return [self toastWithMessage:message forFloatDuration:animLength atPosition:position];
}
-(JToast *)toastWithMessage:(NSString *)message forFloatDuration:(CGFloat)duration atPosition:(ToastPosition)position{
    
    _message = message;
    _position = position;
    _animLength = duration;
    return self;
}
-(void)showSelf{
    [_parent addSubview:_toast];
    _toast.alpha = 0.0f;
    _label.text = _message;
    
    [_toast addSubview:_label];
//    CGRect parentFrame = [JUIView frameConsideringOrientation:_parent];
    CGRect parentFrame = _parent.bounds;

    CGSize size = [JUIView sizeFromString:_message usingFont:_label.font withMaxWidth:(parentFrame.size.width-parentFrame.size.width*0.05)];
    _label.frame = CGRectMake(0, 0, size.width, size.height);
//    CGRect labelFrame = _label.frame;  this was here to see values debugging
    _toast.frame = CGRectMake(0, 0, size.width+(size.height*0.2), size.height+(size.height*0.2));
    [JUIView centerView:_label withinView:_toast withPositioning:CenterPositionInBounds];
    [JUIView centerView:_toast withinView:_parent withPositioning:CenterPositionHorizontally];//I will always center horizontally, then I will position based on ToastPosition
    
    CGFloat toastVerticalBound = (parentFrame.size.height-[KeyboardStateListener sharedInstance].keyboardSize.height)-(_toast.frame.size.height/2);
    CGFloat toastVerticalCenter;
    switch (_position){
        case ToastPositionBottom:
            toastVerticalCenter = (toastVerticalBound-(toastVerticalBound/3));
            break;
        case ToastPositionCenter:
            toastVerticalCenter = (toastVerticalBound/2);
            break;
        case ToastPositionTop:
            toastVerticalCenter = (toastVerticalBound/3);
            break;
    }
    _toast.center = CGPointMake(_toast.center.x, toastVerticalCenter);
    [_parent bringSubviewToFront:_toast];
    [UIView animateWithDuration:TOAST_TRANSITION_LENGTH animations:^{
       _toast.alpha = TOAST_ALPHA_DEFAULT; 
    }];
}
-(void)hideSelf{
    [UIView animateWithDuration:TOAST_TRANSITION_LENGTH animations:^{
        _toast.alpha = 0.0f;
    }completion:^(BOOL finished){
        if (finished)[_toast removeFromSuperview];
    }];
}
-(BOOL) isEqual:(id)object{
    JToast *passedToast = (JToast *)object;
    if ([passedToast.message isEqualToString:self.message]
        &&(-MIN_DIFF_FOR_ANIM_LENGTH<=(passedToast.animLength-self.animLength)&&(passedToast.animLength-self.animLength)<=MIN_DIFF_FOR_ANIM_LENGTH)
        &&passedToast.position
        &&passedToast.parent==self.parent) return YES;
    return NO;
}

@end