Blame client/iOS/Additions/Toast+UIView.m

Packit 1fb8d4
/***************************************************************************
Packit 1fb8d4
 
Packit 1fb8d4
 Toast+UIView.h
Packit 1fb8d4
 Toast
Packit 1fb8d4
 Version 0.1
Packit 1fb8d4
 
Packit 1fb8d4
 Copyright (c) 2011 Charles Scalesse.
Packit 1fb8d4
 
Packit 1fb8d4
 Permission is hereby granted, free of charge, to any person obtaining a
Packit 1fb8d4
 copy of this software and associated documentation files (the
Packit 1fb8d4
 "Software"), to deal in the Software without restriction, including
Packit 1fb8d4
 without limitation the rights to use, copy, modify, merge, publish,
Packit 1fb8d4
 distribute, sublicense, and/or sell copies of the Software, and to
Packit 1fb8d4
 permit persons to whom the Software is furnished to do so, subject to
Packit 1fb8d4
 the following conditions:
Packit 1fb8d4
 
Packit 1fb8d4
 The above copyright notice and this permission notice shall be included
Packit 1fb8d4
 in all copies or substantial portions of the Software.
Packit 1fb8d4
 
Packit 1fb8d4
 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
Packit 1fb8d4
 OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
Packit 1fb8d4
 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
Packit 1fb8d4
 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
Packit 1fb8d4
 CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
Packit 1fb8d4
 TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
Packit 1fb8d4
 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Packit 1fb8d4
 
Packit 1fb8d4
 ***************************************************************************/
