iOS 九宫格切图功能实现

image.png

如上图所示,完成如上功能。下面主要介绍下功能的实现:

一: 编辑页面

1: UI布局
布局较为简单,就不详细阐述了,只说下注意点。因为图片需要滑动,所以图片是加在UIScrollView上的。例如九宫格的布局,scrollView宽高是屏幕的宽度,例如在iphone7中,宽高都是375,那么Imageview的宽高是否也和scrollView一样等于375呢,看起来是相等的,实则不然,因为Imageview是需要滑动的,如果相等,scrollView的contentSize如果大于Imageview的宽高,scrollView滑动过后,就会出现空白的区域。所以Imageview的宽高应该等于scrollView的contentSize的大小,至于contentSize的大小怎么计算,在下面的操作逻辑中会写到!

2: 操作逻辑
(1 ) 用户可向上下左右四个方向滑动图片,来调整图片位置
注意:图片上下左右需要滑动多少,由图片的原始宽度和原始高度决定,根据图片的原始宽高算出scrollView的contentSize,contentSize就是图片能够滑动的范围,也是ImageView.frame 的 width 和height 。
计算ImageView的宽度 (img_Width) :
原始图片的高度除以scrollView的Frame高度,得到图片的高度比例
原始图片的宽度除以图片的高度比例,得到ImageView需要显示的图片宽度
计算ImageView的高度(img_Height) :
原始图片的宽度除以scrollView的Frame宽度,得到图片的宽度比例
原始图片的高度除以图片的宽度比例,得到ImageView需要显示的图片高度

 图片滑动范围: self.scrollView.contentSize = CGSizeMake(img_Width, img_Height);

代码如下:

//计算图片滑动范围
-(void)setScrollviewContentOffset{
   /*
    横向的宽度等于:
       原始图片的高度除以ImageView的Frame高度,得到图片的高度比例
       原始图片的宽度除以图片的高度比例,得到ImageView需要显示的图片宽度
    */
    //_img_Width: imageview的宽度
   _img_Width = self.imageSize.width / (self.imageSize.height / self.scrollView.frame.size.height);
  
   /*
     纵向的高度等于:
       原始图片的宽度除以ImageView的Frame宽度,得到图片的宽度比例
       原始图片的高度除以图片的宽度比例,得到ImageView需要显示的图片高度
    */
      //_img_Height: imageview的高度
   _img_Height = self.imageSize.height / (self.imageSize.width /self.scrollView.frame.size.width);

   if (_img_Width < self.frame.size.width) {
       _img_Width = self.frame.size.width;
   }
    
   if (_img_Height < self.frame.size.height) {
       _img_Height = self.frame.size.height;
   }
  
    NSLog(@"_img_Width ==%ld, _img_Height == %ld",_img_Width,_img_Height);
   NSLog(@"imageSize.Width ==%f, imageSize.height == %f",self.imageSize.width,self.imageSize.height);
   
    //设置图片滑动范围
   self.scrollView.contentSize = CGSizeMake(_img_Width, _img_Height);
}

(2) 用户可通过双指缩放来调整图片大小
需求中放大缩小的比例是2和1,也就是原始图片不能再缩小,最小比例是1.0
功能实现:这里直接采用 scrollView 的 maximumZoomScale 和 minimumZoomScale 属性,设置缩放倍数。

代码如下:

 //设置缩放倍数
    _scrollView.maximumZoomScale = 2.0;
    _scrollView.minimumZoomScale = 1.0;
    _scrollView.delegate = self;

#pragma mark ============ UIScrollViewDelegate ============
//设置需要缩放的view
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView {
   return self.imageView;
}

//在这个代理方法里面设置滚动范围、调整放大图片的位置,固定imageView始终在整个content的居中位置:(如果不设置,放大后图片按照原来比例frame的X,Y值也会跟随比例变化,图片就跑偏了)
- (void)scrollViewDidZoom:(UIScrollView *)scrollView {
    CGRect frame = self.imageView.frame;
    frame.origin.y = (self.scrollView.frame.size.height - self.imageView.frame.size.height) > 0 ? (self.scrollView.frame.size.height - self.imageView.frame.size.height) * 0.5 : 0;
    frame.origin.x = (self.scrollView.frame.size.width - self.imageView.frame.size.width) > 0 ? (self.scrollView.frame.size.width - self.imageView.frame.size.width) * 0.5 : 0;
    self.imageView.frame = frame;
    self.scrollView.contentSize = CGSizeMake(self.imageView.frame.size.width, self.imageView.frame.size.height);
}

