OC添加滑块验证码
纯代码如下,使用麻烦点个赞:
#import <UIKit/UIKit.h>
@protocol ZTXSlideVerifyViewDelegate <NSObject>
- (void)slideVerifyDidSuccess;
- (void)slideVerifyDidFail;
@end
@interface ZTXSlideVerifyView : UIView
@property (nonatomic, weak) id<ZTXSlideVerifyViewDelegate> delegate;
@property (nonatomic, strong) UIImage *backgroundImage;
@property (nonatomic, strong) UIColor *slideTrackColor;
@property (nonatomic, strong) UIColor *slideThumbColor;
@property (nonatomic, assign) CGFloat verifyTolerance;
// 初始化方法
- (instancetype)initWithFrame:(CGRect)frame backgroundImage:(UIImage *)image;
// 初始化方法
- (instancetype)initWithBackgroundImage:(UIImage *)image;
// 重置方法
- (void)reset;
@end
.m界面
#import "ZTXSlideVerifyView.h"
static CGFloat const kPuzzleWidth = 40.0f;
static CGFloat const kPuzzleHeight = 40.0f;
static CGFloat const kSlideThumbWidth = 40.0f;
static CGFloat const kSlideThumbHeight = 40.0f;
static CGFloat const kSlideTrackHeight = 40.0f;
@interface ZTXSlideVerifyView ()
@property (nonatomic, strong) UIImageView *backgroundImageView;
@property (nonatomic, strong) UIView *maskView;
@property (nonatomic, strong) UIImageView *slidePieceImageView;
@property (nonatomic, strong) UIView *slideTrackView;
@property (nonatomic, strong) UIView *slideThumbView;
@property (nonatomic, strong) UILabel *tipLabel;
@property (nonatomic, assign) CGRect puzzleFrame;
@property (nonatomic, assign) CGPoint slidePieceInitialCenter;
@property (nonatomic, assign) CGFloat slideStartX;
@property (nonatomic, assign) CGFloat targetX;
@property (nonatomic, assign) CGFloat targetY;
@property (nonatomic, strong) UIView *puzzleHoleView;
@property (nonatomic, strong) CAShapeLayer *maskLayer;
@property (nonatomic, strong) UIView *bgView;
@end
@implementation ZTXSlideVerifyView
#pragma mark - Initialization
- (instancetype)initWithFrame:(CGRect)frame backgroundImage:(UIImage *)image {
self = [super initWithFrame:frame];
if (self) {
self.tipLabel.text = @"向右滑动完成拼图";
self.tipLabel.textColor = [UIColor darkGrayColor];
_backgroundImage = image;
_slideTrackColor = [UIColor colorWithWhite:0.9 alpha:1.0];
_slideThumbColor = [UIColor whiteColor];
_verifyTolerance = 5.0f;
[self setupUI];
}
return self;
}
#pragma mark - UI Setup
- (void)setupUI {
self.backgroundColor = [UIColor clearColor];
// 背景图片视图
CGFloat imageHeight = self.bounds.size.height - 80;
self.backgroundImageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, self.bounds.size.width, imageHeight)];
self.backgroundImageView.contentMode = UIViewContentModeScaleAspectFill;
self.backgroundImageView.clipsToBounds = YES;
self.backgroundImageView.image = self.backgroundImage;
[self addSubview:self.backgroundImageView];
// 遮罩层
self.maskView = [[UIView alloc] initWithFrame:self.backgroundImageView.bounds];
self.maskView.backgroundColor = [UIColor colorWithWhite:0 alpha:0.5];
[self.backgroundImageView addSubview:self.maskView];
// 遮罩层的形状
self.maskLayer = [CAShapeLayer layer];
self.maskLayer.fillRule = kCAFillRuleEvenOdd;
self.maskView.layer.mask = self.maskLayer;
// 滑动的拼图块
self.slidePieceImageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, kPuzzleWidth, kPuzzleHeight)];
self.slidePieceImageView.layer.borderWidth = 1;
self.slidePieceImageView.layer.borderColor = [UIColor whiteColor].CGColor;
self.slidePieceImageView.layer.shadowColor = [UIColor blackColor].CGColor;
self.slidePieceImageView.layer.shadowOffset = CGSizeMake(0, 1);
self.slidePieceImageView.layer.shadowOpacity = 0.3;
self.slidePieceImageView.layer.shadowRadius = 2;
[self addSubview:self.slidePieceImageView];
[self addSubview:self.tipLabel];
// 滑动轨道
self.slideTrackView = [[UIView alloc] initWithFrame:CGRectMake(20, CGRectGetMaxY(self.tipLabel.frame), self.bounds.size.width - 40, kSlideTrackHeight)];
self.slideTrackView.backgroundColor = self.slideTrackColor;
[self addSubview:self.slideTrackView];
// 滑块
self.slideThumbView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, kSlideThumbWidth, kSlideThumbHeight)];
self.slideThumbView.backgroundColor = self.slideThumbColor;
self.slideThumbView.layer.shadowColor = [UIColor blackColor].CGColor;
self.slideThumbView.layer.shadowOffset = CGSizeMake(0, 2);
self.slideThumbView.layer.shadowOpacity = 0.2;
self.slideThumbView.layer.shadowRadius = 3;
[self.slideTrackView addSubview:self.slideThumbView];
// 添加滑块图标
[self addSlideThumbIcon];
// 添加滑动手势
UIPanGestureRecognizer *panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePan:)];
[self.slideThumbView addGestureRecognizer:panGesture];
// 生成拼图块
if (self.backgroundImage) {
[self generatePuzzlePiece];
}
}
- (UILabel *)tipLabel{
if(!_tipLabel){
CGFloat imageHeight = self.bounds.size.height - 80;
// 提示文字
_tipLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, imageHeight, self.bounds.size.width, 40)];
_tipLabel.text = @"向右滑动完成拼图";
_tipLabel.textAlignment = NSTextAlignmentCenter;
_tipLabel.textColor = [UIColor darkGrayColor];
_tipLabel.font = [UIFont systemFontOfSize:14];
}
return _tipLabel;
}
- (void)addSlideThumbIcon {
// 创建三条横线
for (int i = 0; i < 3; i++) {
CALayer *line = [CALayer layer];
line.frame = CGRectMake(10, 12 + i * 8, 20, 2);
line.backgroundColor = [UIColor colorWithWhite:0.7 alpha:1.0].CGColor;
line.cornerRadius = 1;
[self.slideThumbView.layer addSublayer:line];
}
}
#pragma mark - Puzzle Generation
// 修改图片裁剪相关的代码
- (void)generatePuzzlePiece {
if (!self.backgroundImage) return;
// 获取背景图片的实际显示尺寸
CGSize imageSize = self.backgroundImage.size;
CGSize viewSize = self.backgroundImageView.bounds.size;
// 计算图片在UIImageView中的实际显示区域
CGFloat scale = MAX(viewSize.width / imageSize.width, viewSize.height / imageSize.height);
CGFloat displayWidth = imageSize.width * scale;
CGFloat displayHeight = imageSize.height * scale;
CGFloat offsetX = (viewSize.width - displayWidth) / 2.0;
CGFloat offsetY = (viewSize.height - displayHeight) / 2.0;
// 随机生成拼图位置(确保在显示区域内)
CGFloat maxX = displayWidth - kPuzzleWidth - 60;
CGFloat scaledHeight = imageSize.height * scale;
CGFloat imageY = (viewSize.height - scaledHeight) / 2.0;
CGFloat safeAreaY = MAX(imageY + 20, 20);
CGFloat safeAreaHeight = MIN(scaledHeight - 40, viewSize.height - 40);
CGFloat maxY = safeAreaHeight - kPuzzleHeight;
self.targetX = 60 + arc4random_uniform(maxX);
// self.targetY = 60 + 30;
self.targetY = safeAreaY + arc4random_uniform(maxY);
// 设置拼图区域
self.puzzleFrame = CGRectMake(self.targetX, self.targetY, kPuzzleWidth, kPuzzleHeight);
// 创建遮罩路径
UIBezierPath *path = [UIBezierPath bezierPathWithRect:self.maskView.bounds];
UIBezierPath *holePath = [UIBezierPath bezierPathWithRect:self.puzzleFrame];
[path appendPath:holePath];
self.maskLayer.path = path.CGPath;
// 计算在原图中的裁剪区域
CGFloat imageScale = self.backgroundImage.scale;
CGFloat cropX = ((self.targetX - offsetX) / scale) * imageScale;
CGFloat cropY = ((self.targetY - offsetY) / scale) * imageScale;
CGFloat cropWidth = (kPuzzleWidth / scale) * imageScale;
CGFloat cropHeight = (kPuzzleHeight / scale) * imageScale;
// 确保裁剪区域在图片范围内
cropX = MAX(0, MIN(cropX, imageSize.width * imageScale - cropWidth));
cropY = MAX(0, MIN(cropY, imageSize.height * imageScale - cropHeight));
// 创建裁剪区域
CGRect cropRect = CGRectMake(cropX, cropY, cropWidth, cropHeight);
// 从原图中裁剪
CGImageRef cgImage = self.backgroundImage.CGImage;
CGImageRef croppedCGImage = CGImageCreateWithImageInRect(cgImage, cropRect);
// 创建裁剪后的图片
UIImage *croppedImage = [UIImage imageWithCGImage:croppedCGImage
scale:imageScale
orientation:self.backgroundImage.imageOrientation];
CGImageRelease(croppedCGImage);
// 设置拼图块
self.slidePieceImageView.frame = CGRectMake(0, self.targetY, kPuzzleWidth, kPuzzleHeight);
self.slidePieceImageView.image = croppedImage;
self.slidePieceImageView.contentMode = UIViewContentModeScaleToFill;
self.slidePieceImageView.clipsToBounds = YES;
// 记录初始位置
self.slidePieceInitialCenter = self.slidePieceImageView.center;
}
// 修改手势处理中的位置计算
- (void)handlePan:(UIPanGestureRecognizer *)gesture {
CGPoint translation = [gesture translationInView:self.slideTrackView];
switch (gesture.state) {
case UIGestureRecognizerStateBegan: {
self.slideStartX = self.slideThumbView.center.x;
[self onSlideStart];
break;
}
case UIGestureRecognizerStateChanged: {
CGFloat newX = self.slideStartX + translation.x;
newX = MAX(kSlideThumbWidth/2, newX);
newX = MIN(self.slideTrackView.bounds.size.width - kSlideThumbWidth/2, newX);
self.slideThumbView.center = CGPointMake(newX, self.slideThumbView.center.y);
// 计算拼图块位置
CGFloat progress = (newX - kSlideThumbWidth/2) / (self.slideTrackView.bounds.size.width - kSlideThumbWidth);
CGFloat maxMoveDistance = self.backgroundImageView.bounds.size.width - kPuzzleWidth;
CGFloat pieceX = progress * maxMoveDistance;
// 更新拼图块位置
self.slidePieceImageView.frame = CGRectMake(pieceX, self.targetY, kPuzzleWidth, kPuzzleHeight);
break;
}
case UIGestureRecognizerStateEnded: {
CGFloat finalX = self.slidePieceImageView.frame.origin.x;
if (fabs(finalX - self.targetX) < self.verifyTolerance) {
[self verifySuccess];
} else {
[self verifyFailed];
}
break;
}
default:
break;
}
}
#pragma mark - Animation and Feedback
- (void)onSlideStart {
self.tipLabel.text = @"拼图对准缺口";
self.tipLabel.textColor = [UIColor darkGrayColor];
}
- (void)verifySuccess {
if ([self.delegate respondsToSelector:@selector(slideVerifyDidSuccess)]) {
[self.delegate slideVerifyDidSuccess];
}
self.tipLabel.text = @"验证成功";
self.tipLabel.textColor = [UIColor colorWithRed:0.2 green:0.8 blue:0.2 alpha:1.0];
[UIView animateWithDuration:0.2 animations:^{
self.slidePieceImageView.alpha = 0.8;
} completion:^(BOOL finished) {
[UIView animateWithDuration:0.2 animations:^{
self.slidePieceImageView.alpha = 1.0;
}];
}];
}
- (void)verifyFailed {
if ([self.delegate respondsToSelector:@selector(slideVerifyDidFail)]) {
[self.delegate slideVerifyDidFail];
}
self.tipLabel.text = @"验证失败";
self.tipLabel.textColor = [UIColor redColor];
[UIView animateWithDuration:0.3 animations:^{
self.slideThumbView.center = CGPointMake(kSlideThumbWidth/2, self.slideThumbView.center.y);
self.slidePieceImageView.frame = CGRectMake(0, self.targetY, kPuzzleWidth, kPuzzleHeight);
} completion:^(BOOL finished) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
self.tipLabel.text = @"向右滑动完成拼图";
self.tipLabel.textColor = [UIColor darkGrayColor];
});
}];
}
#pragma mark - Public Methods
- (void)reset {
[UIView animateWithDuration:0.3 animations:^{
self.slideThumbView.center = CGPointMake(kSlideThumbWidth/2, self.slideThumbView.center.y);
self.slidePieceImageView.frame = CGRectMake(0, self.targetY, kPuzzleWidth, kPuzzleHeight);
} completion:^(BOOL finished) {
[self generatePuzzlePiece];
self.tipLabel.text = @"向右滑动完成拼图";
self.tipLabel.textColor = [UIColor darkGrayColor];
}];
}
#pragma mark - Setters
- (void)setBackgroundImage:(UIImage *)backgroundImage {
_backgroundImage = backgroundImage;
self.backgroundImageView.image = backgroundImage;
if (backgroundImage) {
[self generatePuzzlePiece];
}
}
@end
使用方法:
// 创建滑块验证视图
ZTXSlideVerifyView *slideView = [[ZTXSlideVerifyView alloc] initWithFrame:CGRectMake(15, 140, ZTX_Width-30, 300) backgroundImage:[UIImage imageNamed:@"启动背景"]];//这个地方就是加入的图
slideView.delegate = self;
// 自定义属性(可选)
slideView.slideTrackColor = [UIColor lightGrayColor];
slideView.slideThumbColor = [UIColor whiteColor];
slideView.verifyTolerance = 5.0f;
_slideVerifyView = slideView;
UIApplication *ap = [UIApplication sharedApplication];
[self.bgView addSubview:slideView];
[ap.keyWindow addSubview:self.bgView];
//需要一个背景图
- (UIView *)bgView{
if(!_bgView){
_bgView = [[UIView alloc] init];
_bgView.frame = [UIScreen mainScreen].bounds;
_bgView.backgroundColor = ZTXColor_00000050;
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(imageCenterClick:)];
_bgView.userInteractionEnabled = YES;
[_bgView addGestureRecognizer:tap];
}
return _bgView;
}
-(void)imageCenterClick:(UITapGestureRecognizer *)tap{
[self.bgView removeFromSuperview];
[_slideVerifyView removeFromSuperview];
_slideVerifyView = nil;
}
#pragma mark - ZTXSlideVerifyViewDelegate代理
- (void)slideVerifyDidSuccess {
// 验证成功的处理
NSLog(@"验证成功")
}
- (void)slideVerifyDidFail {
// 验证失败的处理
}