iOS-宫格拼图

思路

要求设计思路是类似手持拼图游戏,拼图需求要求有一块为空白版,作为移动方块的预留位置用,通过选择图片后在起初对所有图像方块随机打乱顺序时,发现随机打乱顺序,没办法拼图完成,拼图移动是空白快最临近的上下左右四个图像块的移动,在打乱顺序的时候,也要按照这个算法逻辑实现,才能拼图完成;

另外逻辑实现上,用tag来记录图片,用accessibilityValue 来记录图片的实际位置标记;

用三个数组来实现顺序打乱、正序校验、拼图位置的校验等,起初对三个数组进行相同的初始化值;

实现

变量及相关初始化

///次序,用来排序
@property (nonatomic,strong) NSMutableArray * orderArray;
///次序,用来乱序打乱拼图
@property (nonatomic,strong) NSMutableArray * disorderArray;
///次序,用来拼图移动位置记录
@property (nonatomic,strong) NSMutableArray * puzzleArray;
///图片原图
@property (nonatomic,strong) UIImage * puzzleImage;
///行、列数【难度】
@property (nonatomic,assign) NSInteger rows;
///方块图间距
@property (nonatomic,assign) CGFloat itemSpace;
///四周边距
@property (nonatomic,assign) CGFloat marginSpace;

///是否允许拼图
@property (nonatomic,assign) BOOL allowJoint;
///拖动拼图
@property (nonatomic,strong) UIImageView * panImageView;
///拖动拼图Frame
@property (nonatomic,assign) CGRect  panImageFrame;
- (instancetype)initWithFrame:(CGRect)frame
                         rows:(NSInteger)rows
                  puzzleImage:(UIImage *)puzzleImage{
    self = [super initWithFrame:frame];
    if (self) {
        _rows = rows;
        _puzzleImage = puzzleImage;
        [self setupPP];
    }
    return self;
}

- (void)setupPP{
    self.userInteractionEnabled = NO;
    _allowJoint = YES;
    _orderArray = [NSMutableArray array];
    _disorderArray = [NSMutableArray array];
    _puzzleArray = [NSMutableArray array];
    
//    _rows = 6;
    _itemSpace = floor(_rows*xkScale/(_rows/2));
    _marginSpace = floor(_rows*xkScale);
    
    self.backgroundColor = [UIColor whiteSmoke];
    
    
    [self setupOrderArray:(_rows * _rows)];
    
    ///如果图片的大小大于当前宽度,就压缩
    if (_puzzleImage.size.width > CGRectGetWidth(self.frame)) {
        UIGraphicsBeginImageContextWithOptions(self.bounds.size, NO, 1);
        [_puzzleImage drawInRect:CGRectMake(0,0,self.bounds.size.width,self.bounds.size.height)];
        UIImage* newImage = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
        _puzzleImage = newImage;
    }
  
    CGFloat pWidth = (CGRectGetWidth(self.frame)  - _itemSpace*(_rows-1) - _marginSpace*2)/_rows;
    CGFloat pHeight = (CGRectGetHeight(self.frame)  - _itemSpace*(_rows-1) - _marginSpace*2)/_rows;
    
    for (int i = 0; i < _rows; i ++) {
        for (int j = 0; j < _rows; j ++) {
            NSInteger order = _rows * i + j;
            NSLog(@"order = %ld",order);
            /*
            NSInteger indexes_x = 0;
            NSInteger indexes_y = 0;
            
            if (order < (_rows *_rows) - 1) {
                NSInteger location = [_disorderArray[order] integerValue];
                indexes_y = location/_rows;///第几行
                indexes_x = location%_rows;///第几个
            }
            else{
                indexes_y = _rows - 1;
                indexes_x = _rows - 1;
            }
            CGFloat x_img = _marginSpace + (indexes_x)*(pWidth + _itemSpace);
            CGFloat y_img = _marginSpace + (indexes_y)*(pHeight + _itemSpace);
            */
            CGFloat x = _marginSpace + (j)*(pWidth + _itemSpace);
            CGFloat y = _marginSpace + (i)*(pHeight + _itemSpace);
            

            UIImageView *imgView = [self puzzleImageWithFrame:CGRectMake(x, y, pWidth, pHeight)];
            //将UIImage转化成CGImage
            CGImageRef imageRef = CGImageCreateWithImageInRect(_puzzleImage.CGImage, CGRectMake(x, y, pWidth, pHeight));
            //将CGImage转化成UIImage
            UIImage *imageNew = [UIImage imageWithCGImage:imageRef];
            imgView.image = imageNew;
            ///用来标记view
            imgView.tag = order + 1;
            ///用来记录view位置
            imgView.accessibilityValue = [NSString stringWithFormat:@"%ld",order + 1];
            [self addSubview:imgView];
            
            if (imgView.tag == (_rows * _rows)) {
                imgView.image = [UIImage imageNamed:@"pp_chunk"];
                imgView.backgroundColor = [UIColor whiteSmoke];
            }
        }
    }
    
    dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, 1.5 * NSEC_PER_SEC);
    dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
        [self startDisorganizePuzzleImage];
    });
}
- (void)setupOrderArray:(NSInteger)count{
    for (int i = 1; i<=count; i ++) {
        [_orderArray addObject:[NSString stringWithFormat:@"%d",i]];
        [_disorderArray addObject:[NSString stringWithFormat:@"%d",i]];
        [_puzzleArray addObject:[NSString stringWithFormat:@"%d",i]];
        
    }
}

