Newer
Older
XinYang_IOS / YH_Category / UIView+Toast.m
@zhangfeng zhangfeng on 7 Dec 2023 14 KB 1.8.0
//
//  UIView+Toast.m
//  tinghao
//
//  Created by Luo yuntao on 14-4-21.
//  Copyright © 2019年 YJJ. All rights reserved.
//

#import "UIView+Toast.h"
#import <QuartzCore/QuartzCore.h>
#import <objc/runtime.h>

// 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     = 15.0;
static const CGFloat CSToastCornerRadius        = 3.5;
static const CGFloat CSToastOpacity             = 0.8;
static const CGFloat CSToastFontSize            = 16.0;
static const CGFloat CSToastMaxTitleLines       = 1;
static const CGFloat CSToastMaxMessageLines     = 0;
static const NSTimeInterval CSToastFadeDuration = 0.2;

static const CGFloat CSToastAlpha               = 0.75;

// display duration and position
static const NSString * CSToastDefaultPosition  = @"center";
static const NSTimeInterval CSToastDefaultDuration  = 2;

// image view size
static const CGFloat CSToastImageViewWidth      = 24;
static const CGFloat CSToastImageViewHeight     = 24;

// interaction
static const BOOL CSToastHidesOnTap             = YES;     // excludes activity views

// associative reference keys
static const NSString * CSToastTimerKey         = @"CSToastTimerKey";

@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)makeNetToast:(NSString *)message{
    if (message) {
      [self makeToast:message duration:CSToastDefaultDuration position:@"center" image:[UIImage imageNamed:@"toast_error"]];
    }
}
- (void)makeToast:(NSString *)message {
    if (message && [message length]) {
       [self makeToast:message duration:CSToastDefaultDuration position:CSToastDefaultPosition];
    }
    
}

- (void)makeToast:(NSString *)message duration:(NSTimeInterval)duration position:(id)position {
    if (message) {
        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 {
    if (message) {
        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 {
    if (message) {
        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 {
    if (message) {
        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:)]autorelease];
        [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 - 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 * 0.4);
        }
    } else if ([point isKindOfClass:[NSValue class]]) {
        return [point CGPointValue];
    }
    
    DLog(@"Warning: Invalid position for toast.");
    return [self centerPointForPosition:CSToastDefaultPosition withToast:toast];
}

- (CGSize)sizeForString:(NSString *)string font:(UIFont *)font constrainedToSize:(CGSize)constrainedSize lineBreakMode:(NSLineBreakMode)lineBreakMode {
    
    NSMutableParagraphStyle *paragraphStyle = [[[NSMutableParagraphStyle alloc] init]autorelease];
    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));
    
}

- (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]autorelease];
    wrapperView.autoresizingMask = (UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin);
    
    wrapperView.layer.cornerRadius = CSToastCornerRadius;
    wrapperView.alpha  = CSToastAlpha;
//    wrapperView.backgroundColor=UIColorFromRGB(0x5D5D71);
    wrapperView.backgroundColor = [UIColor colorWithRed:0/255.0 green:0/255.0 blue:0/255.0 alpha:0.70];
    //添加kuan
//    wrapperView.layer.borderColor = [UIColor colorWithRed:255/255.0 green:255/255.0 blue:255/255.0 alpha:0.2].CGColor;
//
//    [wrapperView.layer setBorderWidth:1.0f];
//
//    [wrapperView.layer setMasksToBounds:YES];

    
    
    if(image != nil) {
        
        imageView = [[[UIImageView alloc] initWithImage:image]autorelease];
        imageView.contentMode = UIViewContentModeScaleAspectFit;
//        imageView.frame = CGRectMake(CSToastHorizontalPadding, CSToastVerticalPadding, CSToastImageViewWidth, CSToastImageViewHeight);
        imageView.center = CGPointMake(wrapperView.center.x, imageView.center.y);
        
    }
    
    CGFloat imageWidth, imageHeight, imageLeft,imageTop;
    
    // 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;
        imageTop = CSToastVerticalPadding;
        
    } else {
        imageWidth = imageHeight = imageLeft = imageTop = 0.0;
    }
    
    if (title != nil) {
        titleLabel = [[[UILabel alloc] init]autorelease];
        titleLabel.numberOfLines = CSToastMaxTitleLines;
        titleLabel.font = [UIFont systemFontOfSize:CSToastFontSize];
        titleLabel.textAlignment = NSTextAlignmentCenter;
        titleLabel.lineBreakMode = NSLineBreakByWordWrapping;
        titleLabel.textColor = UIColorFromRGB(0xFFFFFF);
        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]autorelease];
        messageLabel.numberOfLines = CSToastMaxMessageLines;
        messageLabel.font = [UIFont systemFontOfSize:CSToastFontSize];
        messageLabel.lineBreakMode = NSLineBreakByWordWrapping;
        messageLabel.textColor = UIColorFromRGB(0xFFFFFF);
        messageLabel.backgroundColor = [UIColor clearColor];
        messageLabel.alpha = 1.0;
        messageLabel.text = message;
        messageLabel.textAlignment = NSTextAlignmentCenter;
        
        // size the message label according to the length of the text
        CGSize maxSizeMessage = CGSizeMake((self.bounds.size.width * CSToastMaxWidth), 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 = 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 =  CSToastHorizontalPadding;
        if (!title) {
            if (!imageView) {
                messageTop = CSToastVerticalPadding;
            }else{
                messageTop = imageHeight + CSToastVerticalPadding + imageTop;
            }
        }else{
            if (!imageView) {
                messageTop = titleTop + titleHeight +CSToastVerticalPadding;
            }else{
                messageTop = imageHeight + CSToastVerticalPadding + imageTop + 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);
        titleLabel.center = CGPointMake(wrapperView.frame.size.width/2, titleTop + titleHeight/2);
        [wrapperView addSubview:titleLabel];
    }
    
    if(messageLabel != nil) {
        if (wrapperWidth - messageWidth > 2*CSToastHorizontalPadding) {
            messageLeft = (wrapperWidth - messageWidth)/2;
        }
        messageLabel.frame = CGRectMake(messageLeft, messageTop, messageWidth, messageHeight);
        [wrapperView addSubview:messageLabel];
    }
    
    if(imageView != nil) {
        imageView.center = CGPointMake(wrapperView.frame.size.width/2, imageTop + imageHeight/2);
        [wrapperView addSubview:imageView];
    }
    return wrapperView;
}

- (void)makeToast:(NSString *)message andPostion:(PositionSite)site{
    if (site == 0) {
        [self makeToast:message duration:CSToastDefaultDuration position:@"top"];
    }else if (site == 1){
        [self makeToast:message duration:CSToastDefaultDuration position:@"center"];
    }else{
        [self makeToast:message duration:CSToastDefaultDuration position:@"bottom"];
    }
}
@end