如上图所示,完成如上功能。下面主要介绍下功能的实现:
一: 编辑页面
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(@"保存失败");
}
}];
}
结语:
以上就是九宫格的核心功能实现
如有问题请下方留言指正
如有帮助请支持一下