打乱拼图顺序

打乱拼图顺序的算法和规则,可以根据打乱的程度或者次数,通过递归添加结束条件

- (void)setupDisorganizePuzzleImageNumber:(NSInteger)number{
    if (number <= 0) {
        self.userInteractionEnabled = YES;
        return;
    }
    self.userInteractionEnabled = NO;
    ///获取空白格
    UIImageView *emImg = [self viewWithTag:(_rows * _rows)];
    ///获取空白格的位置
    NSInteger emLocation = [emImg.accessibilityValue integerValue];
    ///通过空白格位置,获取四周可以移动的格子的位置与tag
    NSMutableArray *arrayLoc = [NSMutableArray array];
    NSInteger  upLocation = emLocation - _rows;
    NSInteger  downLocation = emLocation + _rows;
    NSInteger  leftLocation = emLocation - 1;
    NSInteger  righjtLocation = emLocation + 1;
    if (upLocation > 0) {///上
        [arrayLoc addObject:@(upLocation)];
    }
    if (downLocation <= (_rows*_rows)) {///下
        [arrayLoc addObject:@(downLocation)];
    }
    if (leftLocation%_rows != 0 && leftLocation <= (_rows*_rows)) {///左
        [arrayLoc addObject:@(leftLocation)];
    }
    if (righjtLocation%_rows != 1 && righjtLocation <= (_rows*_rows)) {///右
        [arrayLoc addObject:@(righjtLocation)];
    }
    ///随机获取一个转移目标
    NSInteger random = arc4random() % arrayLoc.count;
    NSInteger targetLocation = [arrayLoc[random] integerValue];
    NSInteger targetIndex = [_disorderArray indexOfObject:[NSString stringWithFormat:@"%ld",targetLocation]];
    ///获取目标试图
    UIImageView *targetImg = [self viewWithTag:targetIndex + 1];
    if (targetImg) {
        CGRect targetRect = CGRectMake(CGRectGetMinX(targetImg.frame),
                                       CGRectGetMinY(targetImg.frame),
                                       CGRectGetWidth(targetImg.frame),
                                       CGRectGetHeight(targetImg.frame));
        CGRect emRect = CGRectMake(CGRectGetMinX(emImg.frame),
                                   CGRectGetMinY(emImg.frame),
                                   CGRectGetWidth(emImg.frame),
                                   CGRectGetHeight(emImg.frame));
        [UIView animateWithDuration:0.01 animations:^{
            emImg.frame = targetRect;
            targetImg.frame = emRect;
        } completion:^(BOOL finished) {
            ///处理交换【打乱次序】
            NSInteger emIndex = [_disorderArray indexOfObject:emImg.accessibilityValue];
            [_disorderArray exchangeObjectAtIndex:(targetIndex) withObjectAtIndex:(emIndex)];
            
            ///切换保存顺序【拼图】
            NSInteger accesTarget = [targetImg.accessibilityValue integerValue] - 1;
            NSInteger accesEm = [emImg.accessibilityValue integerValue] - 1;
            [_puzzleArray exchangeObjectAtIndex:(accesTarget) withObjectAtIndex:(accesEm)];
            
            targetImg.accessibilityValue = [NSString stringWithFormat:@"%ld",emLocation];
            emImg.accessibilityValue = [NSString stringWithFormat:@"%ld",targetLocation];
            
            
            [self setupDisorganizePuzzleImageNumber:number - 1];
        }];
    }
}

拼图点击手势(空白格不允许)

///拼图点击事件
- (void)puzzleImageTapClick:(UITapGestureRecognizer *)tap{
    NSInteger tapTag = tap.view.tag;
    UIImageView *tapImg = [self viewWithTag:tapTag];
    [self puzzleImageTapGestureHandler:tapImg];
}

