Source of demand

Recently, I saw that the long press of the “Keep” button at the end of running has a good effect, which is exactly consistent with our business logic, so I realized a long press button by imitating the effect of “Keep”. The specific ideas and core code are as follows:

Implementation approach

After checking the effect of Keep, I found that after long pressing the button, the control was enlarged to a certain multiple, and then a circle was drawn according to the original size of the control. After the circle was drawn, long pressing was finished to carry out the corresponding business logic operation. The general idea is this, and then the concrete implementation code.

The implementation code

Create a button and add a long-press gesture
@property (nonatomic, strong) UIButton *endBtn; [self addSubview:self.endBtn]; [self.endBtn mas_makeConstraints:^(MASConstraintMaker *make) { make.centerX.equalTo(self).offset(0);  make.width.height.equalTo(@(Width_Real(60))); make.top.equalTo(self).offset(topHeight); }]; - (UIButton *)endBtn { if (! _endBtn) { _endBtn = [UIButton buttonWithType:UIButtonTypeCustom]; [_endBtn setImage:[UIImage imageNamed:@"icon_new_endBtn"] forState:(UIControlStateNormal)]; _endBtn.hidden = NO; UILongPressGestureRecognizer *longPG=[[UILongPressGestureRecognizer alloc]initWithTarget:self action:@selector(longPG:)]; LongPG. MinimumPressDuration = 0.1; [_endBtn addGestureRecognizer:longPG]; } return _endBtn; }Copy the code
Draw the code of the circle, this code is relatively simple, here will not repeat, directly paste the code
#define degree (x) (M_PI*(x)/180.0) @property (nonatomic, weak) CAShapeLayer *progressLayer; @property (nonatomic,weak) UIBezierPath *path; @property (nonatomic,weak) CAShapeLayer *trackLayer; - (void)drawRect:(CGRect)rect {CGFloat topHeight = 0.0; if ([SGMJCommonTool judgeIsSafeAres]) { topHeight = Width_Real(53) + Width_Real(40); } else { topHeight = Width_Real(35); } // Create a tracker(trackLayer) inside the tracker(trackLayer); trackLayer.frame = rect; [self.layer addSublayer:trackLayer]; TrackLayer. FillColor = [[UIColor blackColor] colorWithAlphaComponent: 0.5]. CGColor; trackLayer.strokeColor = [UIColor whiteColor].CGColor; self.trackLayer=trackLayer; Opacity = 0; // Opacity = 0; trackLayer.lineCap = kCALineCapRound; trackLayer.lineWidth = Width_Real(3); / / create the orbit UIBezierPath * path = [UIBezierPath bezierPathWithArcCenter: CGPointMake (the rect. Size. Width / 2, topHeight + Width_Real(60)/2) radius:Width_Real(60)/2 - Width_Real(3) / 2 startAngle:degreesToRadians(-90) endAngle:degreesToRadians(270) clockwise:YES]; // trackLayer.path = [path CGPath]; self.path=path; path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(rect.size.width / 2, topHeight + Width_Real(60)/2) radius:Width_Real(60)/2 - Width_Real(3) / 2 startAngle:degreesToRadians(Width_Real(3) / 2 - 90) endAngle:degreesToRadians(270 - Width_Real(3) / 2) clockwise:YES]; CAShapeLayer *progressLayer = [CAShapeLayer layer]; progressLayer.frame = rect; progressLayer.fillColor = [UIColor clearColor].CGColor; progressLayer.strokeColor = [UIColor redColor].CGColor; progressLayer.lineCap = kCALineCapRound; progressLayer.lineWidth = Width_Real(3); progressLayer.path = [path CGPath]; ProgressLayer. StrokeEnd = 0.00 f; self.progressLayer = progressLayer; CALayer *gradientLayer = [CALayer layer]; CAGradientLayer *gradientLayer1 = [CAGradientLayer layer]; gradientLayer1.frame = CGRectMake(0, 0, rect.size.width , rect.size.height); [gradientLayer1 setColors:[NSArray arrayWithObjects:(id)[[UIColor whiteColor] CGColor],(id)[[UIColor whiteColor] CGColor],(id)[[UIColor whiteColor] CGColor], nil]]; [gradientLayer1 setStartPoint: CGPointMake (0.5, 0)]; [gradientLayer1 setEndPoint: CGPointMake (0.5, 1)); [gradientLayer addSublayer:gradientLayer1]; // gradientLayer setMask:progressLayer, fill is clear stroke color [gradientLayer setMask:progressLayer]; [self.layer addSublayer:gradientLayer]; }Copy the code
Handle gestures callback, at the start of long time, will be the size of the button to before 1.2 times, and start a timer, because demand is required within 2 seconds progress reached one hundred percent, so the drawing under the call of every 0.02 seconds, at the end of the long press the button, whether the progress reached 100, and will control the size of the same, specific code is as follows:
@property (nonatomic , strong)NSTimer *timer; @property (nonatomic, assign) CGFloat currentProgress; @property (nonatomic, assign) NSInteger index; / / long press button callback methods - (void) longPG: (pg UILongPressGestureRecognizer *) {switch (pg. State) {case UIGestureRecognizerStateBegan: { [UIView animateWithDuration: 0.1 animations: ^ {self. EndBtn. Transform = CGAffineTransformMakeScale (1.2, 1.2);  }completion:^(BOOL finish){ }]; self.index = 1; self.currentProgress = 1; The self. The timer = [NSTimer timerWithTimeInterval: 0.02 target: self selector: @ the selector (timerEvent) the userInfo: nil repeats: YES]; [[NSRunLoop mainRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes]; break; } case UIGestureRecognizerStateEnded: {the if (self currentProgress > = 100) {/ / handle the end of the long business logic} / / clear the timer if (self. The timer) { [self.timer invalidate]; self.timer = nil; } [UIView animateWithDuration: 0.1 animations: ^ {self. EndBtn. Transform = CGAffineTransformMakeScale (1.0, 1.0);  }completion:^(BOOL finish){ }]; [the self setPercet: 00 withTimer: 0.1]; break; } case UIGestureRecognizerStateChanged: { } default: break; } } - (void)timerEvent { self.index+=1; [the self setPercet: self. The index withTimer: 0.02]; if (self.index >=100) { [self.timer invalidate]; self.timer = nil; } } - (void)setPercet:(CGFloat)percent withTimer:(CGFloat)time { [CATransaction begin]; [CATransaction setDisableActions:NO]; [CATransaction setAnimationTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut]]; [CATransaction setAnimationDuration:time]; _progressLayer. StrokeStart = 0.00 f; _progresslayer. strokeEnd = percent / 100.0f; _currentProgress = percent; [CATransaction commit]; }Copy the code

At the end

At this point, the logic of long-pressing the button has been implemented. Of course, some interaction can be added during operation, such as prompting the user to wait for the long-pressing button when short-pressing the button. The specific code is not described here, if you have any questions, feel free to leave them in the comments section.