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