iOSのicon badgeを作ってみた

#概要
iOSのtabはbadge表示できるが、UIViewにはbadgeのAPIがないので、core animation使って簡単に作ってみました。
CAShapeLayer で丸い円をbadgeの円を描く. CATextLayerで数字を表示するだけ。badgeNumberを0に設定した時に
fade out効果もつけました

#code

```objc:UIView+Badge.h

@interface UIView (Badge)

/**
 * viewにbadgeを追加する 0<badgeNumber<=99
 * 注:0は表示しない.最大99まで表示、99超えた場合は99を表示する
 */
- (void)setBadgeNumber:(NSUInteger)badgeNumber;

@end
```

```objc:UIView+Badge.m
#import "UIView+Badge.h"
#import <objc/runtime.h>

static CGFloat const adjustPosition = 0.2f;
static CGFloat const kBorderWidth = 1;
static CGFloat const kBackgroundCircleWidth = 20;
static CGFloat const kCircleCornerRadius = 100;

static CGFloat const kBadgeFontSize = 15;

static NSUInteger const kBadgeNumberLimit = 99;

static const char *kUIViewBadgeLayerKey = "UIViewBadgeLayerKey";
static const char *kUIViewBadgeTextLayerKey = "UIViewBadgeTextLayerKey";


@implementation UIView (Badge)

- (void)setBadgeNumber:(NSUInteger)badgeNumber {
    if (badgeNumber == 0) { // badge layerを削除
        [self p_deleteBadgeLayers];
        return;
    }
    CATextLayer *badgeTextLayer = [self p_badgeTextLayer];

    if (!badgeTextLayer) {
        [self p_configureBadgeLayers];
        badgeTextLayer = [self p_badgeTextLayer];
    }

    badgeNumber = MIN(badgeNumber, kBadgeNumberLimit);
    badgeTextLayer.string = @(badgeNumber).stringValue;
}

- (void)p_configureBadgeLayers {
    // badgeのborder layer 生成
    CAShapeLayer *borderLayer = [CAShapeLayer layer];
    borderLayer.bounds = CGRectMake(0, 0, kBorderWidth, kBorderWidth);
    // view内の位置調整
    borderLayer.path = [UIBezierPath bezierPathWithRoundedRect:borderLayer.bounds
                                                  cornerRadius:kCircleCornerRadius].CGPath;
    borderLayer.fillColor = [UIColor blackColor].CGColor;

    // badgeの丸いbackground layer生成
    CAShapeLayer *backgroundLayer = [CAShapeLayer layer];
    backgroundLayer.bounds = CGRectMake(0, 0, kBackgroundCircleWidth, kBackgroundCircleWidth);

    backgroundLayer.path = [UIBezierPath bezierPathWithRoundedRect:backgroundLayer.bounds
                                                      cornerRadius:kCircleCornerRadius].CGPath;
    backgroundLayer.fillColor = [UIColor redColor].CGColor;

    // badge数字を表示するためのtext layer生成
    CATextLayer *textLayer = [CATextLayer layer];
    textLayer.frame = backgroundLayer.frame;
    textLayer.contentsScale = [UIScreen mainScreen].scale;
    textLayer.wrapped = YES;

    UIFont *font = [UIFont systemFontOfSize:kBadgeFontSize];
    CGFontRef fontRef = CGFontCreateWithFontName((__bridge CFStringRef)font.fontName);
    textLayer.font = fontRef;
    CFRelease(fontRef);
    textLayer.fontSize = font.pointSize;
    textLayer.alignmentMode = kCAAlignmentCenter;

    CALayer *badgeLayer = [CALayer layer];
    badgeLayer.bounds = CGRectMake(0, 0, kBorderWidth, kBorderWidth);
    // view内の位置調整
    CGFloat positionAdjustment = adjustPosition * self.frame.size.width;
    badgeLayer.position = CGPointMake(self.frame.size.width - positionAdjustment, positionAdjustment);

    [badgeLayer addSublayer:borderLayer];
    [badgeLayer addSublayer:backgroundLayer];
    [badgeLayer addSublayer:textLayer];

    [self.layer addSublayer:badgeLayer];

    [self p_setBadgeLayer:badgeLayer];
    [self p_setBadgeTextLayer:textLayer];
}

- (void)p_deleteBadgeLayers {
    if ([self p_badgeLayer]) {
        CALayer *badgeLayer = [self p_badgeLayer];
        CABasicAnimation *opacityAnimation = [CABasicAnimation animationWithKeyPath:@"opacity"];
        [opacityAnimation setToValue:[NSNumber numberWithFloat:0.0]];
        [opacityAnimation setFromValue:[NSNumber numberWithFloat:1.0]];
        [opacityAnimation setDuration:0.2];
        [opacityAnimation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn]];
        opacityAnimation.delegate = self;

        badgeLayer.opacity = 0.0;
        [badgeLayer addAnimation:opacityAnimation forKey:@"opacityAnimation"];
    }
}

- (void)p_setBadgeLayer:(CALayer *)badgeLayer {
    objc_setAssociatedObject(self, kUIViewBadgeLayerKey, badgeLayer, OBJC_ASSOCIATION_ASSIGN);
}

- (CALayer *)p_badgeLayer {
    return objc_getAssociatedObject(self, kUIViewBadgeLayerKey);
}

- (void)p_setBadgeTextLayer:(CATextLayer *)badgeTextLayer {
    objc_setAssociatedObject(self, kUIViewBadgeTextLayerKey, badgeTextLayer, OBJC_ASSOCIATION_ASSIGN);
}

- (CATextLayer *)p_badgeTextLayer {
    return objc_getAssociatedObject(self, kUIViewBadgeTextLayerKey);
}

#pragma mark - CAAnimation delegate

- (void)animationDidStop:(CAAnimation *)animation finished:(BOOL)flag {
    CALayer *badgeLayer = [self p_badgeLayer];

    [badgeLayer removeFromSuperlayer];
    [self p_setBadgeLayer:nil];
    [self p_setBadgeTextLayer:nil];
}

@end

```