在iOS开发过程中经常需要实现这样一个需求在某个按钮上方添加一个小红点,或者是加一个带数字的消息提醒红点,如下图所示的效果:
类似上面的效果如何实现呢,其实早有大神给造出了轮子,前人栽树,后人乘凉,我们只用几行代码就可以搞定了
创建文件 UIButton+Badge.h UIButton+Badge.m
1、在UIButton+Badge.h 中导入如下代码
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface UIButton (Badge)
@property (strong, nonatomic) UILabel *badge;
// Badge value to be display
@property (nonatomic) NSString *badgeValue;
// Badge background color
@property (nonatomic) UIColor *badgeBGColor;
// Badge text color
@property (nonatomic) UIColor *badgeTextColor;
// Badge font
@property (nonatomic) UIFont *badgeFont;
// Padding value for the badge
@property (nonatomic) CGFloat badgePadding;
// Minimum size badge to small
@property (nonatomic) CGFloat badgeMinSize;
// Values for offseting the badge over the BarButtonItem you picked
@property (nonatomic) CGFloat badgeOriginX;
@property (nonatomic) CGFloat badgeOriginY;
// In case of numbers, remove the badge when reaching zero
@property BOOL shouldHideBadgeAtZero;
// Badge has a bounce animation when value changes
@property BOOL shouldAnimateBadge;
@end
NS_ASSUME_NONNULL_END
2、在UIButton+Badge.m 中导入如下代码
#import <objc/runtime.h>
#import "UIButton+Badge.h"
NSString const *UIButton_badgeKey = @"UIButton_badgeKey";
NSString const *UIButton_badgeBGColorKey = @"UIButton_badgeBGColorKey";
NSString const *UIButton_badgeTextColorKey = @"UIButton_badgeTextColorKey";
NSString const *UIButton_badgeFontKey = @"UIButton_badgeFontKey";
NSString const *UIButton_badgePaddingKey = @"UIButton_badgePaddingKey";
NSString const *UIButton_badgeMinSizeKey = @"UIButton_badgeMinSizeKey";
NSString const *UIButton_badgeOriginXKey = @"UIButton_badgeOriginXKey";
NSString const *UIButton_badgeOriginYKey = @"UIButton_badgeOriginYKey";
NSString const *UIButton_shouldHideBadgeAtZeroKey = @"UIButton_shouldHideBadgeAtZeroKey";
NSString const *UIButton_shouldAnimateBadgeKey = @"UIButton_shouldAnimateBadgeKey";
NSString const *UIButton_badgeValueKey = @"UIButton_badgeValueKey";
@implementation UIButton (Badge)
@dynamic badgeValue, badgeBGColor, badgeTextColor, badgeFont;
@dynamic badgePadding, badgeMinSize, badgeOriginX, badgeOriginY;
@dynamic shouldHideBadgeAtZero, shouldAnimateBadge;
- (void)badgeInit
{
// Default design initialization
self.badgeBGColor = [UIColor redColor];
self.badgeTextColor = [UIColor whiteColor];
self.badgeFont = [UIFont systemFontOfSize:12.0];
self.badgePadding = 6;
self.badgeMinSize = 8;
self.badgeOriginX = self.frame.size.width - self.badge.frame.size.width/2;
self.badgeOriginY = -4;
self.shouldHideBadgeAtZero = YES;
self.shouldAnimateBadge = YES;
// Avoids badge to be clipped when animating its scale
self.clipsToBounds = NO;
}
#pragma mark - Utility methods
// Handle badge display when its properties have been changed (color, font, ...)
- (void)refreshBadge
{
// Change new attributes
self.badge.textColor = self.badgeTextColor;
self.badge.backgroundColor = self.badgeBGColor;
self.badge.font = self.badgeFont;
}
- (CGSize) badgeExpectedSize
{
// When the value changes the badge could need to get bigger
// Calculate expected size to fit new value
// Use an intermediate label to get expected size thanks to sizeToFit
// We don't call sizeToFit on the true label to avoid bad display
UILabel *frameLabel = [self duplicateLabel:self.badge];
[frameLabel sizeToFit];
CGSize expectedLabelSize = frameLabel.frame.size;
return expectedLabelSize;
}
- (void)updateBadgeFrame
{
CGSize expectedLabelSize = [self badgeExpectedSize];
// Make sure that for small value, the badge will be big enough
CGFloat minHeight = expectedLabelSize.height;
// Using a const we make sure the badge respect the minimum size
minHeight = (minHeight < self.badgeMinSize) ? self.badgeMinSize : expectedLabelSize.height;
CGFloat minWidth = expectedLabelSize.width;
CGFloat padding = self.badgePadding;
// Using const we make sure the badge doesn't get too smal
minWidth = (minWidth < minHeight) ? minHeight : expectedLabelSize.width;
self.badge.frame = CGRectMake(self.badgeOriginX, self.badgeOriginY, minWidth + padding, minHeight + padding);
self.badge.layer.cornerRadius = (minHeight + padding) / 2;
self.badge.layer.masksToBounds = YES;
}
// Handle the badge changing value
- (void)updateBadgeValueAnimated:(BOOL)animated
{
// Bounce animation on badge if value changed and if animation authorized
if (animated && self.shouldAnimateBadge && ![self.badge.text isEqualToString:self.badgeValue]) {
CABasicAnimation * animation = [CABasicAnimation animationWithKeyPath:@"transform.scale"];
[animation setFromValue:[NSNumber numberWithFloat:1.5]];
[animation setToValue:[NSNumber numberWithFloat:1]];
[animation setDuration:0.2];
[animation setTimingFunction:[CAMediaTimingFunction functionWithControlPoints:.4f :1.3f :1.f :1.f]];
[self.badge.layer addAnimation:animation forKey:@"bounceAnimation"];
}
// Set the new value
self.badge.text = self.badgeValue;
// Animate the size modification if needed
NSTimeInterval duration = animated ? 0.2 : 0;
[UIView animateWithDuration:duration animations:^{
[self updateBadgeFrame];
}];
}
- (UILabel *)duplicateLabel:(UILabel *)labelToCopy
{
UILabel *duplicateLabel = [[UILabel alloc] initWithFrame:labelToCopy.frame];
duplicateLabel.text = labelToCopy.text;
duplicateLabel.font = labelToCopy.font;
return duplicateLabel;
}
- (void)removeBadge
{
// Animate badge removal
[UIView animateWithDuration:0.2 animations:^{
self.badge.transform = CGAffineTransformMakeScale(0, 0);
} completion:^(BOOL finished) {
[self.badge removeFromSuperview];
self.badge = nil;
}];
}
#pragma mark - getters/setters
-(UILabel*) badge {
return objc_getAssociatedObject(self, &UIButton_badgeKey);
}
-(void)setBadge:(UILabel *)badgeLabel
{
objc_setAssociatedObject(self, &UIButton_badgeKey, badgeLabel, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
// Badge value to be display
-(NSString *)badgeValue {
return objc_getAssociatedObject(self, &UIButton_badgeValueKey);
}
-(void) setBadgeValue:(NSString *)badgeValue
{
objc_setAssociatedObject(self, &UIButton_badgeValueKey, badgeValue, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
// When changing the badge value check if we need to remove the badge
if (!badgeValue || [badgeValue isEqualToString:@""] || ([badgeValue isEqualToString:@"0"] && self.shouldHideBadgeAtZero)) {
[self removeBadge];
} else if (!self.badge) {
// Create a new badge because not existing
self.badge = [[UILabel alloc] initWithFrame:CGRectMake(self.badgeOriginX, self.badgeOriginY, 20, 20)];
self.badge.textColor = self.badgeTextColor;
self.badge.backgroundColor = self.badgeBGColor;
self.badge.font = self.badgeFont;
self.badge.textAlignment = NSTextAlignmentCenter;
[self badgeInit];
[self addSubview:self.badge];
[self updateBadgeValueAnimated:NO];
} else {
[self updateBadgeValueAnimated:YES];
}
}
// Badge background color
-(UIColor *)badgeBGColor {
return objc_getAssociatedObject(self, &UIButton_badgeBGColorKey);
}
-(void)setBadgeBGColor:(UIColor *)badgeBGColor
{
objc_setAssociatedObject(self, &UIButton_badgeBGColorKey, badgeBGColor, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
if (self.badge) {
[self refreshBadge];
}
}
// Badge text color
-(UIColor *)badgeTextColor {
return objc_getAssociatedObject(self, &UIButton_badgeTextColorKey);
}
-(void)setBadgeTextColor:(UIColor *)badgeTextColor
{
objc_setAssociatedObject(self, &UIButton_badgeTextColorKey, badgeTextColor, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
if (self.badge) {
[self refreshBadge];
}
}
// Badge font
-(UIFont *)badgeFont {
return objc_getAssociatedObject(self, &UIButton_badgeFontKey);
}
-(void)setBadgeFont:(UIFont *)badgeFont
{
objc_setAssociatedObject(self, &UIButton_badgeFontKey, badgeFont, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
if (self.badge) {
[self refreshBadge];
}
}
// Padding value for the badge
-(CGFloat) badgePadding {
NSNumber *number = objc_getAssociatedObject(self, &UIButton_badgePaddingKey);
return number.floatValue;
}
-(void) setBadgePadding:(CGFloat)badgePadding
{
NSNumber *number = [NSNumber numberWithDouble:badgePadding];
objc_setAssociatedObject(self, &UIButton_badgePaddingKey, number, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
if (self.badge) {
[self updateBadgeFrame];
}
}
// Minimum size badge to small
-(CGFloat) badgeMinSize {
NSNumber *number = objc_getAssociatedObject(self, &UIButton_badgeMinSizeKey);
return number.floatValue;
}
-(void) setBadgeMinSize:(CGFloat)badgeMinSize
{
NSNumber *number = [NSNumber numberWithDouble:badgeMinSize];
objc_setAssociatedObject(self, &UIButton_badgeMinSizeKey, number, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
if (self.badge) {
[self updateBadgeFrame];
}
}
// Values for offseting the badge over the BarButtonItem you picked
-(CGFloat) badgeOriginX {
NSNumber *number = objc_getAssociatedObject(self, &UIButton_badgeOriginXKey);
return number.floatValue;
}
-(void) setBadgeOriginX:(CGFloat)badgeOriginX
{
NSNumber *number = [NSNumber numberWithDouble:badgeOriginX];
objc_setAssociatedObject(self, &UIButton_badgeOriginXKey, number, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
if (self.badge) {
[self updateBadgeFrame];
}
}
-(CGFloat) badgeOriginY {
NSNumber *number = objc_getAssociatedObject(self, &UIButton_badgeOriginYKey);
return number.floatValue;
}
-(void) setBadgeOriginY:(CGFloat)badgeOriginY
{
NSNumber *number = [NSNumber numberWithDouble:badgeOriginY];
objc_setAssociatedObject(self, &UIButton_badgeOriginYKey, number, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
if (self.badge) {
[self updateBadgeFrame];
}
}
// In case of numbers, remove the badge when reaching zero
-(BOOL) shouldHideBadgeAtZero {
NSNumber *number = objc_getAssociatedObject(self, &UIButton_shouldHideBadgeAtZeroKey);
return number.boolValue;
}
- (void)setShouldHideBadgeAtZero:(BOOL)shouldHideBadgeAtZero
{
NSNumber *number = [NSNumber numberWithBool:shouldHideBadgeAtZero];
objc_setAssociatedObject(self, &UIButton_shouldHideBadgeAtZeroKey, number, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
// Badge has a bounce animation when value changes
-(BOOL) shouldAnimateBadge {
NSNumber *number = objc_getAssociatedObject(self, &UIButton_shouldAnimateBadgeKey);
return number.boolValue;
}
- (void)setShouldAnimateBadge:(BOOL)shouldAnimateBadge
{
NSNumber *number = [NSNumber numberWithBool:shouldAnimateBadge];
objc_setAssociatedObject(self, &UIButton_shouldAnimateBadgeKey, number, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
@end
3、导入UIButton+Badge.h
添加如下代码即可
//
btn.badgeValue=@"0";
//边距尺寸
btn.badgePadding=-5;
//尺寸
btn.badgeMinSize=0;
//背景颜色
btn.badgeBGColor=[UIColor redColor];
//字体颜色
btn.badgeTextColor=[UIColor redColor];
//x坐标值
btn.badgeOriginX=30;
//y坐标值
btn.badgeOriginY=30;