LoginSignup
9
9

More than 5 years have passed since last update.

iOSのicon badgeを作ってみた

Posted at

概要

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

code

UIView+Badge.h

@interface UIView (Badge)

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

@end
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

9
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
9