(3) 完成图片操作后,点击下一步,截取图片
刚开始做这个功能,截图一直比较模糊,是因为使用了:UIGraphicsBeginImageContext(size),
UIGraphicsBeginImageContext(size) 等价于 UIGraphicsBeginImageContextWithOptions(size,NO,1.0)
图片再切的时候它的scale,即缩放比率这里默认不缩放处理,那么剪切出来的图片用UI开发那边的说法即为1倍图,而我们iOS的手机现在市面上的一般都是retain屏幕,像素用一 倍图处理处理的图片当然显示出来会比较模糊。那么。我们就可以将需要切的图片的scale设置为 [UIScreen mainScreen].scale
使用 UIGraphicsBeginImageContextWithOptions(size, YES, [UIScreen mainScreen].scale)

代码如下:

// 使用上下文截图,并使用指定的区域裁剪
- (UIImage *)screenShot
{
    // 将要被截图的view
    CGSize size = self.sudokuView.frame.size;

    // 开启上下文,使用参数之后,截出来的是原图(YES  0.0 质量高)
    UIGraphicsBeginImageContextWithOptions(size, YES, [UIScreen mainScreen].scale);

    // 裁剪的矩形范围
    CGRect rect = CGRectMake(0, 0, size.width, size.height);
    
    //注:iOS7以后renderInContext:由drawViewHierarchyInRect:afterScreenUpdates:替代
    [self.sudokuView drawViewHierarchyInRect:rect afterScreenUpdates:NO];
       
    // 从上下文中,取出UIImage
    UIImage *snapshot = UIGraphicsGetImageFromCurrentImageContext();

    //结束上下文(移除栈顶上下文)
    UIGraphicsEndImageContext();
     
    return snapshot;
}

二: 保存页面

1: UI布局
一张图片要切割成多张,切割后的每一张大小相等,每一张都能点击保存,所以布局用 UICollectionView
切割的划分白线显示,设置UICollectionViewFlowLayout 的.minimumInteritemSpacing 和 minimumLineSpacing即可
//相邻两个cell的最小间距
layout.minimumInteritemSpacing = iPad ? 2.0 : 1.0;
//两行之间的间距
layout.minimumLineSpacing = iPad ? 2.0 : 1.0;
设置背景色: self.collectionView.backgroundColor = [UIColor whiteColor];

计算cell大小,要减去分割线的间距

//定义每一个cell的大小
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath{
    return CGSizeMake((self.imageView.frame.size.width - (iPad ? 4:2)) / 3, (self.imageView.frame.size.width - (iPad ? 4:2)) / 3);
}

2: 图片切割功能
图片切割,要根据三、六、九、十二宫格的类型进行切割,例如十二宫格,是3行4列,切割后的图片是12张;三宫格是1行1列,切割后的图片是3张。
切割完成后,将图片保存在数组中,图片数组就是UICollectionView的数据源。

代码如下:

  //对大图进行切割,切割完成得到图片数组
- (void)captureImage {
    //切割成正方形,宽高一样
    CGFloat  imgView_width = (self.imageView.frame.size.width / 3);
    CGFloat  imgView_height = (self.imageView.frame.size.width / 3);

    if (self.imgCount == 3) {
        // 把图片切成三宫格,就是竖着切3段
        for (int i = 0; i < 3; i++) {
            UIImage *theImage = [self captureView:self.imageView frame:CGRectMake(i*imgView_width, 0, imgView_width,imgView_height)];
            [self.imageArr addObject:theImage];
         }
        
    }else if (self.imgCount == 6){
        // 把图片切成六宫格,就是横着切2段,竖着切3段,2个for循环,对已知image进行切割
        for (int i = 0; i < 2; i++) {
            for (int j = 0; j<3; j++) {
                    UIImage *theImage = [self captureView:self.imageView frame:CGRectMake(j*imgView_width,i*imgView_height, imgView_width, imgView_height)];
                    [self.imageArr addObject:theImage];
            }
         }
        
    }else if (self.imgCount == 9){
       // 把图片切成九宫格,就是横着切3段,竖着切3段,2个for循环,对已知image进行切割
        for (int i = 0; i < 3; i++) {
            for (int j = 0; j<3; j++) {
                    UIImage *theImage = [self captureView:self.imageView frame:CGRectMake(j*imgView_width, i*imgView_height, imgView_width, imgView_height)];
                    [self.imageArr addObject:theImage];
             }
        }
        
    }else if (self.imgCount == 12){
        // 把图片切成十二宫格,就是横着切4段,竖着切3段,2个for循环,对已知image进行切割
        for (int i = 0; i < 4; i++) {
            for (int j = 0; j<3; j++) {
                NSLog(@"i===== %d j===== %d",i,j);
                UIImage *theImage = [self captureView:self.imageView frame:CGRectMake(j*imgView_width, i*imgView_height, imgView_width, imgView_height)];
                [self.imageArr addObject:theImage];
            }
        }
    }       
}

