iOS-宮格拼圖

思路

要求設計思路是相似手持拼圖遊戲,拼圖需求要求有一塊爲空白版,做爲移動方塊的預留位置用,經過選擇圖片後在起初對全部圖像方塊隨機打亂順序時,發現隨機打亂順序,沒辦法拼圖完成,拼圖移動是空白快最臨近的上下左右四個圖像塊的移動,在打亂順序的時候,也要按照這個算法邏輯實現,才能拼圖完成;算法

另外邏輯實現上,用tag來記錄圖片,用accessibilityValue 來記錄圖片的實際位置標記;數組

用三個數組來實現順序打亂、正序校驗、拼圖位置的校驗等,起初對三個數組進行相同的初始化值;dom

實現

變量及相關初始化

///次序,用來排序
@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]];
        
    }
}

打亂拼圖順序

打亂拼圖順序的算法和規則,能夠根據打亂的程度或者次數,經過遞歸添加結束條件atom

- (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];
}

效果

   

相關文章
相關標籤/搜索