Packit 1fb8d4
Packit 1fb8d4
#import "Toast+UIView.h"
Packit 1fb8d4
#import <QuartzCore/QuartzCore.h>
Packit 1fb8d4
#import <objc/runtime.h>
Packit 1fb8d4
Packit 1fb8d4
#define kMaxWidth           0.8
Packit 1fb8d4
#define kMaxHeight          0.8
Packit 1fb8d4
Packit 1fb8d4
#define kHorizontalPadding  10.0
Packit 1fb8d4
#define kVerticalPadding    10.0
Packit 1fb8d4
#define kCornerRadius       10.0
Packit 1fb8d4
#define kOpacity            0.8
Packit 1fb8d4
#define kFontSize           16.0
Packit 1fb8d4
#define kMaxTitleLines      999
Packit 1fb8d4
#define kMaxMessageLines    999
Packit 1fb8d4
Packit 1fb8d4
#define kFadeDuration       0.2
Packit 1fb8d4
Packit 1fb8d4
#define kDefaultLength      3
Packit 1fb8d4
#define kDefaultPosition    @"bottom"
Packit 1fb8d4
Packit 1fb8d4
#define kImageWidth         80.0
Packit 1fb8d4
#define kImageHeight        80.0
Packit 1fb8d4
Packit 1fb8d4
static NSString *kDurationKey = @"duration";
Packit 1fb8d4
Packit 1fb8d4
Packit 1fb8d4
@interface UIView (ToastPrivate)
Packit 1fb8d4
Packit 1fb8d4
-(CGPoint)getPositionFor:(id)position toast:(UIView *)toast;
Packit 1fb8d4
-(UIView *)makeViewForMessage:(NSString *)message title:(NSString *)title image:(UIImage *)image;
Packit 1fb8d4
Packit 1fb8d4
@end
Packit 1fb8d4
Packit 1fb8d4
Packit 1fb8d4
@implementation UIView (Toast)
Packit 1fb8d4
Packit 1fb8d4
#pragma mark -
Packit 1fb8d4
#pragma mark Toast Methods
Packit 1fb8d4
Packit 1fb8d4
-(void)makeToast:(NSString *)message {
Packit 1fb8d4
    [self makeToast:message duration:kDefaultLength position:kDefaultPosition];
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
-(void)makeToast:(NSString *)message duration:(float)interval position:(id)point {
Packit 1fb8d4
    UIView *toast = [self makeViewForMessage:message title:nil image:nil];
Packit 1fb8d4
    [self showToast:toast duration:interval position:point];  
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
-(void)makeToast:(NSString *)message duration:(float)interval position:(id)point title:(NSString *)title {
Packit 1fb8d4
    UIView *toast = [self makeViewForMessage:message title:title image:nil];
Packit 1fb8d4
    [self showToast:toast duration:interval position:point];  
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
-(void)makeToast:(NSString *)message duration:(float)interval position:(id)point image:(UIImage *)image {
Packit 1fb8d4
    UIView *toast = [self makeViewForMessage:message title:nil image:image];
Packit 1fb8d4
    [self showToast:toast duration:interval position:point];  
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
-(void)makeToast:(NSString *)message duration:(float)interval  position:(id)point title:(NSString *)title image:(UIImage *)image {
Packit 1fb8d4
    UIView *toast = [self makeViewForMessage:message title:title image:image];
Packit 1fb8d4
    [self showToast:toast duration:interval position:point];  
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
-(void)showToast:(UIView *)toast {
Packit 1fb8d4
    [self showToast:toast duration:kDefaultLength position:kDefaultPosition];
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
-(void)showToast:(UIView *)toast duration:(float)interval position:(id)point {
Packit 1fb8d4
    
Packit 1fb8d4
    /****************************************************
Packit 1fb8d4
     *                                                  *
Packit 1fb8d4
     * Displays a view for a given duration & position. *
Packit 1fb8d4
     *                                                  *
Packit 1fb8d4
     ****************************************************/
Packit 1fb8d4
    
Packit 1fb8d4
    CGPoint toastPoint = [self getPositionFor:point toast:toast];
Packit 1fb8d4
    
Packit 1fb8d4
    //use an associative reference to associate the toast view with the display interval
Packit 1fb8d4
    objc_setAssociatedObject (toast, &kDurationKey, [NSNumber numberWithFloat:interval], OBJC_ASSOCIATION_RETAIN);
Packit 1fb8d4
    
Packit 1fb8d4
    [toast setCenter:toastPoint];
Packit 1fb8d4
    [toast setAlpha:0.0];
Packit 1fb8d4
    [self addSubview:toast];
Packit 1fb8d4
    
Packit 1fb8d4
    [UIView beginAnimations:@"fade_in" context:toast];
Packit 1fb8d4
    [UIView setAnimationDuration:kFadeDuration];
Packit 1fb8d4
    [UIView setAnimationDelegate:self];
Packit 1fb8d4
    [UIView setAnimationDidStopSelector:@selector(animationDidStop:finished:context:)];
Packit 1fb8d4
    [UIView setAnimationCurve:UIViewAnimationCurveEaseOut];
Packit 1fb8d4
    [toast setAlpha:1.0];
Packit 1fb8d4
    [UIView commitAnimations];
Packit 1fb8d4
    
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
#pragma mark -
Packit 1fb8d4
#pragma mark Animation Delegate Method
Packit 1fb8d4
Packit 1fb8d4
- (void)animationDidStop:(NSString*)animationID finished:(BOOL)finished context:(void *)context {
Packit 1fb8d4
    
Packit 1fb8d4
    UIView *toast = (UIView *)context;
Packit 1fb8d4
    
Packit 1fb8d4
    // retrieve the display interval associated with the view
Packit 1fb8d4
    float interval = [(NSNumber *)objc_getAssociatedObject(toast, &kDurationKey) floatValue];
Packit 1fb8d4
    
Packit 1fb8d4
    if([animationID isEqualToString:@"fade_in"]) {
Packit 1fb8d4
        
Packit 1fb8d4
        [UIView beginAnimations:@"fade_out" context:toast];
Packit 1fb8d4
        [UIView setAnimationDelay:interval];
Packit 1fb8d4
        [UIView setAnimationDuration:kFadeDuration];
Packit 1fb8d4
        [UIView setAnimationDelegate:self];
Packit 1fb8d4
        [UIView setAnimationDidStopSelector:@selector(animationDidStop:finished:context:)];
Packit 1fb8d4
        [UIView setAnimationCurve:UIViewAnimationCurveEaseIn];
Packit 1fb8d4
        [toast setAlpha:0.0];
Packit 1fb8d4
        [UIView commitAnimations];
Packit 1fb8d4
        
Packit 1fb8d4
    } else if ([animationID isEqualToString:@"fade_out"]) {
Packit 1fb8d4
        
Packit 1fb8d4
        [toast removeFromSuperview];
Packit 1fb8d4
        
Packit 1fb8d4
    }
Packit 1fb8d4
    
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
#pragma mark -
Packit 1fb8d4
#pragma mark Private Methods
Packit 1fb8d4
Packit 1fb8d4
-(CGPoint)getPositionFor:(id)point toast:(UIView *)toast {
Packit 1fb8d4
    
Packit 1fb8d4
    /*************************************************************************************
Packit 1fb8d4
     *                                                                                   *
Packit 1fb8d4
     * Converts string literals @"top", @"bottom", @"center", or any point wrapped in an *
Packit 1fb8d4
     * NSValue object into a CGPoint                                                     *
Packit 1fb8d4
     *                                                                                   *
Packit 1fb8d4
     *************************************************************************************/
Packit 1fb8d4
    
Packit 1fb8d4
    if([point isKindOfClass:[NSString class]]) {
Packit 1fb8d4
        
Packit 1fb8d4
        if( [point caseInsensitiveCompare:@"top"] == NSOrderedSame ) {
Packit 1fb8d4
            return CGPointMake(self.bounds.size.width/2, (toast.frame.size.height / 2) + kVerticalPadding);
Packit 1fb8d4
        } else if( [point caseInsensitiveCompare:@"bottom"] == NSOrderedSame ) {
Packit 1fb8d4
            return CGPointMake(self.bounds.size.width/2, (self.bounds.size.height - (toast.frame.size.height / 2)) - kVerticalPadding);
Packit 1fb8d4
        } else if( [point caseInsensitiveCompare:@"center"] == NSOrderedSame ) {
Packit 1fb8d4
            return CGPointMake(self.bounds.size.width / 2, self.bounds.size.height / 2);
Packit 1fb8d4
        }
Packit 1fb8d4
        
Packit 1fb8d4
    } else if ([point isKindOfClass:[NSValue class]]) {
Packit 1fb8d4
        return [point CGPointValue];
Packit 1fb8d4
    }
Packit 1fb8d4
    
Packit 1fb8d4
    NSLog(@"Error: Invalid position for toast.");
Packit 1fb8d4
    return [self getPositionFor:kDefaultPosition toast:toast];
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
-(UIView *)makeViewForMessage:(NSString *)message title:(NSString *)title image:(UIImage *)image {
Packit 1fb8d4
    
Packit 1fb8d4
    /***********************************************************************************
Packit 1fb8d4
     *                                                                                 *
Packit 1fb8d4
     * Dynamically build a toast view with any combination of message, title, & image. *
Packit 1fb8d4
     *                                                                                 *
Packit 1fb8d4
     ***********************************************************************************/
Packit 1fb8d4
    
Packit 1fb8d4
    if((message == nil) && (title == nil) && (image == nil)) return nil;
Packit 1fb8d4
Packit 1fb8d4
    UILabel *messageLabel = nil;
Packit 1fb8d4
    UILabel *titleLabel = nil;
Packit 1fb8d4
    UIImageView *imageView = nil;
Packit 1fb8d4
    
Packit 1fb8d4
    // create the parent view
Packit 1fb8d4
    UIView *wrapperView = [[[UIView alloc] init] autorelease];
Packit 1fb8d4
    [wrapperView.layer setCornerRadius:kCornerRadius];
Packit 1fb8d4
    [wrapperView setBackgroundColor:[[UIColor blackColor] colorWithAlphaComponent:kOpacity]];
Packit 1fb8d4
    wrapperView.autoresizingMask = UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleBottomMargin;
Packit 1fb8d4
Packit 1fb8d4
    if(image != nil) {
Packit 1fb8d4
        imageView = [[[UIImageView alloc] initWithImage:image] autorelease];
Packit 1fb8d4
        [imageView setContentMode:UIViewContentModeScaleAspectFit];
Packit 1fb8d4
        [imageView setFrame:CGRectMake(kHorizontalPadding, kVerticalPadding, kImageWidth, kImageHeight)];
Packit 1fb8d4
    }
Packit 1fb8d4
    
Packit 1fb8d4
    float imageWidth, imageHeight, imageLeft;
Packit 1fb8d4
    
Packit 1fb8d4
    // the imageView frame values will be used to size & position the other views
Packit 1fb8d4
    if(imageView != nil) {
Packit 1fb8d4
        imageWidth = imageView.bounds.size.width;
Packit 1fb8d4
        imageHeight = imageView.bounds.size.height;
Packit 1fb8d4
        imageLeft = kHorizontalPadding;
Packit 1fb8d4
    } else {
Packit 1fb8d4
        imageWidth = imageHeight = imageLeft = 0;
Packit 1fb8d4
    }
Packit 1fb8d4
    
Packit 1fb8d4
    if (title != nil) {
Packit 1fb8d4
        titleLabel = [[[UILabel alloc] init] autorelease];
Packit 1fb8d4
        [titleLabel setNumberOfLines:kMaxTitleLines];
Packit 1fb8d4
        [titleLabel setFont:[UIFont boldSystemFontOfSize:kFontSize]];
Packit 1fb8d4
        [titleLabel setTextAlignment:UITextAlignmentLeft];
Packit 1fb8d4
        [titleLabel setLineBreakMode:UILineBreakModeWordWrap];
Packit 1fb8d4
        [titleLabel setTextColor:[UIColor whiteColor]];
Packit 1fb8d4
        [titleLabel setBackgroundColor:[UIColor clearColor]];
Packit 1fb8d4
        [titleLabel setAlpha:1.0];
Packit 1fb8d4
        [titleLabel setText:title];
Packit 1fb8d4
        
Packit 1fb8d4
        // size the title label according to the length of the text
Packit 1fb8d4
        CGSize maxSizeTitle = CGSizeMake((self.bounds.size.width * kMaxWidth) - imageWidth, self.bounds.size.height * kMaxHeight);
Packit 1fb8d4
        CGSize expectedSizeTitle = [title sizeWithFont:titleLabel.font constrainedToSize:maxSizeTitle lineBreakMode:titleLabel.lineBreakMode]; 
Packit 1fb8d4
        [titleLabel setFrame:CGRectMake(0, 0, expectedSizeTitle.width, expectedSizeTitle.height)];
Packit 1fb8d4
    }
Packit 1fb8d4
    
Packit 1fb8d4
    if (message != nil) {
Packit 1fb8d4
        messageLabel = [[[UILabel alloc] init] autorelease];
Packit 1fb8d4
        [messageLabel setNumberOfLines:kMaxMessageLines];
Packit 1fb8d4
        [messageLabel setFont:[UIFont systemFontOfSize:kFontSize]];
Packit 1fb8d4
        [messageLabel setLineBreakMode:UILineBreakModeWordWrap];
Packit 1fb8d4
        [messageLabel setTextColor:[UIColor whiteColor]];
Packit 1fb8d4
        [messageLabel setTextAlignment:UITextAlignmentCenter];
Packit 1fb8d4
        [messageLabel setBackgroundColor:[UIColor clearColor]];
Packit 1fb8d4
        [messageLabel setAlpha:1.0];
Packit 1fb8d4
        [messageLabel setText:message];
Packit 1fb8d4
        
Packit 1fb8d4
        // size the message label according to the length of the text
Packit 1fb8d4
        CGSize maxSizeMessage = CGSizeMake((self.bounds.size.width * kMaxWidth) - imageWidth, self.bounds.size.height * kMaxHeight);
Packit 1fb8d4
        CGSize expectedSizeMessage = [message sizeWithFont:messageLabel.font constrainedToSize:maxSizeMessage lineBreakMode:messageLabel.lineBreakMode]; 
Packit 1fb8d4
        [messageLabel setFrame:CGRectMake(0, 0, expectedSizeMessage.width, expectedSizeMessage.height)];
Packit 1fb8d4
    }
Packit 1fb8d4
    
Packit 1fb8d4
    // titleLabel frame values
Packit 1fb8d4
    float titleWidth, titleHeight, titleTop, titleLeft;
Packit 1fb8d4
    
Packit 1fb8d4
    if(titleLabel != nil) {
Packit 1fb8d4
        titleWidth = titleLabel.bounds.size.width;
Packit 1fb8d4
        titleHeight = titleLabel.bounds.size.height;
Packit 1fb8d4
        titleTop = kVerticalPadding;
Packit 1fb8d4
        titleLeft = imageLeft + imageWidth + kHorizontalPadding;
Packit 1fb8d4
    } else {
Packit 1fb8d4
        titleWidth = titleHeight = titleTop = titleLeft = 0;
Packit 1fb8d4
    }
Packit 1fb8d4
    
Packit 1fb8d4
    // messageLabel frame values
Packit 1fb8d4
    float messageWidth, messageHeight, messageLeft, messageTop;
Packit 1fb8d4
Packit 1fb8d4
    if(messageLabel != nil) {
Packit 1fb8d4
        messageWidth = messageLabel.bounds.size.width;
Packit 1fb8d4
        messageHeight = messageLabel.bounds.size.height;
Packit 1fb8d4
        messageLeft = imageLeft + imageWidth + kHorizontalPadding;
Packit 1fb8d4
        messageTop = titleTop + titleHeight + kVerticalPadding;
Packit 1fb8d4
    } else {
Packit 1fb8d4
        messageWidth = messageHeight = messageLeft = messageTop = 0;
Packit 1fb8d4
    }
Packit 1fb8d4
    
Packit 1fb8d4
    // compare the title & message widths and use the longer value to calculate the size of the wrapper width
Packit 1fb8d4
    // the same logic applies to the x value (left)
Packit 1fb8d4
    float longerWidth = (messageWidth < titleWidth) ? titleWidth : messageWidth;
Packit 1fb8d4
    float longerLeft = (messageLeft < titleLeft) ? titleLeft : messageLeft;
Packit 1fb8d4
    
Packit 1fb8d4
    // if the image width is larger than longerWidth, use the image width to calculate the wrapper width.
Packit 1fb8d4
    // the same logic applies to the wrapper height
Packit 1fb8d4
    float wrapperWidth = ((longerLeft + longerWidth + kHorizontalPadding) < imageWidth + (kHorizontalPadding * 2)) ? imageWidth + (kHorizontalPadding * 2) : (longerLeft + longerWidth + kHorizontalPadding);
Packit 1fb8d4
    float wrapperHeight = ((messageTop + messageHeight + kVerticalPadding) < imageHeight + (kVerticalPadding * 2)) ? imageHeight + (kVerticalPadding * 2) : (messageTop + messageHeight + kVerticalPadding);
Packit 1fb8d4
                         
Packit 1fb8d4
    [wrapperView setFrame:CGRectMake(0, 0, wrapperWidth, wrapperHeight)];
Packit 1fb8d4
    
Packit 1fb8d4
    if(titleLabel != nil) {
Packit 1fb8d4
        [titleLabel setFrame:CGRectMake(titleLeft, titleTop, titleWidth, titleHeight)];
Packit 1fb8d4
        [wrapperView addSubview:titleLabel];
Packit 1fb8d4
    }
Packit 1fb8d4
    
Packit 1fb8d4
    if(messageLabel != nil) {
Packit 1fb8d4
        [messageLabel setFrame:CGRectMake(messageLeft, messageTop, messageWidth, messageHeight)];
Packit 1fb8d4
        [wrapperView addSubview:messageLabel];
Packit 1fb8d4
    }
Packit 1fb8d4
    
Packit 1fb8d4
    if(imageView != nil) {
Packit 1fb8d4
        [wrapperView addSubview:imageView];
Packit 1fb8d4
    }
Packit 1fb8d4
    
Packit 1fb8d4
    return wrapperView;
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
@end