425 lines
13 KiB
Objective-C
Executable File
425 lines
13 KiB
Objective-C
Executable File
//
|
|
// 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 <objc/runtime.h>
|
|
#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;
|
|
}
|
|
|
|
- (void)showCustomBadgeWithContent:(NSString *)content
|
|
{
|
|
[self badgeInit];
|
|
self.badge.tag = WBadgeStyleNumber;
|
|
self.badge.font = self.badgeFont;
|
|
self.badge.text = content;
|
|
[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
|