Нет описания

UIView+WZLBadge.m 12KB

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