diff --git a/plugin.xml b/plugin.xml
index 296e9d8..da4ee66 100755
--- a/plugin.xml
+++ b/plugin.xml
@@ -30,10 +30,12 @@
-
-
+
+
+
+
diff --git a/src/ios/Toast+UIView.h b/src/ios/Toast+UIView.h
new file mode 100755
index 0000000..652ef35
--- /dev/null
+++ b/src/ios/Toast+UIView.h
@@ -0,0 +1,51 @@
+/***************************************************************************
+
+Toast+UIView.h
+Toast
+Version 2.2
+
+Copyright (c) 2013 Charles Scalesse.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+***************************************************************************/
+
+
+#import
+
+@interface UIView (Toast)
+
+// each makeToast method creates a view and displays it as toast
+- (void)makeToast:(NSString *)message;
+- (void)makeToast:(NSString *)message duration:(NSTimeInterval)interval position:(id)position;
+- (void)makeToast:(NSString *)message duration:(NSTimeInterval)interval position:(id)position image:(UIImage *)image;
+- (void)makeToast:(NSString *)message duration:(NSTimeInterval)interval position:(id)position title:(NSString *)title;
+- (void)makeToast:(NSString *)message duration:(NSTimeInterval)interval position:(id)position title:(NSString *)title image:(UIImage *)image;
+
+// displays toast with an activity spinner
+- (void)makeToastActivity;
+- (void)makeToastActivity:(id)position;
+- (void)hideToastActivity;
+
+// the showToast methods display any view as toast
+- (void)showToast:(UIView *)toast;
+- (void)showToast:(UIView *)toast duration:(NSTimeInterval)interval position:(id)point;
+
+@end
diff --git a/src/ios/Toast+UIView.m b/src/ios/Toast+UIView.m
new file mode 100755
index 0000000..0c6b97e
--- /dev/null
+++ b/src/ios/Toast+UIView.m
@@ -0,0 +1,363 @@
+//
+// Toast+UIView.m
+// Toast
+// Version 2.2
+//
+// Copyright 2013 Charles Scalesse.
+//
+
+#import "Toast+UIView.h"
+#import
+#import
+
+/*
+ * CONFIGURE THESE VALUES TO ADJUST LOOK & FEEL,
+ * DISPLAY DURATION, ETC.
+ */
+
+// general appearance
+static const CGFloat CSToastMaxWidth = 0.8; // 80% of parent view width
+static const CGFloat CSToastMaxHeight = 0.8; // 80% of parent view height
+static const CGFloat CSToastHorizontalPadding = 10.0;
+static const CGFloat CSToastVerticalPadding = 10.0;
+static const CGFloat CSToastCornerRadius = 10.0;
+static const CGFloat CSToastOpacity = 0.8;
+static const CGFloat CSToastFontSize = 16.0;
+static const CGFloat CSToastMaxTitleLines = 0;
+static const CGFloat CSToastMaxMessageLines = 0;
+static const NSTimeInterval CSToastFadeDuration = 0.2;
+
+// shadow appearance
+static const CGFloat CSToastShadowOpacity = 0.8;
+static const CGFloat CSToastShadowRadius = 6.0;
+static const CGSize CSToastShadowOffset = { 4.0, 4.0 };
+static const BOOL CSToastDisplayShadow = YES;
+
+// display duration and position
+static const NSString * CSToastDefaultPosition = @"bottom";
+static const NSTimeInterval CSToastDefaultDuration = 3.0;
+
+// image view size
+static const CGFloat CSToastImageViewWidth = 80.0;
+static const CGFloat CSToastImageViewHeight = 80.0;
+
+// activity
+static const CGFloat CSToastActivityWidth = 100.0;
+static const CGFloat CSToastActivityHeight = 100.0;
+static const NSString * CSToastActivityDefaultPosition = @"center";
+
+// interaction
+static const BOOL CSToastHidesOnTap = YES; // excludes activity views
+
+// associative reference keys
+static const NSString * CSToastTimerKey = @"CSToastTimerKey";
+static const NSString * CSToastActivityViewKey = @"CSToastActivityViewKey";
+
+@interface UIView (ToastPrivate)
+
+- (void)hideToast:(UIView *)toast;
+- (void)toastTimerDidFinish:(NSTimer *)timer;
+- (void)handleToastTapped:(UITapGestureRecognizer *)recognizer;
+- (CGPoint)centerPointForPosition:(id)position withToast:(UIView *)toast;
+- (UIView *)viewForMessage:(NSString *)message title:(NSString *)title image:(UIImage *)image;
+- (CGSize)sizeForString:(NSString *)string font:(UIFont *)font constrainedToSize:(CGSize)constrainedSize lineBreakMode:(NSLineBreakMode)lineBreakMode;
+
+@end
+
+
+@implementation UIView (Toast)
+
+#pragma mark - Toast Methods
+
+- (void)makeToast:(NSString *)message {
+ [self makeToast:message duration:CSToastDefaultDuration position:CSToastDefaultPosition];
+}
+
+- (void)makeToast:(NSString *)message duration:(NSTimeInterval)duration position:(id)position {
+ UIView *toast = [self viewForMessage:message title:nil image:nil];
+ [self showToast:toast duration:duration position:position];
+}
+
+- (void)makeToast:(NSString *)message duration:(NSTimeInterval)duration position:(id)position title:(NSString *)title {
+ UIView *toast = [self viewForMessage:message title:title image:nil];
+ [self showToast:toast duration:duration position:position];
+}
+
+- (void)makeToast:(NSString *)message duration:(NSTimeInterval)duration position:(id)position image:(UIImage *)image {
+ UIView *toast = [self viewForMessage:message title:nil image:image];
+ [self showToast:toast duration:duration position:position];
+}
+
+- (void)makeToast:(NSString *)message duration:(NSTimeInterval)duration position:(id)position title:(NSString *)title image:(UIImage *)image {
+ UIView *toast = [self viewForMessage:message title:title image:image];
+ [self showToast:toast duration:duration position:position];
+}
+
+- (void)showToast:(UIView *)toast {
+ [self showToast:toast duration:CSToastDefaultDuration position:CSToastDefaultPosition];
+}
+
+- (void)showToast:(UIView *)toast duration:(NSTimeInterval)duration position:(id)point {
+ toast.center = [self centerPointForPosition:point withToast:toast];
+ toast.alpha = 0.0;
+
+ if (CSToastHidesOnTap) {
+ UITapGestureRecognizer *recognizer = [[UITapGestureRecognizer alloc] initWithTarget:toast action:@selector(handleToastTapped:)];
+ [toast addGestureRecognizer:recognizer];
+ toast.userInteractionEnabled = YES;
+ toast.exclusiveTouch = YES;
+ }
+
+ [self addSubview:toast];
+
+ [UIView animateWithDuration:CSToastFadeDuration
+ delay:0.0
+ options:(UIViewAnimationOptionCurveEaseOut | UIViewAnimationOptionAllowUserInteraction)
+ animations:^{
+ toast.alpha = 1.0;
+ } completion:^(BOOL finished) {
+ NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:duration target:self selector:@selector(toastTimerDidFinish:) userInfo:toast repeats:NO];
+ // associate the timer with the toast view
+ objc_setAssociatedObject (toast, &CSToastTimerKey, timer, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
+ }];
+
+}
+
+- (void)hideToast:(UIView *)toast {
+ [UIView animateWithDuration:CSToastFadeDuration
+ delay:0.0
+ options:(UIViewAnimationOptionCurveEaseIn | UIViewAnimationOptionBeginFromCurrentState)
+ animations:^{
+ toast.alpha = 0.0;
+ } completion:^(BOOL finished) {
+ [toast removeFromSuperview];
+ }];
+}
+
+#pragma mark - Events
+
+- (void)toastTimerDidFinish:(NSTimer *)timer {
+ [self hideToast:(UIView *)timer.userInfo];
+}
+
+- (void)handleToastTapped:(UITapGestureRecognizer *)recognizer {
+ NSTimer *timer = (NSTimer *)objc_getAssociatedObject(self, &CSToastTimerKey);
+ [timer invalidate];
+
+ [self hideToast:recognizer.view];
+}
+
+#pragma mark - Toast Activity Methods
+
+- (void)makeToastActivity {
+ [self makeToastActivity:CSToastActivityDefaultPosition];
+}
+
+- (void)makeToastActivity:(id)position {
+ // sanity
+ UIView *existingActivityView = (UIView *)objc_getAssociatedObject(self, &CSToastActivityViewKey);
+ if (existingActivityView != nil) return;
+
+ UIView *activityView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, CSToastActivityWidth, CSToastActivityHeight)];
+ activityView.center = [self centerPointForPosition:position withToast:activityView];
+ activityView.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:CSToastOpacity];
+ activityView.alpha = 0.0;
+ activityView.autoresizingMask = (UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin);
+ activityView.layer.cornerRadius = CSToastCornerRadius;
+
+ if (CSToastDisplayShadow) {
+ activityView.layer.shadowColor = [UIColor blackColor].CGColor;
+ activityView.layer.shadowOpacity = CSToastShadowOpacity;
+ activityView.layer.shadowRadius = CSToastShadowRadius;
+ activityView.layer.shadowOffset = CSToastShadowOffset;
+ }
+
+ UIActivityIndicatorView *activityIndicatorView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
+ activityIndicatorView.center = CGPointMake(activityView.bounds.size.width / 2, activityView.bounds.size.height / 2);
+ [activityView addSubview:activityIndicatorView];
+ [activityIndicatorView startAnimating];
+
+ // associate the activity view with self
+ objc_setAssociatedObject (self, &CSToastActivityViewKey, activityView, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
+
+ [self addSubview:activityView];
+
+ [UIView animateWithDuration:CSToastFadeDuration
+ delay:0.0
+ options:UIViewAnimationOptionCurveEaseOut
+ animations:^{
+ activityView.alpha = 1.0;
+ } completion:nil];
+}
+
+- (void)hideToastActivity {
+ UIView *existingActivityView = (UIView *)objc_getAssociatedObject(self, &CSToastActivityViewKey);
+ if (existingActivityView != nil) {
+ [UIView animateWithDuration:CSToastFadeDuration
+ delay:0.0
+ options:(UIViewAnimationOptionCurveEaseIn | UIViewAnimationOptionBeginFromCurrentState)
+ animations:^{
+ existingActivityView.alpha = 0.0;
+ } completion:^(BOOL finished) {
+ [existingActivityView removeFromSuperview];
+ objc_setAssociatedObject (self, &CSToastActivityViewKey, nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
+ }];
+ }
+}
+
+#pragma mark - Helpers
+
+- (CGPoint)centerPointForPosition:(id)point withToast:(UIView *)toast {
+ if([point isKindOfClass:[NSString class]]) {
+ // convert string literals @"top", @"bottom", @"center", or any point wrapped in an NSValue object into a CGPoint
+ if([point caseInsensitiveCompare:@"top"] == NSOrderedSame) {
+ return CGPointMake(self.bounds.size.width/2, (toast.frame.size.height / 2) + CSToastVerticalPadding);
+ } else if([point caseInsensitiveCompare:@"bottom"] == NSOrderedSame) {
+ return CGPointMake(self.bounds.size.width/2, (self.bounds.size.height - (toast.frame.size.height / 2)) - CSToastVerticalPadding);
+ } else if([point caseInsensitiveCompare:@"center"] == NSOrderedSame) {
+ return CGPointMake(self.bounds.size.width / 2, self.bounds.size.height / 2);
+ }
+ } else if ([point isKindOfClass:[NSValue class]]) {
+ return [point CGPointValue];
+ }
+
+ NSLog(@"Warning: Invalid position for toast.");
+ return [self centerPointForPosition:CSToastDefaultPosition withToast:toast];
+}
+
+- (CGSize)sizeForString:(NSString *)string font:(UIFont *)font constrainedToSize:(CGSize)constrainedSize lineBreakMode:(NSLineBreakMode)lineBreakMode {
+ if ([string respondsToSelector:@selector(boundingRectWithSize:options:attributes:context:)]) {
+ NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
+ paragraphStyle.lineBreakMode = lineBreakMode;
+ NSDictionary *attributes = @{NSFontAttributeName:font, NSParagraphStyleAttributeName:paragraphStyle};
+ CGRect boundingRect = [string boundingRectWithSize:constrainedSize options:NSStringDrawingUsesLineFragmentOrigin attributes:attributes context:nil];
+ return CGSizeMake(ceilf(boundingRect.size.width), ceilf(boundingRect.size.height));
+ }
+
+ return [string sizeWithFont:font constrainedToSize:constrainedSize lineBreakMode:lineBreakMode];
+}
+
+- (UIView *)viewForMessage:(NSString *)message title:(NSString *)title image:(UIImage *)image {
+ // sanity
+ if((message == nil) && (title == nil) && (image == nil)) return nil;
+
+ // dynamically build a toast view with any combination of message, title, & image.
+ UILabel *messageLabel = nil;
+ UILabel *titleLabel = nil;
+ UIImageView *imageView = nil;
+
+ // create the parent view
+ UIView *wrapperView = [[UIView alloc] init];
+ wrapperView.autoresizingMask = (UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin);
+ wrapperView.layer.cornerRadius = CSToastCornerRadius;
+
+ if (CSToastDisplayShadow) {
+ wrapperView.layer.shadowColor = [UIColor blackColor].CGColor;
+ wrapperView.layer.shadowOpacity = CSToastShadowOpacity;
+ wrapperView.layer.shadowRadius = CSToastShadowRadius;
+ wrapperView.layer.shadowOffset = CSToastShadowOffset;
+ }
+
+ wrapperView.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:CSToastOpacity];
+
+ if(image != nil) {
+ imageView = [[UIImageView alloc] initWithImage:image];
+ imageView.contentMode = UIViewContentModeScaleAspectFit;
+ imageView.frame = CGRectMake(CSToastHorizontalPadding, CSToastVerticalPadding, CSToastImageViewWidth, CSToastImageViewHeight);
+ }
+
+ CGFloat imageWidth, imageHeight, imageLeft;
+
+ // the imageView frame values will be used to size & position the other views
+ if(imageView != nil) {
+ imageWidth = imageView.bounds.size.width;
+ imageHeight = imageView.bounds.size.height;
+ imageLeft = CSToastHorizontalPadding;
+ } else {
+ imageWidth = imageHeight = imageLeft = 0.0;
+ }
+
+ if (title != nil) {
+ titleLabel = [[UILabel alloc] init];
+ titleLabel.numberOfLines = CSToastMaxTitleLines;
+ titleLabel.font = [UIFont boldSystemFontOfSize:CSToastFontSize];
+ titleLabel.textAlignment = NSTextAlignmentLeft;
+ titleLabel.lineBreakMode = NSLineBreakByWordWrapping;
+ titleLabel.textColor = [UIColor whiteColor];
+ titleLabel.backgroundColor = [UIColor clearColor];
+ titleLabel.alpha = 1.0;
+ titleLabel.text = title;
+
+ // size the title label according to the length of the text
+ CGSize maxSizeTitle = CGSizeMake((self.bounds.size.width * CSToastMaxWidth) - imageWidth, self.bounds.size.height * CSToastMaxHeight);
+ CGSize expectedSizeTitle = [self sizeForString:title font:titleLabel.font constrainedToSize:maxSizeTitle lineBreakMode:titleLabel.lineBreakMode];
+ titleLabel.frame = CGRectMake(0.0, 0.0, expectedSizeTitle.width, expectedSizeTitle.height);
+ }
+
+ if (message != nil) {
+ messageLabel = [[UILabel alloc] init];
+ messageLabel.numberOfLines = CSToastMaxMessageLines;
+ messageLabel.font = [UIFont systemFontOfSize:CSToastFontSize];
+ messageLabel.lineBreakMode = NSLineBreakByWordWrapping;
+ messageLabel.textColor = [UIColor whiteColor];
+ messageLabel.backgroundColor = [UIColor clearColor];
+ messageLabel.alpha = 1.0;
+ messageLabel.text = message;
+
+ // size the message label according to the length of the text
+ CGSize maxSizeMessage = CGSizeMake((self.bounds.size.width * CSToastMaxWidth) - imageWidth, self.bounds.size.height * CSToastMaxHeight);
+ CGSize expectedSizeMessage = [self sizeForString:message font:messageLabel.font constrainedToSize:maxSizeMessage lineBreakMode:messageLabel.lineBreakMode];
+ messageLabel.frame = CGRectMake(0.0, 0.0, expectedSizeMessage.width, expectedSizeMessage.height);
+ }
+
+ // titleLabel frame values
+ CGFloat titleWidth, titleHeight, titleTop, titleLeft;
+
+ if(titleLabel != nil) {
+ titleWidth = titleLabel.bounds.size.width;
+ titleHeight = titleLabel.bounds.size.height;
+ titleTop = CSToastVerticalPadding;
+ titleLeft = imageLeft + imageWidth + CSToastHorizontalPadding;
+ } else {
+ titleWidth = titleHeight = titleTop = titleLeft = 0.0;
+ }
+
+ // messageLabel frame values
+ CGFloat messageWidth, messageHeight, messageLeft, messageTop;
+
+ if(messageLabel != nil) {
+ messageWidth = messageLabel.bounds.size.width;
+ messageHeight = messageLabel.bounds.size.height;
+ messageLeft = imageLeft + imageWidth + CSToastHorizontalPadding;
+ messageTop = titleTop + titleHeight + CSToastVerticalPadding;
+ } else {
+ messageWidth = messageHeight = messageLeft = messageTop = 0.0;
+ }
+
+ CGFloat longerWidth = MAX(titleWidth, messageWidth);
+ CGFloat longerLeft = MAX(titleLeft, messageLeft);
+
+ // wrapper width uses the longerWidth or the image width, whatever is larger. same logic applies to the wrapper height
+ CGFloat wrapperWidth = MAX((imageWidth + (CSToastHorizontalPadding * 2)), (longerLeft + longerWidth + CSToastHorizontalPadding));
+ CGFloat wrapperHeight = MAX((messageTop + messageHeight + CSToastVerticalPadding), (imageHeight + (CSToastVerticalPadding * 2)));
+
+ wrapperView.frame = CGRectMake(0.0, 0.0, wrapperWidth, wrapperHeight);
+
+ if(titleLabel != nil) {
+ titleLabel.frame = CGRectMake(titleLeft, titleTop, titleWidth, titleHeight);
+ [wrapperView addSubview:titleLabel];
+ }
+
+ if(messageLabel != nil) {
+ messageLabel.frame = CGRectMake(messageLeft, messageTop, messageWidth, messageHeight);
+ [wrapperView addSubview:messageLabel];
+ }
+
+ if(imageView != nil) {
+ [wrapperView addSubview:imageView];
+ }
+
+ return wrapperView;
+}
+
+@end
diff --git a/src/ios/Toast.m b/src/ios/Toast.m
index 7ded471..8cd96ad 100755
--- a/src/ios/Toast.m
+++ b/src/ios/Toast.m
@@ -1,6 +1,6 @@
#import "Toast.h"
-#import // TODO: required?
-#import "lib/iToast.h"
+#import "Toast+UIView.h"
+#import
@implementation Toast
@@ -15,8 +15,10 @@
// TODO pass in
NSInteger drTime = iToastDurationShort;
- [[[[iToast makeText:NSLocalizedString(message, @"")]
- setGravity:grv offsetLeft:0 offsetTop:0] setDuration:drTime] show];
+ [self.view makeToast:@"This is a piece of toast."];
+
+// [[[[iToast makeText:NSLocalizedString(message, @"")]
+// setGravity:grv offsetLeft:0 offsetTop:0] setDuration:drTime] show];
}