编辑页讲到了图片裁剪适应屏幕比例([UIScreen mainScreen].scale)的问题,这里也要考虑这个问题,那么我们的剪切位置frame.orgain.x与y都应该乘以scale的倍数。图片的剪切frame.size.width与height也应该乘以scale,而且在绘制好之后的压缩处理的时候也要除以[UIScreen mainScreen].scale,以达到选择区域的宽度和高度的图片范围)

代码如下:

//切割图片
- (UIImage *)captureView:(UIView *)theView frame:(CGRect)fra {  
    //屏幕比例
    NSInteger multiple = [UIScreen mainScreen].scale;
    // 开启位图上下文
     UIGraphicsBeginImageContextWithOptions(theView.frame.size, NO, multiple);
   // 裁剪区域
    CGRect rect = CGRectMake(fra.origin.x * multiple, fra.origin.y * multiple, fra.size.width *multiple, fra.size.height * multiple);
    // 进行渲染
     CGContextRef ctx = UIGraphicsGetCurrentContext();
     [self.imageView.layer renderInContext:ctx];

    // 生成图片
    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
    //关闭上下文
    UIGraphicsEndImageContext();
    
    newImage = [UIImage imageWithCGImage:CGImageCreateWithImageInRect(self.imageView.image.CGImage, rect)];
    newImage = [self harfWithImage:newImage toSize:CGSizeMake(newImage.size.width /multiple, newImage.size.height / multiple)] ;
    return newImage;    
}

然后对照片进行“缩”处理,这个时候,获取的图片大小是我们想要的2倍大小。然后就需要进一步的“缩”1/2处理。
再一次的缩处理,我们还是要缩放选择2.0.即目前的倍率(不然选择1倍或者不缩放处理,结果一样是模糊的图片)。然后将我们的图片绘制到一个宽度和高度都是目前一般的image就可以了。

代码如下(将传入的size大小直接定为传入image的宽和高的一半就好了):

-(UIImage *)harfWithImage:(UIImage *)image toSize:(CGSize)size{
    UIGraphicsBeginImageContextWithOptions(size, YES, [UIScreen mainScreen].scale);
    [image drawInRect:CGRectMake(0, 0, size.width, size.height)];
    UIImage *resultImg = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return resultImg;
}

3: 图片保存功能
一次性保存多张图片,有时候会出现保存不全(例如保存12张图,实际只保存了6张)或保存的图片顺序错乱的问题,使用如下方法解决:


//保存全部图片
-(void)saveAllBtn:(UIButton *)sender{
   for (UIImage *image in self.sudokuSaveView.imageArr){
            [self loadImageFinished:image];
       }
}

    // 存储图片到相册
- (void)loadImageFinished:(UIImage *)image{
    [[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
        //写入图片到相册
        [PHAssetChangeRequest creationRequestForAssetFromImage:image];
    } completionHandler:^(BOOL success, NSError * _Nullable error) {
        NSLog(@"success = %d, error = %@", success, error);
        if(success){
            dispatch_async(dispatch_get_main_queue(), ^{
                NSLog(@"保存成功,可以在这里做保存完成的操作");
               
            });
        } else {
            NSLog(@"保存失败");
        }
    }];
}

结语:

以上就是九宫格的核心功能实现

如有问题请下方留言指正

如有帮助请支持一下

你可能感兴趣的:(iOS 九宫格切图功能实现)