///点击手势操作
- (void)puzzleImageTapGestureHandler:(UIImageView *)puzzleImage{
    if (!_allowJoint) {
        return;
    }
    NSInteger emTag = (_rows * _rows);
    NSInteger tapTag = puzzleImage.tag;
    if (emTag == tapTag) {
        return;
    }
    UIImageView *emImg = [self viewWithTag:emTag];
    UIImageView *tapImg = puzzleImage;
    
    CGFloat emMinX = floor(CGRectGetMinX(emImg.frame));
    CGFloat emMaxX = floor(CGRectGetMaxX(emImg.frame));
    CGFloat emMinY = floor(CGRectGetMinY(emImg.frame));
    CGFloat emMaxY = floor(CGRectGetMaxY(emImg.frame));
    
    CGFloat tapMinX = floor(CGRectGetMinX(tapImg.frame));
    CGFloat tapMaxX = floor(CGRectGetMaxX(tapImg.frame));
    CGFloat tapMinY = floor(CGRectGetMinY(tapImg.frame));
    CGFloat tapMaxY = floor(CGRectGetMaxY(tapImg.frame));
 
    BOOL isExchange = NO;
    if ((tapMinX == emMinX) &&
        fabs((tapMaxY + _itemSpace) - emMinY) < 5*xkScale){
        isExchange = YES;
    }
    else if ((tapMinX == emMinX) &&
             fabs((emMaxY + _itemSpace) - tapMinY) < 5*xkScale){
        isExchange = YES;
    }
    else if ((tapMinY == emMinY) &&
             fabs((tapMaxX + _itemSpace) - emMinX) < 5*xkScale){
        isExchange = YES;
    }
    else if ((tapMinY == emMinY) &&
             fabs((emMaxX + _itemSpace) - tapMinX) < 5*xkScale){
        isExchange = YES;
    }
    else{
        isExchange = NO;
    }
    
    CGRect tapRect = CGRectMake(CGRectGetMinX(tapImg.frame),
                                CGRectGetMinY(tapImg.frame),
                                CGRectGetWidth(tapImg.frame),
                                CGRectGetHeight(tapImg.frame));
    CGRect emRect = CGRectMake(CGRectGetMinX(emImg.frame),
                               CGRectGetMinY(emImg.frame),
                               CGRectGetWidth(emImg.frame),
                               CGRectGetHeight(emImg.frame));
    if (isExchange) {
        NSLog(@"允许交换");
        [UIView animateWithDuration:0.3 animations:^{
            _allowJoint = NO;
            emImg.frame = tapRect;
            tapImg.frame = emRect;
        } completion:^(BOOL finished) {
            
            
            NSInteger accesTap = [tapImg.accessibilityValue integerValue];
            NSInteger accesEm = [emImg.accessibilityValue integerValue];
            ///因为accessibilityValue与tag一样,索引需要减1
            [_puzzleArray exchangeObjectAtIndex:(accesTap - 1) withObjectAtIndex:(accesEm - 1)];
            tapImg.accessibilityValue = [NSString stringWithFormat:@"%ld",accesEm];
            emImg.accessibilityValue = [NSString stringWithFormat:@"%ld",accesTap];
            _allowJoint = YES;
            if ([self isPuzzleImageFinish]) {
                NSLog(@"拼图完成");
                [self puzzleImageFinishHandler];
            }
            else{
                NSLog(@"继续加油");
            }
            
        }];
    }
    else{
        NSLog(@"不允许交换");
    }
}

拼图拖动手势(空白格不允许)

/// 拼图拖动
- (void)puzzleImagePanGesture:(UIPanGestureRecognizer *)pan{
    _panImageView = (UIImageView *)pan.view;
    [self bringSubviewToFront:pan.view];
    if (_panImageView.tag == (_rows * _rows)) {///空白格
        
    }
    else{
        if (pan.state == UIGestureRecognizerStateBegan) {
            _panImageFrame = pan.view.frame;
            _panImageView = (UIImageView *)pan.view;
        }
        else if (pan.state == UIGestureRecognizerStateChanged){
            //获取偏移量
            CGPoint transP = [pan translationInView:pan.view];
            // 移动图片控件
            CGRect tapRect = CGRectMake(CGRectGetMinX(pan.view.frame) + transP.x,
                                        CGRectGetMinY(pan.view.frame) + transP.y,
                                        CGRectGetWidth(pan.view.frame),
                                        CGRectGetHeight(pan.view.frame));
            pan.view.frame = tapRect;
            // 复位,表示相对上一次位置复位重置
            [pan setTranslation:CGPointZero inView:pan.view];
        }
        else if (pan.state == UIGestureRecognizerStateEnded){
            if (!_allowJoint) {
                [UIView animateWithDuration:0.1 animations:^{
                    _allowJoint = NO;
                    _panImageView.frame = _panImageFrame;
                } completion:^(BOOL finished) {
                    _allowJoint = YES;
                }];
                return;
            }
            
            NSInteger emTag = (_rows * _rows);
            UIImageView *emImg = [self viewWithTag:emTag];
            CGPoint point1 = _panImageView.center;
            CGPoint point2 = emImg.center;
            CGFloat distance = sqrt(pow((point1.x - point2.x), 2) + pow((point1.y - point2.y), 2));
            if (distance <= CGRectGetHeight(_panImageFrame)/2) {///中心点相差小于20的,允许判断是否交换位置
                [self puzzleImagePanGestureHandler:_panImageView defaultFrame:_panImageFrame];
            }
            else{///放回原来位置
                [UIView animateWithDuration:0.1 animations:^{
                    _allowJoint = NO;
                    _panImageView.frame = _panImageFrame;
                } completion:^(BOOL finished) {
                    _allowJoint = YES;
                }];
            }
            
        }
        else{
            
        }
    }
}

