当前位置: 首页 > news >正文

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 {

    // 验证失败的处理

}

http://www.dtcms.com/a/108836.html

相关文章:

  • XLRS-Bench:您能否理解极端大型超高分辨率遥感影像?
  • 预测分析(三):基于机器学习的分类预测
  • 基于微信小程序的生签到系统设计与实现
  • c语言数据结构--------拓扑排序和逆拓扑排序(Kahn算法和DFS算法实现)
  • MySQL的安装与初始化流程
  • 联想M7400打印机怎么清零
  • 基于LangChain和通义(Tongyi)实现NL2SQL的智能检索(无需训练)
  • Spring Boot 3.4.3 和 Spring Security 6.4.2 实现基于内存和 MySQL 的用户认证
  • 《Linux内存管理:实验驱动的深度探索》【附录】【实验环境搭建 1】【Qemu 运行 linux 6.0.9】
  • c++函数中的多态是怎样体现的
  • Cursor的主要好处
  • 【排序算法】堆排、快排、归并排、各种排
  • 动态规划 线性dp系列:数字三角形
  • Mysql 中数据主键类型不一样导致数据插入速度快慢问题
  • Java开发如何基于 Spring AI Alibaba 玩转 MCP:从发布、调用到 Claude Manus 集成
  • 2.5路径问题专题:LeetCode 64. 最小路径和
  • Python的三方库之Pandas(三)
  • MetaBase Mysql8.0连接不上
  • 怎么理解量子比特模型,迁移到量子计算机开始编程
  • 飞桨新一代框架3.0正式发布:加速大模型时代的技术创新与产业应用
  • AF3 OpenFoldMultimerDataset类解读
  • 洛谷题单3-P1035 [NOIP 2002 普及组] 级数求和-python-流程图重构
  • JavaScript日期对象
  • Python 编程实战:打造高效便捷的目录结构生成器
  • 踩坑ubuntu24.04 编译vtk9.3.1的安卓版本
  • 前端开发技术演进与就业现实:顺应时代方能不被淘汰-优雅草卓伊凡
  • ubantu执行sudo chown -R username xxx(文件夹)命令失效
  • 推荐系统(二十一):基于MaskNet的商品推荐CTR模型实现
  • OpenCV 图形API(12)用于计算图像或矩阵的平均值函数mean()
  • dify开启多租户模式