|
|
@@ -0,0 +1,389 @@
|
|
|
+#import "Toast+UIView.h"
|
|
|
+#import <QuartzCore/QuartzCore.h>
|
|
|
+#import <objc/runtime.h>
|
|
|
+
|
|
|
+/*
|
|
|
+ * 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 CSToastTopBottomOffset = 20.0;
|
|
|
+static const CGFloat CSToastCornerRadius = 5.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 = 1.5;
|
|
|
+
|
|
|
+// 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";
|
|
|
+
|
|
|
+static UIView *prevToast = NULL;
|
|
|
+
|
|
|
+@interface UIView (ToastPrivate)
|
|
|
+
|
|
|
+- (void)hideToast:(UIView *)toast;
|
|
|
+- (void)toastTimerDidFinish:(NSTimer *)timer;
|
|
|
+- (void)handleToastTapped:(UITapGestureRecognizer *)recognizer;
|
|
|
+- (CGPoint)centerPointForPosition:(id)position withToast:(UIView *)toast withAddedPixelsY:(int) addPixelsY;
|
|
|
+- (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 addPixelsY:(int)addPixelsY {
|
|
|
+ UIView *toast = [self viewForMessage:message title:nil image:nil];
|
|
|
+ [self showToast:toast duration:duration position:position addedPixelsY:addPixelsY];
|
|
|
+}
|
|
|
+
|
|
|
+- (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 {
|
|
|
+ [self showToast:toast duration:CSToastDefaultDuration position:CSToastDefaultPosition addedPixelsY:0];
|
|
|
+}
|
|
|
+
|
|
|
+- (void)showToast:(UIView *)toast duration:(NSTimeInterval)duration position:(id)point addedPixelsY:(int) addPixelsY {
|
|
|
+ [self hideToast];
|
|
|
+ prevToast = toast;
|
|
|
+ toast.center = [self centerPointForPosition:point withToast:toast withAddedPixelsY:addPixelsY];
|
|
|
+ toast.alpha = 0.0;
|
|
|
+
|
|
|
+ if (CSToastHidesOnTap) {
|
|
|
+ UITapGestureRecognizer *recognizer = [[UITapGestureRecognizer alloc] initWithTarget:toast action:@selector(handleToastTapped:)];
|
|
|
+ [toast addGestureRecognizer:recognizer];
|
|
|
+ toast.userInteractionEnabled = YES;
|
|
|
+ toast.exclusiveTouch = YES;
|
|
|
+ }
|
|
|
+
|
|
|
+ // make sure that if InAppBrowser is active, we're still showing Toasts on top of it
|
|
|
+ UIViewController *vc = [self getTopMostViewController];
|
|
|
+ UIView *v = [vc view];
|
|
|
+ [v addSubview:toast];
|
|
|
+
|
|
|
+ [UIView animateWithDuration:CSToastFadeDuration
|
|
|
+ delay:0.0
|
|
|
+ options:(UIViewAnimationOptionCurveEaseOut | UIViewAnimationOptionAllowUserInteraction)
|
|
|
+ animations:^{
|
|
|
+ toast.alpha = CSToastOpacity;
|
|
|
+ } 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);
|
|
|
+ }];
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+- (UIViewController*) getTopMostViewController {
|
|
|
+ UIViewController *presentingViewController = [[[UIApplication sharedApplication] delegate] window].rootViewController;
|
|
|
+ while (presentingViewController.presentedViewController != nil) {
|
|
|
+ presentingViewController = presentingViewController.presentedViewController;
|
|
|
+ }
|
|
|
+ return presentingViewController;
|
|
|
+}
|
|
|
+
|
|
|
+- (void)hideToast {
|
|
|
+ if (prevToast){
|
|
|
+ [self hideToast:prevToast];
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+- (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 withAddedPixelsY:0];
|
|
|
+ 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 withAddedPixelsY:(int) addPixelsY {
|
|
|
+ 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) + addPixelsY + CSToastVerticalPadding + CSToastTopBottomOffset);
|
|
|
+ } else if([point caseInsensitiveCompare:@"bottom"] == NSOrderedSame) {
|
|
|
+ return CGPointMake(self.bounds.size.width/2, (self.bounds.size.height - (toast.frame.size.height / 2)) - CSToastVerticalPadding - CSToastTopBottomOffset + addPixelsY);
|
|
|
+ } else if([point caseInsensitiveCompare:@"center"] == NSOrderedSame) {
|
|
|
+ return CGPointMake(self.bounds.size.width / 2, (self.bounds.size.height / 2) + addPixelsY);
|
|
|
+ }
|
|
|
+ } else if ([point isKindOfClass:[NSValue class]]) {
|
|
|
+ return [point CGPointValue];
|
|
|
+ }
|
|
|
+
|
|
|
+ NSLog(@"Warning: Invalid position for toast.");
|
|
|
+ return [self centerPointForPosition:CSToastDefaultPosition withToast:toast withAddedPixelsY:addPixelsY];
|
|
|
+}
|
|
|
+
|
|
|
+- (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));
|
|
|
+ }
|
|
|
+
|
|
|
+#pragma clang diagnostic push
|
|
|
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
|
|
+ return [string sizeWithFont:font constrainedToSize:constrainedSize lineBreakMode:lineBreakMode];
|
|
|
+#pragma clang diagnostic pop
|
|
|
+}
|
|
|
+
|
|
|
+- (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
|