///拖动手势操作
- (void)puzzleImagePanGestureHandler:(UIImageView *)puzzleImage defaultFrame:(CGRect)defaultFrame{
    
    NSInteger emTag = (_rows * _rows);
    NSInteger tapTag = puzzleImage.tag;
    if (emTag == tapTag) {
        return;
    }
    UIImageView *emImg = [self viewWithTag:emTag];
    UIImageView *tapImg = puzzleImage;
    
    CGFloat emMinX = floor(CGRectGetMinX(emImg.frame));
    CGFloat emMaxX = floor(CGRectGetMaxX(emImg.frame));
    CGFloat emMinY = floor(CGRectGetMinY(emImg.frame));
    CGFloat emMaxY = floor(CGRectGetMaxY(emImg.frame));
    
    CGFloat tapMinX = floor(CGRectGetMinX(defaultFrame));
    CGFloat tapMaxX = floor(CGRectGetMaxX(defaultFrame));
    CGFloat tapMinY = floor(CGRectGetMinY(defaultFrame));
    CGFloat tapMaxY = floor(CGRectGetMaxY(defaultFrame));
   
    BOOL isExchange = NO;
    if ((tapMinX == emMinX) &&
        fabs((tapMaxY + _itemSpace) - emMinY) < 5*xkScale){
        isExchange = YES;
    }
    else if ((tapMinX == emMinX) &&
             fabs((emMaxY + _itemSpace) - tapMinY) < 5*xkScale){
        isExchange = YES;
    }
    else if ((tapMinY == emMinY) &&
             fabs((tapMaxX + _itemSpace) - emMinX) < 5*xkScale){
        isExchange = YES;
    }
    else if ((tapMinY == emMinY) &&
             fabs((emMaxX + _itemSpace) - tapMinX) < 5*xkScale){
        isExchange = YES;
    }
    else{
        isExchange = NO;
    }
    
    CGRect emRect = CGRectMake(CGRectGetMinX(emImg.frame),
                               CGRectGetMinY(emImg.frame),
                               CGRectGetWidth(emImg.frame),
                               CGRectGetHeight(emImg.frame));
    if (isExchange) {
        NSLog(@"允许交换");
        [UIView animateWithDuration:0.3 animations:^{
            _allowJoint = NO;
            emImg.frame = defaultFrame;
            tapImg.frame = emRect;
        } completion:^(BOOL finished) {
            NSInteger accesTap = [tapImg.accessibilityValue integerValue];
            NSInteger accesEm = [emImg.accessibilityValue integerValue];
            
            [_puzzleArray exchangeObjectAtIndex:(accesTap - 1) withObjectAtIndex:(accesEm - 1)];
            tapImg.accessibilityValue = [NSString stringWithFormat:@"%ld",accesEm];
            emImg.accessibilityValue = [NSString stringWithFormat:@"%ld",accesTap];
            _allowJoint = YES;
            if ([self isPuzzleImageFinish]) {
                NSLog(@"拼图完成");
                [self puzzleImageFinishHandler];
            }
            else{
                NSLog(@"继续加油");
            }
        }];
    }
    else{
        NSLog(@"不允许交换");
        ///原图归位
        [UIView animateWithDuration:0.3 animations:^{
            _allowJoint = NO;
            tapImg.frame = defaultFrame;
        } completion:^(BOOL finished) {
            _allowJoint = YES;
        }];
        
    }
}

判断拼图是否完成

- (BOOL)isPuzzleImageFinish{
    NSString *order = [_orderArray componentsJoinedByString:@""];
    NSString *after = [_puzzleArray componentsJoinedByString:@""];
    return [order isEqualToString:after];
}

效果

iOS-宫格拼图 iOS-宫格拼图 iOS-宫格拼图 iOS-宫格拼图

 

上一篇:Web API-本地存储


下一篇:iOS 画贝塞尔曲线 连续曲线 平滑曲线 曲线图表