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