#import "TGCheckButtonView.h" #import "TGColor.h" @interface TGCheckButtonView () { UIView *_wrapperView; CALayer *_checkBackground; TGCheckButtonStyle _style; CGSize _size; bool _borderOnTop; UIImage *_fillImage; UIView *_checkView; UIImageView *_checkFillView; UIView *_checkShortFragment; UIView *_checkLongFragment; } @end @implementation TGCheckButtonView static CGAffineTransform TGCheckButtonDefaultTransform; - (instancetype)initWithStyle:(TGCheckButtonStyle)style { CGSize size = CGSizeMake(32.0f, 32.0f); if (style == TGCheckButtonStyleGallery) size = CGSizeMake(42.0f, 42.0f); self = [super initWithFrame:CGRectMake(0, 0, size.width, size.height)]; if (self != nil) { static dispatch_once_t onceToken; static NSMutableDictionary *backgroundImages; static NSMutableDictionary *fillImages; dispatch_once(&onceToken, ^ { TGCheckButtonDefaultTransform = CGAffineTransformMakeRotation(-M_PI_4); backgroundImages = [[NSMutableDictionary alloc] init]; fillImages = [[NSMutableDictionary alloc] init]; }); bool borderOnTop = false; CGFloat insideInset = 0.0f; switch (style) { case TGCheckButtonStyleGallery: { insideInset = 3.5f; borderOnTop = true; } case TGCheckButtonStyleMedia: { insideInset = 4.0f; borderOnTop = true; } break; case TGCheckButtonStyleBar: { insideInset = 2.0f; } break; default: { insideInset = 5.0f; } break; } UIImage *backgroundImage = backgroundImages[@(style)]; if (backgroundImage == nil) { switch (style) { case TGCheckButtonStyleGallery: { CGRect rect = CGRectMake(0, 0, size.width, size.height); UIGraphicsBeginImageContextWithOptions(rect.size, false, 0); CGContextRef context = UIGraphicsGetCurrentContext(); CGContextSetShadowWithColor(context, CGSizeZero, 2.5f, [UIColor colorWithWhite:0.0f alpha:0.22f].CGColor); CGContextSetLineWidth(context, 1.5f); CGContextSetStrokeColorWithColor(context, [UIColor whiteColor].CGColor); CGContextStrokeEllipseInRect(context, CGRectInset(rect, insideInset + 0.5f, insideInset + 0.5f)); backgroundImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); } break; case TGCheckButtonStyleMedia: { CGRect rect = CGRectMake(0, 0, size.width, size.height); UIGraphicsBeginImageContextWithOptions(rect.size, false, 0); CGContextRef context = UIGraphicsGetCurrentContext(); CGContextSetShadowWithColor(context, CGSizeZero, 2.5f, [UIColor colorWithWhite:0.0f alpha:0.22f].CGColor); CGContextSetLineWidth(context, 1.5f); CGContextSetStrokeColorWithColor(context, [UIColor whiteColor].CGColor); CGContextStrokeEllipseInRect(context, CGRectInset(rect, insideInset + 0.5f, insideInset + 0.5f)); backgroundImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); } break; case TGCheckButtonStyleBar: { CGRect rect = CGRectMake(0, 0, size.width, size.height); UIGraphicsBeginImageContextWithOptions(rect.size, false, 0); CGContextRef context = UIGraphicsGetCurrentContext(); CGContextSetLineWidth(context, 1.0f); CGContextSetStrokeColorWithColor(context, TGColorWithHex(0xcacacf).CGColor); CGContextStrokeEllipseInRect(context, CGRectInset(rect, insideInset + 0.5f, insideInset + 0.5f)); backgroundImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); } break; default: { CGRect rect = CGRectMake(0, 0, size.width, size.height); UIGraphicsBeginImageContextWithOptions(rect.size, false, 0); CGContextRef context = UIGraphicsGetCurrentContext(); CGContextSetLineWidth(context, 1.0f); CGContextSetStrokeColorWithColor(context, TGColorWithHex(0xcacacf).CGColor); CGContextStrokeEllipseInRect(context, CGRectInset(rect, insideInset + 0.5f, insideInset + 0.5f)); backgroundImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); } break; } backgroundImages[@(style)] = backgroundImage; } UIImage *fillImage = fillImages[@(style)]; if (fillImage == nil) { CGRect rect = CGRectMake(0, 0, size.width, size.height); UIGraphicsBeginImageContextWithOptions(rect.size, false, 0); CGContextRef context = UIGraphicsGetCurrentContext(); CGContextSetFillColorWithColor(context, TGColorWithHex(0x29c519).CGColor); CGContextFillEllipseInRect(context, CGRectInset(rect, insideInset, insideInset)); fillImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); fillImages[@(style)] = fillImage; } _style = style; _size = size; _borderOnTop = borderOnTop; _fillImage = fillImage; _wrapperView = [[UIView alloc] initWithFrame:self.bounds]; _wrapperView.userInteractionEnabled = false; [self addSubview:_wrapperView]; _checkBackground = [[CALayer alloc] init]; _checkBackground.contents = (__bridge id)(backgroundImage.CGImage); _checkBackground.frame = CGRectMake(0.0f, 0.0f, size.width, size.height); [_wrapperView.layer addSublayer:_checkBackground]; } return self; } - (void)_createCheckButtonDetailsIfNeeded { if (_checkFillView != nil) return; if (_borderOnTop) [_checkBackground removeFromSuperlayer]; _checkFillView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, _size.width, _size.height)]; _checkFillView.alpha = 0.0f; _checkFillView.image = _fillImage; _checkFillView.transform = CGAffineTransformMakeScale(0.01f, 0.01f); [_wrapperView addSubview:_checkFillView]; _checkView = [[UIView alloc] initWithFrame:CGRectInset(self.bounds, 4, 4)]; _checkView.alpha = 0.0f; _checkView.userInteractionEnabled = false; _checkView.transform = TGCheckButtonDefaultTransform; [_wrapperView addSubview:_checkView]; CGRect shortFragmentFrame = CGRectMake(6.5f, 8.5f, 1.5f, 6.0f); CGRect longFragmentFrame = CGRectMake(7.5f, 13.0f, 11.0f, 1.5f); if (_style == TGCheckButtonStyleGallery) { shortFragmentFrame = CGRectMake(9.0f, 12.5f, 2.0f, 8.0f); longFragmentFrame = CGRectMake(9.5f, 18.5f, 17.0f, 2.0f); } _checkShortFragment = [[UIView alloc] init]; _checkShortFragment.backgroundColor = [UIColor whiteColor]; _checkShortFragment.layer.anchorPoint = CGPointMake(0.5f, 0); _checkShortFragment.frame = shortFragmentFrame; _checkShortFragment.transform = CGAffineTransformMakeScale(1.0f, 0.0f); [_checkView addSubview:_checkShortFragment]; _checkLongFragment = [[UIView alloc] init]; _checkLongFragment.backgroundColor = [UIColor whiteColor]; _checkLongFragment.layer.anchorPoint = CGPointMake(0, 0.5f); _checkLongFragment.frame = longFragmentFrame; _checkLongFragment.transform = CGAffineTransformMakeScale(0.0f, 1.0f); [_checkView addSubview:_checkLongFragment]; if (_borderOnTop) [_wrapperView.layer addSublayer:_checkBackground]; } - (void)setSelected:(bool)selected animated:(bool)animated { [self setSelected:selected animated:animated bump:false]; } - (void)setSelected:(bool)selected animated:(bool)animated bump:(bool)bump { if (selected) [self _createCheckButtonDetailsIfNeeded]; if (animated && bump) [self setWrapperScale:selected ? 0.77f : 0.87f animated:false]; static dispatch_once_t onceToken; static bool inhibitAnimation = false; dispatch_once(&onceToken, ^ { inhibitAnimation = ([[[UIDevice currentDevice] systemVersion] compare:@"7" options:NSNumericSearch] == NSOrderedAscending); }); if (inhibitAnimation) animated = false; if (animated) { if (self.selected == selected) return; if (bump) { if (selected) _checkFillView.transform = CGAffineTransformIdentity; } else { if (selected) _checkFillView.transform = CGAffineTransformMakeScale(0.01f, 0.01f); else _checkFillView.transform = CGAffineTransformIdentity; } [UIView animateWithDuration:0.19f animations:^ { _checkFillView.alpha = selected ? 1.0f : 0.0f; if (!bump || !selected) _checkFillView.transform = selected ? CGAffineTransformIdentity : CGAffineTransformMakeScale(0.01f, 0.01f); }]; if (selected) { CGFloat duration = 0.4f; CGFloat damping = 0.35f; CGFloat initialVelocity = 0.8f; if (bump) { duration = 0.5f; damping = 0.4f; initialVelocity = 0.6f; } [UIView animateWithDuration:duration delay:0.0f usingSpringWithDamping:damping initialSpringVelocity:initialVelocity options:UIViewAnimationOptionBeginFromCurrentState animations:^ { _wrapperView.transform = CGAffineTransformIdentity; } completion:nil]; _checkView.alpha = 1.0f; _checkShortFragment.transform = CGAffineTransformMakeScale(1.0f, 0.0f); _checkLongFragment.transform = CGAffineTransformMakeScale(0.0f, 1.0f); [UIView animateKeyframesWithDuration:0.21f delay:0.0f options:kNilOptions animations:^ { [UIView addKeyframeWithRelativeStartTime:0.0f relativeDuration:0.333f animations:^ { _checkShortFragment.transform = CGAffineTransformIdentity; }]; [UIView addKeyframeWithRelativeStartTime:0.333f relativeDuration:0.666f animations:^ { _checkLongFragment.transform = CGAffineTransformIdentity; }]; } completion:nil]; } else { CGFloat duration = 0.17f; if (bump) duration = 0.15f; [UIView animateWithDuration:duration animations:^ { _checkView.transform = CGAffineTransformScale(_checkView.transform, 0.01f, 0.01f); _checkView.alpha = 0.0f; if (bump) _wrapperView.transform = CGAffineTransformIdentity; } completion:^(__unused BOOL finished) { _checkView.transform = TGCheckButtonDefaultTransform; }]; } } else { _checkFillView.alpha = selected ? 1.0f : 0.0f; _checkFillView.transform = selected ? CGAffineTransformIdentity : CGAffineTransformMakeScale(0.1f, 0.1f); _checkView.alpha = selected ? 1.0f : 0.0f; _checkView.transform = TGCheckButtonDefaultTransform; _checkShortFragment.transform = CGAffineTransformIdentity; _checkLongFragment.transform = CGAffineTransformIdentity; } super.selected = selected; } #pragma mark - - (void)setWrapperScale:(CGFloat)scale animated:(bool)animated { void (^change)(void) = ^ { _wrapperView.transform = CGAffineTransformMakeScale(scale, scale); }; if (animated) [UIView animateWithDuration:0.12 delay:0 options:UIViewAnimationOptionAllowAnimatedContent animations:change completion:nil]; else change(); } - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { [super touchesBegan:touches withEvent:event]; [self setWrapperScale:0.85f animated:true]; } - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { [super touchesEnded:touches withEvent:event]; [self setWrapperScale:1.0f animated:true]; } - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event { [super touchesCancelled:touches withEvent:event]; [self setWrapperScale:1.0f animated:true]; } @end