UIView+WZLBadge.m 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366
  1. //
  2. // UIView+WZLBadge.m
  3. // WZLBadgeDemo
  4. //
  5. // Created by zilin_weng on 15/6/24.
  6. // Copyright (c) 2015年 Weng-Zilin. All rights reserved.
  7. //
  8. #import "UIView+WZLBadge.h"
  9. #import <objc/runtime.h>
  10. #import "CAAnimation+WAnimation.h"
  11. #define kWZLBadgeDefaultFont ([UIFont boldSystemFontOfSize:9])
  12. #define kWZLBadgeDefaultMaximumBadgeNumber 99
  13. @implementation UIView (WZLBadge)
  14. #pragma mark -- public methods
  15. /**
  16. * show badge with red dot style and WBadgeAnimTypeNone by default.
  17. */
  18. - (void)showBadge
  19. {
  20. [self showBadgeWithStyle:WBadgeStyleRedDot value:0 animationType:WBadgeAnimTypeNone];
  21. }
  22. /**
  23. * showBadge
  24. *
  25. * @param style WBadgeStyle type
  26. * @param value (if 'style' is WBadgeStyleRedDot or WBadgeStyleNew,
  27. * this value will be ignored. In this case, any value will be ok.)
  28. */
  29. - (void)showBadgeWithStyle:(WBadgeStyle)style value:(NSInteger)value animationType:(WBadgeAnimType)aniType
  30. {
  31. self.aniType = aniType;
  32. switch (style) {
  33. case WBadgeStyleRedDot:
  34. [self showRedDotBadge];
  35. break;
  36. case WBadgeStyleNumber:
  37. [self showNumberBadgeWithValue:value];
  38. break;
  39. case WBadgeStyleNew:
  40. [self showNewBadge];
  41. break;
  42. default:
  43. break;
  44. }
  45. if (aniType != WBadgeAnimTypeNone) {
  46. [self beginAnimation];
  47. }
  48. }
  49. /**
  50. * clear badge
  51. */
  52. - (void)clearBadge
  53. {
  54. self.badge.hidden = YES;
  55. }
  56. /**
  57. * make bage(if existing) not hiden
  58. */
  59. - (void)resumeBadge
  60. {
  61. if (self.badge && self.badge.hidden == YES) {
  62. self.badge.hidden = NO;
  63. }
  64. }
  65. #pragma mark -- private methods
  66. - (void)showRedDotBadge
  67. {
  68. [self badgeInit];
  69. //if badge has been displayed and, in addition, is was not red dot style, we must update UI.
  70. if (self.badge.tag != WBadgeStyleRedDot) {
  71. self.badge.text = @"";
  72. self.badge.tag = WBadgeStyleRedDot;
  73. self.badge.layer.cornerRadius = CGRectGetWidth(self.badge.frame) / 2;
  74. }
  75. self.badge.hidden = NO;
  76. }
  77. - (void)showNewBadge
  78. {
  79. [self badgeInit];
  80. //if badge has been displayed and, in addition, is not red dot style, we must update UI.
  81. if (self.badge.tag != WBadgeStyleNew) {
  82. self.badge.text = @"new";
  83. self.badge.tag = WBadgeStyleNew;
  84. CGRect frame = self.badge.frame;
  85. frame.size.width = 22;
  86. frame.size.height = 13;
  87. self.badge.frame = frame;
  88. self.badge.center = CGPointMake(CGRectGetWidth(self.frame) + 2 + self.badgeCenterOffset.x, self.badgeCenterOffset.y);
  89. self.badge.font = kWZLBadgeDefaultFont;
  90. self.badge.layer.cornerRadius = CGRectGetHeight(self.badge.frame) / 3;
  91. }
  92. self.badge.hidden = NO;
  93. }
  94. - (void)showNumberBadgeWithValue:(NSInteger)value
  95. {
  96. if (value < 0) {
  97. return;
  98. }
  99. [self badgeInit];
  100. self.badge.hidden = (value == 0);
  101. self.badge.tag = WBadgeStyleNumber;
  102. self.badge.font = self.badgeFont;
  103. self.badge.text = (value > self.badgeMaximumBadgeNumber ?
  104. [NSString stringWithFormat:@"%@+", @(self.badgeMaximumBadgeNumber)] :
  105. [NSString stringWithFormat:@"%@", @(value)]);
  106. [self adjustLabelWidth:self.badge];
  107. CGRect frame = self.badge.frame;
  108. frame.size.width += 4;
  109. frame.size.height += 4;
  110. if(CGRectGetWidth(frame) < CGRectGetHeight(frame)) {
  111. frame.size.width = CGRectGetHeight(frame);
  112. }
  113. self.badge.frame = frame;
  114. self.badge.center = CGPointMake(CGRectGetWidth(self.frame) + 2 + self.badgeCenterOffset.x, self.badgeCenterOffset.y);
  115. self.badge.layer.cornerRadius = CGRectGetHeight(self.badge.frame) / 2;
  116. }
  117. //lazy loading
  118. - (void)badgeInit
  119. {
  120. if (self.badgeBgColor == nil) {
  121. self.badgeBgColor = [UIColor redColor];
  122. }
  123. if (self.badgeTextColor == nil) {
  124. self.badgeTextColor = [UIColor whiteColor];
  125. }
  126. if (nil == self.badge) {
  127. CGFloat redotWidth = 8;
  128. CGRect frm = CGRectMake(CGRectGetWidth(self.frame), -redotWidth, redotWidth, redotWidth);
  129. self.badge = [[UILabel alloc] initWithFrame:frm];
  130. self.badge.textAlignment = NSTextAlignmentCenter;
  131. self.badge.center = CGPointMake(CGRectGetWidth(self.frame) + 2 + self.badgeCenterOffset.x, self.badgeCenterOffset.y);
  132. self.badge.backgroundColor = self.badgeBgColor;
  133. self.badge.textColor = self.badgeTextColor;
  134. self.badge.text = @"";
  135. self.badge.tag = WBadgeStyleRedDot;//red dot by default
  136. self.badge.layer.cornerRadius = CGRectGetWidth(self.badge.frame) / 2;
  137. self.badge.layer.masksToBounds = YES;//very important
  138. self.badge.hidden = NO;
  139. [self addSubview:self.badge];
  140. [self bringSubviewToFront:self.badge];
  141. }
  142. }
  143. #pragma mark -- other private methods
  144. - (void)adjustLabelWidth:(UILabel *)label
  145. {
  146. [label setNumberOfLines:0];
  147. NSString *s = label.text;
  148. UIFont *font = [label font];
  149. CGSize size = CGSizeMake(320,2000);
  150. CGSize labelsize;
  151. if (![s respondsToSelector:@selector(boundingRectWithSize:options:attributes:context:)]) {
  152. #pragma clang diagnostic push
  153. #pragma clang diagnostic ignored "-Wdeprecated-declarations"
  154. labelsize = [s sizeWithFont:font constrainedToSize:size lineBreakMode:NSLineBreakByWordWrapping];
  155. #pragma clang diagnostic pop
  156. } else {
  157. NSMutableParagraphStyle *style = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
  158. [style setLineBreakMode:NSLineBreakByWordWrapping];
  159. labelsize = [s boundingRectWithSize:size
  160. options:(NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading)
  161. attributes:@{ NSFontAttributeName:font, NSParagraphStyleAttributeName : style}
  162. context:nil].size;
  163. }
  164. CGRect frame = label.frame;
  165. frame.size = CGSizeMake(ceilf(labelsize.width), ceilf(labelsize.height));
  166. [label setFrame:frame];
  167. }
  168. #pragma mark -- animation
  169. //if u want to add badge animation type, follow steps bellow:
  170. //1. go to definition of WBadgeAnimType and add new type
  171. //2. go to category of CAAnimation+WAnimation to add new animation interface
  172. //3. call that new interface here
  173. - (void)beginAnimation
  174. {
  175. switch(self.aniType) {
  176. case WBadgeAnimTypeBreathe:
  177. [self.badge.layer addAnimation:[CAAnimation opacityForever_Animation:1.4]
  178. forKey:kBadgeBreatheAniKey];
  179. break;
  180. case WBadgeAnimTypeShake:
  181. [self.badge.layer addAnimation:[CAAnimation shake_AnimationRepeatTimes:CGFLOAT_MAX
  182. durTimes:1
  183. forObj:self.badge.layer]
  184. forKey:kBadgeShakeAniKey];
  185. break;
  186. case WBadgeAnimTypeScale:
  187. [self.badge.layer addAnimation:[CAAnimation scaleFrom:1.4
  188. toScale:0.6
  189. durTimes:1
  190. rep:MAXFLOAT]
  191. forKey:kBadgeScaleAniKey];
  192. break;
  193. case WBadgeAnimTypeBounce:
  194. [self.badge.layer addAnimation:[CAAnimation bounce_AnimationRepeatTimes:CGFLOAT_MAX
  195. durTimes:1
  196. forObj:self.badge.layer]
  197. forKey:kBadgeBounceAniKey];
  198. break;
  199. case WBadgeAnimTypeNone:
  200. default:
  201. break;
  202. }
  203. }
  204. - (void)removeAnimation
  205. {
  206. if (self.badge) {
  207. [self.badge.layer removeAllAnimations];
  208. }
  209. }
  210. #pragma mark -- setter/getter
  211. - (UILabel *)badge
  212. {
  213. return objc_getAssociatedObject(self, &badgeLabelKey);
  214. }
  215. - (void)setBadge:(UILabel *)label
  216. {
  217. objc_setAssociatedObject(self, &badgeLabelKey, label, OBJC_ASSOCIATION_RETAIN);
  218. }
  219. - (UIFont *)badgeFont
  220. {
  221. id font = objc_getAssociatedObject(self, &badgeFontKey);
  222. return font == nil ? kWZLBadgeDefaultFont : font;
  223. }
  224. - (void)setBadgeFont:(UIFont *)badgeFont
  225. {
  226. objc_setAssociatedObject(self, &badgeFontKey, badgeFont, OBJC_ASSOCIATION_RETAIN);
  227. if (self.badge) {
  228. self.badge.font = badgeFont;
  229. }
  230. }
  231. - (UIColor *)badgeBgColor
  232. {
  233. return objc_getAssociatedObject(self, &badgeBgColorKey);
  234. }
  235. - (void)setBadgeBgColor:(UIColor *)badgeBgColor
  236. {
  237. objc_setAssociatedObject(self, &badgeBgColorKey, badgeBgColor, OBJC_ASSOCIATION_RETAIN);
  238. if (self.badge) {
  239. self.badge.backgroundColor = badgeBgColor;
  240. }
  241. }
  242. - (UIColor *)badgeTextColor
  243. {
  244. return objc_getAssociatedObject(self, &badgeTextColorKey);
  245. }
  246. - (void)setBadgeTextColor:(UIColor *)badgeTextColor
  247. {
  248. objc_setAssociatedObject(self, &badgeTextColorKey, badgeTextColor, OBJC_ASSOCIATION_RETAIN);
  249. if (self.badge) {
  250. self.badge.textColor = badgeTextColor;
  251. }
  252. }
  253. - (WBadgeAnimType)aniType
  254. {
  255. id obj = objc_getAssociatedObject(self, &badgeAniTypeKey);
  256. if(obj != nil && [obj isKindOfClass:[NSNumber class]])
  257. {
  258. return [obj integerValue];
  259. }
  260. else
  261. return WBadgeAnimTypeNone;
  262. }
  263. - (void)setAniType:(WBadgeAnimType)aniType
  264. {
  265. NSNumber *numObj = @(aniType);
  266. objc_setAssociatedObject(self, &badgeAniTypeKey, numObj, OBJC_ASSOCIATION_RETAIN);
  267. if (self.badge) {
  268. [self removeAnimation];
  269. [self beginAnimation];
  270. }
  271. }
  272. - (CGRect)badgeFrame
  273. {
  274. id obj = objc_getAssociatedObject(self, &badgeFrameKey);
  275. if (obj != nil && [obj isKindOfClass:[NSDictionary class]] && [obj count] == 4) {
  276. CGFloat x = [obj[@"x"] floatValue];
  277. CGFloat y = [obj[@"y"] floatValue];
  278. CGFloat width = [obj[@"width"] floatValue];
  279. CGFloat height = [obj[@"height"] floatValue];
  280. return CGRectMake(x, y, width, height);
  281. } else
  282. return CGRectZero;
  283. }
  284. - (void)setBadgeFrame:(CGRect)badgeFrame
  285. {
  286. NSDictionary *frameInfo = @{@"x" : @(badgeFrame.origin.x), @"y" : @(badgeFrame.origin.y),
  287. @"width" : @(badgeFrame.size.width), @"height" : @(badgeFrame.size.height)};
  288. objc_setAssociatedObject(self, &badgeFrameKey, frameInfo, OBJC_ASSOCIATION_RETAIN);
  289. if (self.badge) {
  290. self.badge.frame = badgeFrame;
  291. }
  292. }
  293. - (CGPoint)badgeCenterOffset
  294. {
  295. id obj = objc_getAssociatedObject(self, &badgeCenterOffsetKey);
  296. if (obj != nil && [obj isKindOfClass:[NSDictionary class]] && [obj count] == 2) {
  297. CGFloat x = [obj[@"x"] floatValue];
  298. CGFloat y = [obj[@"y"] floatValue];
  299. return CGPointMake(x, y);
  300. } else
  301. return CGPointZero;
  302. }
  303. - (void)setBadgeCenterOffset:(CGPoint)badgeCenterOff
  304. {
  305. NSDictionary *cenerInfo = @{@"x" : @(badgeCenterOff.x), @"y" : @(badgeCenterOff.y)};
  306. objc_setAssociatedObject(self, &badgeCenterOffsetKey, cenerInfo, OBJC_ASSOCIATION_RETAIN);
  307. if (self.badge) {
  308. self.badge.center = CGPointMake(CGRectGetWidth(self.frame) + 2 + badgeCenterOff.x, badgeCenterOff.y);
  309. }
  310. }
  311. - (NSInteger)badgeMaximumBadgeNumber {
  312. id obj = objc_getAssociatedObject(self, &badgeMaximumBadgeNumberKey);
  313. if(obj != nil && [obj isKindOfClass:[NSNumber class]])
  314. {
  315. return [obj integerValue];
  316. }
  317. else
  318. return kWZLBadgeDefaultMaximumBadgeNumber;
  319. }
  320. - (void)setBadgeMaximumBadgeNumber:(NSInteger)badgeMaximumBadgeNumber {
  321. NSNumber *numObj = @(badgeMaximumBadgeNumber);
  322. objc_setAssociatedObject(self, &badgeMaximumBadgeNumberKey, numObj, OBJC_ASSOCIATION_RETAIN);
  323. }
  324. @end