preface

  • Share 3 buttons to use, countdown button, indicator button, like particle effect button

Countdown button

Property & API

@property(nonatomic,copy, readWrite)void(^kButtonCountDownStop)(void); - (void)kj_startTime:(NSInteger)timeout CountDownFormat:(NSString*)format; // Cancel the timer - (void)kj_cancelTimer; @endCopy the code

A simple introduction

The button that is counting down is not clickable. The main thing inside is to declare a timer NSTimer to handle the countdown button and turn off the userInteractionEnabled property for the duration of the timer

1. kButtonCountDownStop

The callback is called when the countdown ends

2. kj_startTime:CountDownFormat:

Start the time

- (void)kj_startTime:(NSInteger)timeout CountDownFormat:(NSString*)format{ [self kj_cancelTimer]; self.timeOut = timeout; self.xxtitle = self.titleLabel.text; NSDictionary *info = @{@"countDownFormat":format ? : @ lh-zd "% s"}; The self. The timer = [NSTimer timerWithTimeInterval: 1.0 target: self selector: @ the selector (timerMethod:) the userInfo: info repeats: YES];  [[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes]; dispatch_async(dispatch_get_main_queue(), ^{ [self setTitle:[NSString stringWithFormat:format ?: @ lh-zd "% s", a timeout] forState: UIControlStateNormal]; self.userInteractionEnabled = NO; }); } - (void)timerMethod:(NSTimer*)timer{ NSDictionary *info = timer.userInfo; NSString *countDownFormat = info[@"countDownFormat"]; if (self.timeOut <= 0){ [self kj_cancelTimer]; }else{ self.timeOut--; dispatch_async(dispatch_get_main_queue(), ^{ [self setTitle:[NSString stringWithFormat:countDownFormat,self.timeOut] forState:UIControlStateNormal]; self.userInteractionEnabled = NO; }); }}Copy the code

3. kj_cancelTimer

Cancel the countdown

- (void)kj_cancelTimer{ if (self.timer == nil) return; [self.timer invalidate]; self.timer = nil; dispatch_async(dispatch_get_main_queue(), ^{ [self setTitle:self.xxtitle forState:UIControlStateNormal]; self.userInteractionEnabled = YES; if (self.kButtonCountDownStop) { self.kButtonCountDownStop(); }}); }Copy the code

Internal Property

- (NSTimer*)timer{
    return objc_getAssociatedObject(self, @selector(timer));
}
- (void)setTimer:(NSTimer*)timer{
    objc_setAssociatedObject(self, @selector(timer), timer, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (NSString*)xxtitle{
    return objc_getAssociatedObject(self, @selector(xxtitle));
}
- (void)setXxtitle:(NSString*)xxtitle{
    objc_setAssociatedObject(self, @selector(xxtitle), xxtitle, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (void)setKButtonCountDownStop:(void(^)(void))kButtonCountDownStop{
    objc_setAssociatedObject(self, @selector(kButtonCountDownStop), kButtonCountDownStop, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (void(^)(void))kButtonCountDownStop{
    return objc_getAssociatedObject(self, @selector(kButtonCountDownStop));
}
- (NSInteger)timeOut{
    return [objc_getAssociatedObject(self, @selector(timeOut)) integerValue];
}
- (void)setTimeOut:(NSInteger)timeOut{
    objc_setAssociatedObject(self, @selector(timeOut), @(timeOut), OBJC_ASSOCIATION_ASSIGN);
}
Copy the code

Use the sample

[_countDownButton kj_addAction:^(UIButton * _Nonnull kButton) {[kButton kj_startTime:6 CountDownFormat:@" count %zd seconds "];}]; _countDownButton. KButtonCountDownStop = ^ {NSLog (@ "end of time!!!" ); };Copy the code

Indicator button

Property & API

@ interface UIButton (KJIndicator) are / / / button to submit the @ property (nonatomic, assign, readonly) bool date; /// indicator and text spacing, default 5px @property(nonatomic,assign)CGFloat indicatorSpace; / / / color indicator, the default white @ property (nonatomic, assign) UIActivityIndicatorViewStyle indicatorType; /// start to submit. The indicator follows the words - (void) kj_begincodin :(NSString*)title; /// End of submission - (void) kj_endcob; /// display pointer - (void)kj_showIndicator; /// hide indicator - (void)kj_hideIndicator; @endCopy the code

A simple introduction

It’s just a text UILabel and an indicator UIActivityIndicatorView inside the button

Inside out

#import "UIButton+KJIndicator.h" #import <objc/runtime.h> @implementation UIButton (KJIndicator) static NSString *kIndicatorLastTitle = nil; - (void)kj_beginSubmitting:(NSString*)title{ [self kj_endSubmitting]; kSubmitting = true; kIndicatorLastTitle = self.titleLabel.text; self.enabled = NO; [self setTitle:@"" forState:UIControlStateNormal]; self.indicatorType = self.indicatorType? :UIActivityIndicatorViewStyleWhite; self.indicatorView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:self.indicatorType]; [self addSubview:self.indicatorView]; self.indicatorSpace = self.indicatorSpace? : 5; CGFloat w = self.bounds.size.width; CGFloat h = self.bounds.size.height; CGFloat sp = w / 2.; if (! [title isEqualToString:@""]) { self.indicatorLabel = [[UILabel alloc] init]; self.indicatorLabel.text = title; self.indicatorLabel.font = self.titleLabel.font; self.indicatorLabel.textColor = self.titleLabel.textColor; [self addSubview:self.indicatorLabel]; CGSize size = [title boundingRectWithSize: CGSizeMake (MAXFLOAT, 0.0) options: NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName:self.titleLabel.font} context:nil].size; sp = ((w-self.indicatorSpace-size.width)*.5)? : 0.0; self.indicatorLabel.frame = CGRectMake(sp+self.indicatorSpace+self.indicatorView.frame.size.width/2, 0, size.width, h); } self.indicatorView.center = CGPointMake(sp, h/2); [self.indicatorView startAnimating]; } - (void)kj_endSubmitting { [self kj_hideIndicator]; self.indicatorView = nil; self.indicatorLabel = nil; } - (void)kj_showIndicator { if (self.indicatorView && self.indicatorView.superview == nil) { [self addSubview:self.indicatorView]; [self.indicatorView startAnimating]; } if (self.indicatorLabel && self.indicatorLabel.superview == nil) { [self addSubview:self.indicatorLabel]; [self setTitle:@"" forState:UIControlStateNormal]; } } - (void)kj_hideIndicator { kSubmitting = false; self.enabled = YES; [self.indicatorView removeFromSuperview]; [self.indicatorLabel removeFromSuperview]; if (self.indicatorLabel) { [self setTitle:kIndicatorLastTitle forState:UIControlStateNormal]; } if (self.indicatorView) { [self.indicatorView stopAnimating]; [self setTitle:kIndicatorLastTitle forState:UIControlStateNormal]; } } #pragma mark - getter/setter static bool kSubmitting = false; - (bool)submitting{ return kSubmitting; } - (CGFloat)indicatorSpace{ return [objc_getAssociatedObject(self, @selector(indicatorSpace)) floatValue]; } - (void)setIndicatorSpace:(CGFloat)indicatorSpace{ objc_setAssociatedObject(self, @selector(indicatorSpace), @(indicatorSpace), OBJC_ASSOCIATION_RETAIN_NONATOMIC); } - (UIActivityIndicatorViewStyle)indicatorType{ return (UIActivityIndicatorViewStyle)[objc_getAssociatedObject(self, @selector(indicatorType)) intValue]; } - (void)setIndicatorType:(UIActivityIndicatorViewStyle)indicatorType{ objc_setAssociatedObject(self, @selector(indicatorType), @(indicatorType), OBJC_ASSOCIATION_RETAIN_NONATOMIC); } - (UIActivityIndicatorView*)indicatorView{ return objc_getAssociatedObject(self, @selector(indicatorView)); } - (void)setIndicatorView:(UIActivityIndicatorView*)indicatorView{ objc_setAssociatedObject(self, @selector(indicatorView), indicatorView, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } - (UILabel*)indicatorLabel{ return objc_getAssociatedObject(self, @selector(indicatorLabel)); } - (void)setIndicatorLabel:(UILabel*)indicatorLabel{ objc_setAssociatedObject(self, @selector(indicatorLabel), indicatorLabel, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } @endCopy the code

Use the sample

[button kj_addAction:^(UIButton * _Nonnull kButton) {[kButton KJ_begin_tegcob :@" tegcoB "];}]; [button kj_addAction:^(UIButton * _Nonnull kButton) { kButton.selected = ! kButton.selected; if (kButton.selected) { [weakself.submitButton kj_hideIndicator];  }else{ [weakself.submitButton kj_showIndicator]; } }];Copy the code

Effect of particle

Property & API

@ interface UIButton (KJEmitter) / / / particle, note don't change the name attribute @ property (nonatomic, strong, readonly) CAEmitterCell * emitterCell; // (void)kj_buttonSetEmitterImage:(UIImage*_Nullable)image opentemitterimage :(bool)open; @endCopy the code

A simple introduction

We’re actually handling the particle effect after setSelected

kj_buttonSetEmitterImage:

Initialize effect, get particle graph, set CAEmitterLayer

- (void)kj_buttonSetEmitterImage:(UIImage*_Nullable)image OpenEmitter:(bool)open{ static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ method_exchangeImplementations(class_getInstanceMethod(self.class, @selector(setSelected:)), class_getInstanceMethod(self.class, @selector(kj_setSelected:))); }); self.emitterImage = image? :[UIImage imageNamed:@"KJKit.bundle/button_sparkle"]; self.emitterOpen = open; [self setupLayer]; }Copy the code

Set parameters related to particle effect. Considering the situation of user-defined emitterImage, it is better to open the particle emitterCell, so as to facilitate external modification of corresponding parameters (Note: Name attribute should not be modified).

- (void)setupLayer{ CAEmitterCell *emitterCell = [CAEmitterCell emitterCell]; emitterCell.name = @"name"; EmitterCell. AlphaRange = 0.10; EmitterCell. Lifetime = 0.7; EmitterCell. LifetimeRange = 0.3; EmitterCell. Velocity = 40.00; EmitterCell. VelocityRange = 10.00; EmitterCell. Scale = 0.04; EmitterCell. ScaleRange = 0.02; emitterCell.contents = (id)self.emitterImage.CGImage; self.emitterCell = emitterCell; CAEmitterLayer *emitterLayer = [CAEmitterLayer layer]; emitterLayer.name = @"emitterLayer"; emitterLayer.emitterShape = kCAEmitterLayerCircle; emitterLayer.emitterMode = kCAEmitterLayerOutline; emitterLayer.emitterSize = CGSizeMake(10, 0); emitterLayer.emitterCells = @[emitterCell]; emitterLayer.renderMode = kCAEmitterLayerOldestFirst; EmitterLayer. Position = CGPointMake (self. Frame. The size, width / 2.0, the self. The frame. The size, height / 2.0); emitterLayer.zPosition = -1; [self.layer addSublayer:emitterLayer]; self.explosionLayer = emitterLayer; }Copy the code

Turn on particle ejection and Zoom effects

- (void)buttonAnimation{ CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"transform.scale"];  If (self. Selected) {animation. Values = @ [@ 1.5, @ 0.8, @ 1.0, @ 1.2, @ 1.0]; Animation. Duration = 0.4; / / / jet self. ExplosionLayer. BeginTime = CACurrentMediaTime (); [self.explosionLayer setValue:@2000 forKeyPath:@"emitterCells.name.birthRate"]; [the self performSelector: @ the selector (stop) withObject: nil afterDelay: 0.2]; }else{animation.values = @[@0.8, @1.0]; Animation. Duration = 0.2; } animation.calculationMode = kCAAnimationCubic; [self.layer addAnimation:animation forKey:@"transform.scale"]; }Copy the code

Stop spraying after 0.2 seconds

[the self performSelector: @ the selector (stop) withObject: nil afterDelay: 0.2];Copy the code

By stopping the ejection, you essentially set the particle’s life cycle to zero

- (void)stop {
    [self.explosionLayer setValue:@0 forKeyPath:@"emitterCells.name.birthRate"];
}
Copy the code

Attach the complete code

#import "UIButton+KJEmitter. H "#import <objc/runtime.h> @implementation UIButton (KJEmitter (void)kj_buttonSetEmitterImage:(UIImage*_Nullable)image OpenEmitter:(bool)open{ static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ method_exchangeImplementations(class_getInstanceMethod(self.class, @selector(setSelected:)), class_getInstanceMethod(self.class, @selector(kj_setSelected:))); }); self.emitterImage = image? :[UIImage imageNamed:@"KJKit.bundle/button_sparkle"]; self.emitterOpen = open; [self setupLayer]; } /// method swap - (void)kj_setSelected: BOOL kj_setSelected {self kj_setSelected:selected]; if (self.emitterOpen) [self buttonAnimation]; } - (UIImage*)emitterImage{ return objc_getAssociatedObject(self, @selector(emitterImage)); } - (void)setEmitterImage:(UIImage*)emitterImage{ objc_setAssociatedObject(self, @selector(emitterImage), emitterImage, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } - (BOOL)emitterOpen{ return [objc_getAssociatedObject(self, @selector(emitterOpen)) intValue]; } - (void)setEmitterOpen:(BOOL)emitterOpen{ objc_setAssociatedObject(self, @selector(emitterOpen), @(emitterOpen), OBJC_ASSOCIATION_ASSIGN); } - (CAEmitterLayer*)explosionLayer{ return objc_getAssociatedObject(self, @selector(explosionLayer)); } - (void)setExplosionLayer:(CAEmitterLayer *)explosionLayer{ objc_setAssociatedObject(self, @selector(explosionLayer), explosionLayer, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } - (CAEmitterCell*)emitterCell{ return objc_getAssociatedObject(self, @selector(emitterCell)); } - (void)setEmitterCell:(CAEmitterCell*)emitterCell{ objc_setAssociatedObject(self, @selector(emitterCell), emitterCell, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } #pragma mark - (void)setupLayer{CAEmitterCell *emitterCell = [CAEmitterCell emitterCell]; emitterCell.name = @"name"; EmitterCell. AlphaRange = 0.10; EmitterCell. Lifetime = 0.7; EmitterCell. LifetimeRange = 0.3; EmitterCell. Velocity = 40.00; EmitterCell. VelocityRange = 10.00; EmitterCell. Scale = 0.04; EmitterCell. ScaleRange = 0.02; emitterCell.contents = (id)self.emitterImage.CGImage; self.emitterCell = emitterCell; CAEmitterLayer *emitterLayer = [CAEmitterLayer layer]; emitterLayer.name = @"emitterLayer"; emitterLayer.emitterShape = kCAEmitterLayerCircle; emitterLayer.emitterMode = kCAEmitterLayerOutline; emitterLayer.emitterSize = CGSizeMake(10, 0); emitterLayer.emitterCells = @[emitterCell]; emitterLayer.renderMode = kCAEmitterLayerOldestFirst; EmitterLayer. Position = CGPointMake (self. Frame. The size, width / 2.0, the self. The frame. The size, height / 2.0); emitterLayer.zPosition = -1; [self.layer addSublayer:emitterLayer]; self.explosionLayer = emitterLayer; } /// Start animation - (void)buttonAnimation{CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"transform.scale"]; If (self. Selected) {animation. Values = @ [@ 1.5, @ 0.8, @ 1.0, @ 1.2, @ 1.0]; Animation. Duration = 0.4; self.explosionLayer.beginTime = CACurrentMediaTime(); [self.explosionLayer setValue:@2000 forKeyPath:@"emitterCells.name.birthRate"]; [the self performSelector: @ the selector (stop) withObject: nil afterDelay: 0.2]; }else{animation.values = @[@0.8, @1.0]; Animation. Duration = 0.2; } animation.calculationMode = kCAAnimationCubic; [self.layer addAnimation:animation forKey:@"transform.scale"]; } / / / stop injection - (void) stop {[self. ExplosionLayer setValue: @ 0 forKeyPath: @ "emitterCells. Name. BirthRate"]. } @endCopy the code

Introduction to the three kinds of button, free again complement, code word I’m beat – |

Note: some function methods and Demo used in this article are from the tripartite library **KJEmitterView**, if there is a need for friends can by themselvespod 'KJEmitterView'Introduction to

Countdown, indicators, particle effect introduction is over here, there are related to supplement, it is not easy to write an article, but also please point a **Little stars* * portal