// // UIView+WZLBadge.m // WZLBadgeDemo // // Created by zilin_weng on 15/6/24. // Copyright (c) 2015年 Weng-Zilin. All rights reserved. // #import "UIView+WZLBadge.h" #import #import "CAAnimation+WAnimation.h" #define kWZLBadgeDefaultFont ([UIFont boldSystemFontOfSize:9]) #define kWZLBadgeDefaultMaximumBadgeNumber 99 static const CGFloat kWZLBadgeDefaultRedDotRadius = 4.f; @implementation UIView (WZLBadge) #pragma mark -- public methods /** * show badge with red dot style and WBadgeAnimTypeNone by default. */ - (void)showBadge { [self showBadgeWithStyle:WBadgeStyleRedDot value:0 animationType:WBadgeAnimTypeNone]; } /** * showBadge * * @param style WBadgeStyle type * @param value (if 'style' is WBadgeStyleRedDot or WBadgeStyleNew, * this value will be ignored. In this case, any value will be ok.) */ - (void)showBadgeWithStyle:(WBadgeStyle)style value:(NSInteger)value animationType:(WBadgeAnimType)aniType { self.aniType = aniType; switch (style) { case WBadgeStyleRedDot: [self showRedDotBadge]; break; case WBadgeStyleNumber: [self showNumberBadgeWithValue:value]; break; case WBadgeStyleNew: [self showNewBadge]; break; default: break; } if (aniType != WBadgeAnimTypeNone) { [self beginAnimation]; } } - (void)showNumberBadgeWithValue:(NSInteger)value animationType:(WBadgeAnimType)aniType { self.aniType = aniType; [self showNumberBadgeWithValue:value]; if (aniType != WBadgeAnimTypeNone) { [self beginAnimation]; } } /** * clear badge */ - (void)clearBadge { self.badge.hidden = YES; } /** * make bage(if existing) not hiden */ - (void)resumeBadge { if (self.badge && self.badge.hidden == YES) { self.badge.hidden = NO; } } #pragma mark -- private methods - (void)showRedDotBadge { [self badgeInit]; //if badge has been displayed and, in addition, is was not red dot style, we must update UI. if (self.badge.tag != WBadgeStyleRedDot) { self.badge.text = @""; self.badge.tag = WBadgeStyleRedDot; [self resetBadgeForRedDot]; self.badge.layer.cornerRadius = CGRectGetWidth(self.badge.frame) / 2; } self.badge.hidden = NO; } - (void)resetBadgeForRedDot { if (self.badgeRadius > 0) { self.badge.frame = CGRectMake(self.badge.center.x - self.badgeRadius, self.badge.center.y + self.badgeRadius , self.badgeRadius * 2, self.badgeRadius *2); } } - (void)showNewBadge { [self badgeInit]; //if badge has been displayed and, in addition, is not red dot style, we must update UI. if (self.badge.tag != WBadgeStyleNew) { self.badge.text = @"new"; self.badge.tag = WBadgeStyleNew; CGRect frame = self.badge.frame; frame.size.width = 22; frame.size.height = 13; self.badge.frame = frame; self.badge.center = CGPointMake(CGRectGetWidth(self.frame) + 2 + self.badgeCenterOffset.x, self.badgeCenterOffset.y); self.badge.font = kWZLBadgeDefaultFont; self.badge.layer.cornerRadius = CGRectGetHeight(self.badge.frame) / 3; } self.badge.hidden = NO; } - (void)showNumberBadgeWithValue:(NSInteger)value { if (value < 0) { return; } [self badgeInit]; self.badge.hidden = (value == 0); self.badge.tag = WBadgeStyleNumber; self.badge.font = self.badgeFont; self.badge.text = (value > self.badgeMaximumBadgeNumber ? [NSString stringWithFormat:@"%@+", @(self.badgeMaximumBadgeNumber)] : [NSString stringWithFormat:@"%@", @(value)]); [self adjustLabelWidth:self.badge]; CGRect frame = self.badge.frame; frame.size.width += 4; frame.size.height += 4; if(CGRectGetWidth(frame) < CGRectGetHeight(frame)) { frame.size.width = CGRectGetHeight(frame); } self.badge.frame = frame; self.badge.center = CGPointMake(CGRectGetWidth(self.frame) + 2 + self.badgeCenterOffset.x, self.badgeCenterOffset.y); self.badge.layer.cornerRadius = CGRectGetHeight(self.badge.frame) / 2; } //lazy loading - (void)badgeInit { if (self.badgeBgColor == nil) { self.badgeBgColor = [UIColor redColor]; } if (self.badgeTextColor == nil) { self.badgeTextColor = [UIColor whiteColor]; } if (nil == self.badge) { CGFloat redotWidth = kWZLBadgeDefaultRedDotRadius *2; CGRect frm = CGRectMake(CGRectGetWidth(self.frame), -redotWidth, redotWidth, redotWidth); self.badge = [[UILabel alloc] initWithFrame:frm]; self.badge.textAlignment = NSTextAlignmentCenter; self.badge.center = CGPointMake(CGRectGetWidth(self.frame) + 2 + self.badgeCenterOffset.x, self.badgeCenterOffset.y); self.badge.backgroundColor = self.badgeBgColor; self.badge.textColor = self.badgeTextColor; self.badge.text = @""; self.badge.tag = WBadgeStyleRedDot;//red dot by default self.badge.layer.cornerRadius = CGRectGetWidth(self.badge.frame) / 2; self.badge.layer.masksToBounds = YES;//very important self.badge.hidden = NO; [self addSubview:self.badge]; [self bringSubviewToFront:self.badge]; } } #pragma mark -- other private methods - (void)adjustLabelWidth:(UILabel *)label { [label setNumberOfLines:0]; NSString *s = label.text; UIFont *font = [label font]; CGSize size = CGSizeMake(320,2000); CGSize labelsize; if (![s respondsToSelector:@selector(boundingRectWithSize:options:attributes:context:)]) { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" labelsize = [s sizeWithFont:font constrainedToSize:size lineBreakMode:NSLineBreakByWordWrapping]; #pragma clang diagnostic pop } else { NSMutableParagraphStyle *style = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; [style setLineBreakMode:NSLineBreakByWordWrapping]; labelsize = [s boundingRectWithSize:size options:(NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading) attributes:@{ NSFontAttributeName:font, NSParagraphStyleAttributeName : style} context:nil].size; } CGRect frame = label.frame; frame.size = CGSizeMake(ceilf(labelsize.width), ceilf(labelsize.height)); [label setFrame:frame]; } #pragma mark -- animation //if u want to add badge animation type, follow steps bellow: //1. go to definition of WBadgeAnimType and add new type //2. go to category of CAAnimation+WAnimation to add new animation interface //3. call that new interface here - (void)beginAnimation { switch(self.aniType) { case WBadgeAnimTypeBreathe: [self.badge.layer addAnimation:[CAAnimation opacityForever_Animation:1.4] forKey:kBadgeBreatheAniKey]; break; case WBadgeAnimTypeShake: [self.badge.layer addAnimation:[CAAnimation shake_AnimationRepeatTimes:CGFLOAT_MAX durTimes:1 forObj:self.badge.layer] forKey:kBadgeShakeAniKey]; break; case WBadgeAnimTypeScale: [self.badge.layer addAnimation:[CAAnimation scaleFrom:1.4 toScale:0.6 durTimes:1 rep:MAXFLOAT] forKey:kBadgeScaleAniKey]; break; case WBadgeAnimTypeBounce: [self.badge.layer addAnimation:[CAAnimation bounce_AnimationRepeatTimes:CGFLOAT_MAX durTimes:1 forObj:self.badge.layer] forKey:kBadgeBounceAniKey]; break; case WBadgeAnimTypeNone: default: break; } } - (void)removeAnimation { if (self.badge) { [self.badge.layer removeAllAnimations]; } } #pragma mark -- setter/getter - (UILabel *)badge { return objc_getAssociatedObject(self, &badgeLabelKey); } - (void)setBadge:(UILabel *)label { objc_setAssociatedObject(self, &badgeLabelKey, label, OBJC_ASSOCIATION_RETAIN); } - (UIFont *)badgeFont { id font = objc_getAssociatedObject(self, &badgeFontKey); return font == nil ? kWZLBadgeDefaultFont : font; } - (void)setBadgeFont:(UIFont *)badgeFont { objc_setAssociatedObject(self, &badgeFontKey, badgeFont, OBJC_ASSOCIATION_RETAIN); if (!self.badge) { [self badgeInit]; } self.badge.font = badgeFont; } - (UIColor *)badgeBgColor { return objc_getAssociatedObject(self, &badgeBgColorKey); } - (void)setBadgeBgColor:(UIColor *)badgeBgColor { objc_setAssociatedObject(self, &badgeBgColorKey, badgeBgColor, OBJC_ASSOCIATION_RETAIN); if (!self.badge) { [self badgeInit]; } self.badge.backgroundColor = badgeBgColor; } - (UIColor *)badgeTextColor { return objc_getAssociatedObject(self, &badgeTextColorKey); } - (void)setBadgeTextColor:(UIColor *)badgeTextColor { objc_setAssociatedObject(self, &badgeTextColorKey, badgeTextColor, OBJC_ASSOCIATION_RETAIN); if (!self.badge) { [self badgeInit]; } self.badge.textColor = badgeTextColor; } - (WBadgeAnimType)aniType { id obj = objc_getAssociatedObject(self, &badgeAniTypeKey); if(obj != nil && [obj isKindOfClass:[NSNumber class]]) { return [obj integerValue]; } else return WBadgeAnimTypeNone; } - (void)setAniType:(WBadgeAnimType)aniType { NSNumber *numObj = @(aniType); objc_setAssociatedObject(self, &badgeAniTypeKey, numObj, OBJC_ASSOCIATION_RETAIN); if (!self.badge) { [self badgeInit]; } [self removeAnimation]; [self beginAnimation]; } - (CGRect)badgeFrame { id obj = objc_getAssociatedObject(self, &badgeFrameKey); if (obj != nil && [obj isKindOfClass:[NSDictionary class]] && [obj count] == 4) { CGFloat x = [obj[@"x"] floatValue]; CGFloat y = [obj[@"y"] floatValue]; CGFloat width = [obj[@"width"] floatValue]; CGFloat height = [obj[@"height"] floatValue]; return CGRectMake(x, y, width, height); } else return CGRectZero; } - (void)setBadgeFrame:(CGRect)badgeFrame { NSDictionary *frameInfo = @{@"x" : @(badgeFrame.origin.x), @"y" : @(badgeFrame.origin.y), @"width" : @(badgeFrame.size.width), @"height" : @(badgeFrame.size.height)}; objc_setAssociatedObject(self, &badgeFrameKey, frameInfo, OBJC_ASSOCIATION_RETAIN); if (!self.badge) { [self badgeInit]; } self.badge.frame = badgeFrame; } - (CGPoint)badgeCenterOffset { id obj = objc_getAssociatedObject(self, &badgeCenterOffsetKey); if (obj != nil && [obj isKindOfClass:[NSDictionary class]] && [obj count] == 2) { CGFloat x = [obj[@"x"] floatValue]; CGFloat y = [obj[@"y"] floatValue]; return CGPointMake(x, y); } else return CGPointZero; } - (void)setBadgeCenterOffset:(CGPoint)badgeCenterOff { NSDictionary *cenerInfo = @{@"x" : @(badgeCenterOff.x), @"y" : @(badgeCenterOff.y)}; objc_setAssociatedObject(self, &badgeCenterOffsetKey, cenerInfo, OBJC_ASSOCIATION_RETAIN); if (!self.badge) { [self badgeInit]; } self.badge.center = CGPointMake(CGRectGetWidth(self.frame) + 2 + badgeCenterOff.x, badgeCenterOff.y); } //badgeRadiusKey - (void)setBadgeRadius:(CGFloat)badgeRadius { objc_setAssociatedObject(self, &badgeRadiusKey, [NSNumber numberWithFloat:badgeRadius], OBJC_ASSOCIATION_RETAIN); if (!self.badge) { [self badgeInit]; } } - (CGFloat)badgeRadius { return [objc_getAssociatedObject(self, &badgeRadiusKey) floatValue]; } - (NSInteger)badgeMaximumBadgeNumber { id obj = objc_getAssociatedObject(self, &badgeMaximumBadgeNumberKey); if(obj != nil && [obj isKindOfClass:[NSNumber class]]) { return [obj integerValue]; } else return kWZLBadgeDefaultMaximumBadgeNumber; } - (void)setBadgeMaximumBadgeNumber:(NSInteger)badgeMaximumBadgeNumber { NSNumber *numObj = @(badgeMaximumBadgeNumber); objc_setAssociatedObject(self, &badgeMaximumBadgeNumberKey, numObj, OBJC_ASSOCIATION_RETAIN); if (!self.badge) { [self badgeInit]; } } @end