SLImageClipController.m 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346
  1. //
  2. // SLImageClipController.m
  3. // DarkMode
  4. //
  5. // Created by wsl on 2019/11/2.
  6. // Copyright © 2019 wsl. All rights reserved.
  7. //
  8. #import "SLImageClipController.h"
  9. #import "SLImageZoomView.h"
  10. #import "SLGridView.h"
  11. #import "UIView+SLImage.h"
  12. #define KBottomMenuHeight 100 //底部菜单高度
  13. #define KGridTopMargin 40 //顶部间距
  14. #define KGridBottomMargin 20 //底部间距
  15. #define KGridLRMargin 20 //左右边距
  16. @interface SLImageClipController ()<UIScrollViewDelegate, SLGridViewDelegate, SLImageZoomViewDelegate>
  17. // 缩放视图
  18. @property (nonatomic, strong) SLImageZoomView *zoomView;
  19. //网格视图 裁剪框
  20. @property (nonatomic, strong) SLGridView *gridView;
  21. /// 原始位置区域
  22. @property (nonatomic, assign) CGRect originalRect;
  23. /// 最大裁剪区域
  24. @property (nonatomic, assign) CGRect maxGridRect;
  25. /// 裁剪区域
  26. //@property (nonatomic, assign) CGRect clipRect;
  27. /// 当前旋转角度
  28. @property (nonatomic, assign) NSInteger rotateAngle;
  29. /// 图像方向
  30. @property (nonatomic, assign) UIImageOrientation imageOrientation;
  31. @property (nonatomic, strong) UIButton *rotateBtn; //旋转操作
  32. @property (nonatomic, strong) UIButton *cancleClipBtn; //取消操作
  33. @property (nonatomic, strong) UIButton *recoveryBtn; //还原
  34. @property (nonatomic, strong) UIButton *doneClipBtn; //保存操作
  35. @end
  36. @implementation SLImageClipController
  37. #pragma mark - Override
  38. - (void)viewDidLoad {
  39. [super viewDidLoad];
  40. self.view.backgroundColor = [UIColor blackColor];
  41. [self setupUI];
  42. }
  43. - (BOOL)prefersStatusBarHidden {
  44. return YES;
  45. }
  46. - (void)dealloc {
  47. NSLog(@"图片裁剪视图释放了");
  48. }
  49. #pragma mark - UI
  50. - (void)setupUI {
  51. self.zoomView.image = self.image;
  52. self.maxGridRect = CGRectMake(KGridLRMargin, KGridTopMargin, self.view.sl_width - KGridLRMargin * 2, self.view.sl_height - KGridTopMargin - KGridBottomMargin- KBottomMenuHeight);
  53. CGSize newSize = CGSizeMake(self.view.sl_width - 2 * KGridLRMargin, (self.view.sl_width - 2 * KGridLRMargin)*self.image.size.height/self.image.size.width);
  54. if (newSize.height > self.maxGridRect.size.height) {
  55. newSize = CGSizeMake(self.maxGridRect.size.height*self.image.size.width/self.image.size.height, self.maxGridRect.size.height);
  56. self.zoomView.sl_size = newSize;
  57. self.zoomView.sl_y = KGridTopMargin;
  58. self.zoomView.sl_centerX = self.view.sl_width/2.0;
  59. }else {
  60. self.zoomView.sl_size = newSize;
  61. self.zoomView.center = CGPointMake(self.view.sl_width/2.0, (self.view.sl_height - KBottomMenuHeight)/2.0);
  62. }
  63. [self.view addSubview:self.zoomView];
  64. self.zoomView.imageView.frame = self.zoomView.bounds;
  65. self.originalRect = self.zoomView.frame;
  66. self.gridView.gridRect = self.zoomView.frame;
  67. self.gridView.maxGridRect = self.maxGridRect;
  68. [self.view addSubview:self.gridView];
  69. [self.view addSubview:self.rotateBtn];
  70. [self.view addSubview:self.cancleClipBtn];
  71. [self.view addSubview:self.recoveryBtn];
  72. [self.view addSubview:self.doneClipBtn];
  73. }
  74. #pragma mark - Getter
  75. - (SLImageZoomView *)zoomView {
  76. if (!_zoomView) {
  77. _zoomView = [[SLImageZoomView alloc] initWithFrame:CGRectMake(KGridLRMargin, KGridTopMargin, self.view.sl_width - KGridLRMargin *2,( self.view.sl_width - KGridLRMargin *2)*self.image.size.height/self.image.size.width)];
  78. _zoomView.sl_centerY = (self.view.sl_height - KBottomMenuHeight)/2.0;
  79. _zoomView.backgroundColor = [UIColor blackColor];
  80. _zoomView.zoomViewDelegate = self;
  81. }
  82. return _zoomView;
  83. }
  84. - (SLGridView *)gridView {
  85. if (!_gridView) {
  86. _gridView = [[SLGridView alloc] initWithFrame:self.view.bounds];
  87. _gridView.delegate = self;
  88. }
  89. return _gridView;
  90. }
  91. - (UIButton *)rotateBtn {
  92. if (_rotateBtn == nil) {
  93. _rotateBtn = [[UIButton alloc] initWithFrame:CGRectMake(30, self.view.sl_height - KBottomMenuHeight, 40, 30)];
  94. [_rotateBtn setTitle:@"旋转" forState:UIControlStateNormal];
  95. [_rotateBtn setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
  96. _rotateBtn.titleLabel.font = [UIFont boldSystemFontOfSize:16];
  97. [_rotateBtn addTarget:self action:@selector(rotateBtnClicked:) forControlEvents:UIControlEventTouchUpInside];
  98. }
  99. return _rotateBtn;
  100. }
  101. - (UIButton *)cancleClipBtn {
  102. if (_cancleClipBtn == nil) {
  103. _cancleClipBtn = [[UIButton alloc] initWithFrame:CGRectMake(30, self.view.sl_height - 30 - 20, 40, 30)];
  104. [_cancleClipBtn setImage:[UIImage imageNamed:@"EditImageClipCancel"] forState:UIControlStateNormal];
  105. _cancleClipBtn.titleLabel.font = [UIFont systemFontOfSize:14];
  106. [_cancleClipBtn addTarget:self action:@selector(cancleClipClicked:) forControlEvents:UIControlEventTouchUpInside];
  107. }
  108. return _cancleClipBtn;
  109. }
  110. - (UIButton *)recoveryBtn {
  111. if (_recoveryBtn == nil) {
  112. _recoveryBtn = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 40, 30)];
  113. _recoveryBtn.sl_centerX = self.view.sl_width/2.0;
  114. _recoveryBtn.sl_centerY = self.cancleClipBtn.center.y;
  115. [_recoveryBtn setTitle:@"还原" forState:UIControlStateNormal];
  116. [_recoveryBtn setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
  117. _recoveryBtn.titleLabel.font = [UIFont boldSystemFontOfSize:16];
  118. [_recoveryBtn addTarget:self action:@selector(recoveryClicked:) forControlEvents:UIControlEventTouchUpInside];
  119. }
  120. return _recoveryBtn;
  121. }
  122. - (UIButton *)doneClipBtn {
  123. if (_doneClipBtn == nil) {
  124. _doneClipBtn = [[UIButton alloc] initWithFrame:CGRectMake(self.view.sl_width - 30 - 40, self.view.sl_height - 30 - 20, 40, 30)];
  125. [_doneClipBtn setImage:[UIImage imageNamed:@"EditImageClipDone"] forState:UIControlStateNormal];
  126. [_doneClipBtn addTarget:self action:@selector(doneClipClicked:) forControlEvents:UIControlEventTouchUpInside];
  127. }
  128. return _doneClipBtn;
  129. }
  130. /// 返回图像方向
  131. - (UIImageOrientation)imageOrientation {
  132. UIImageOrientation orientation = UIImageOrientationUp;
  133. switch (_rotateAngle) {
  134. case 90:
  135. case -270:
  136. orientation = UIImageOrientationRight;
  137. break;
  138. case -90:
  139. case 270:
  140. orientation = UIImageOrientationLeft;
  141. break;
  142. case 180:
  143. case -180:
  144. orientation = UIImageOrientationDown;
  145. break;
  146. default:
  147. break;
  148. }
  149. _imageOrientation = orientation;
  150. return orientation;
  151. }
  152. #pragma mark - HelpMethods
  153. // 放大zoomView区域到指定网格gridRect区域
  154. - (void)zoomInToRect:(CGRect)gridRect{
  155. // 正在拖拽或减速
  156. if (self.zoomView.dragging || self.zoomView.decelerating) {
  157. return;
  158. }
  159. CGRect imageRect = [self.zoomView convertRect:self.zoomView.imageView.frame toView:self.view];
  160. //当网格往图片边缘(x/y轴方向)移动即将出图片边界时,调整self.zoomView.contentOffset和缩放zoomView大小,把网格外的图片区域逐步移到网格内
  161. if (!CGRectContainsRect(imageRect,gridRect)) {
  162. CGPoint contentOffset = self.zoomView.contentOffset;
  163. if (self.imageOrientation == UIImageOrientationRight) {
  164. if (CGRectGetMaxX(gridRect) > CGRectGetMaxX(imageRect)) contentOffset.y = 0;
  165. if (CGRectGetMinY(gridRect) < CGRectGetMinY(imageRect)) contentOffset.x = 0;
  166. }
  167. if (self.imageOrientation == UIImageOrientationLeft) {
  168. if (CGRectGetMinX(gridRect) < CGRectGetMinX(imageRect)) contentOffset.y = 0;
  169. if (CGRectGetMaxY(gridRect) > CGRectGetMaxY(imageRect)) contentOffset.x = 0;
  170. }
  171. if (self.imageOrientation == UIImageOrientationUp) {
  172. if (CGRectGetMinY(gridRect) < CGRectGetMinY(imageRect)) contentOffset.y = 0;
  173. if (CGRectGetMinX(gridRect) < CGRectGetMinX(imageRect)) contentOffset.x = 0;
  174. }
  175. if (self.imageOrientation == UIImageOrientationDown) {
  176. if (CGRectGetMaxY(gridRect) > CGRectGetMaxY(imageRect)) contentOffset.y = 0;
  177. if (CGRectGetMaxX(gridRect) > CGRectGetMaxX(imageRect)) contentOffset.x = 0;
  178. }
  179. self.zoomView.contentOffset = contentOffset;
  180. /** 取最大值缩放 */
  181. CGRect myFrame = self.zoomView.frame;
  182. myFrame.origin.x = MIN(myFrame.origin.x, gridRect.origin.x);
  183. myFrame.origin.y = MIN(myFrame.origin.y, gridRect.origin.y);
  184. myFrame.size.width = MAX(myFrame.size.width, gridRect.size.width);
  185. myFrame.size.height = MAX(myFrame.size.height, gridRect.size.height);
  186. self.zoomView.frame = myFrame;
  187. [self resetMinimumZoomScale];
  188. [self.zoomView setZoomScale:self.zoomView.zoomScale];
  189. }
  190. }
  191. //重置最小缩放系数 只要改变了zoomView大小就重置
  192. - (void)resetMinimumZoomScale {
  193. CGRect rotateoriginalRect = CGRectApplyAffineTransform(self.originalRect, self.zoomView.transform);
  194. if (CGSizeEqualToSize(rotateoriginalRect.size, CGSizeZero)) {
  195. /** size为0时候不能继续,否则minimumZoomScale=+Inf,会无法缩放 */
  196. return;
  197. }
  198. //设置最小缩放系数
  199. CGFloat zoomScale = MAX(CGRectGetWidth(self.zoomView.frame) / CGRectGetWidth(rotateoriginalRect), CGRectGetHeight(self.zoomView.frame) / CGRectGetHeight(rotateoriginalRect));
  200. self.zoomView.minimumZoomScale = zoomScale;
  201. }
  202. //获取网格区域在图片上的相对位置
  203. - (CGRect)rectOfGridOnImageByGridRect:(CGRect)cropRect {
  204. CGRect rect = [self.view convertRect:cropRect toView:self.zoomView.imageView];
  205. return rect;
  206. }
  207. //保存图片完成后调用的方法
  208. - (void)savedPhotoImage:(UIImage*)image didFinishSavingWithError:(NSError *)error contextInfo: (void *)contextInfo {
  209. if (error) {
  210. NSLog(@"保存图片出错%@", error.localizedDescription);
  211. } else {
  212. NSLog(@"保存图片成功");
  213. }
  214. }
  215. #pragma mark - EventsHandle
  216. - (void)rotateBtnClicked:(id)sender {
  217. _rotateAngle = (_rotateAngle+=90)%360;
  218. CGFloat angleInRadians = 0.0f;
  219. switch (_rotateAngle) {
  220. case 90: angleInRadians = M_PI_2; break;
  221. case -90: angleInRadians = -M_PI_2; break;
  222. case 180: angleInRadians = M_PI; break;
  223. case -180: angleInRadians = -M_PI; break;
  224. case 270: angleInRadians = (M_PI + M_PI_2); break;
  225. case -270: angleInRadians = -(M_PI + M_PI_2); break;
  226. default: break;
  227. }
  228. //旋转前获得网格框在图片上选择的区域
  229. CGRect gridRectOfImage = [self rectOfGridOnImageByGridRect:self.gridView.gridRect];
  230. /// 旋转变形
  231. CGAffineTransform transform = CGAffineTransformRotate(CGAffineTransformIdentity, angleInRadians);
  232. self.zoomView.transform = transform;
  233. //transform后,bounds不会变,frame会变
  234. CGFloat width = CGRectGetWidth(self.zoomView.frame);
  235. CGFloat height = CGRectGetHeight(self.zoomView.frame);
  236. //计算旋转之后
  237. CGSize newSize = CGSizeMake(self.view.sl_width - 2 * KGridLRMargin, (self.view.sl_width - 2 * KGridLRMargin)*height/width);
  238. if (newSize.height > self.gridView.maxGridRect.size.height) {
  239. newSize = CGSizeMake(self.gridView.maxGridRect.size.height*width/height, self.gridView.maxGridRect.size.height);
  240. self.zoomView.sl_size = newSize;
  241. self.zoomView.sl_y = KGridTopMargin;
  242. self.zoomView.sl_centerX = self.view.sl_width/2.0;
  243. }else {
  244. self.zoomView.sl_size = newSize;
  245. self.zoomView.center = CGPointMake(self.view.sl_width/2.0, (self.view.sl_height - KBottomMenuHeight)/2.0);
  246. }
  247. self.gridView.gridRect = self.zoomView.frame;
  248. //重置最小缩放系数
  249. [self resetMinimumZoomScale];
  250. CGFloat scale = MIN(CGRectGetWidth(self.zoomView.frame) / width, CGRectGetHeight(self.zoomView.frame) / height);
  251. [self.zoomView setZoomScale:self.zoomView.zoomScale * scale];
  252. // 调整contentOffset
  253. self.zoomView.contentOffset = CGPointMake(gridRectOfImage.origin.x*self.zoomView.zoomScale, gridRectOfImage.origin.y*self.zoomView.zoomScale);
  254. }
  255. - (void)cancleClipClicked:(id)sender {
  256. [self dismissViewControllerAnimated:NO completion:nil];
  257. }
  258. //还原
  259. - (void)recoveryClicked:(id)sender {
  260. self.zoomView.minimumZoomScale = 1;
  261. self.zoomView.zoomScale = 1;
  262. self.zoomView.transform = CGAffineTransformIdentity;
  263. self.zoomView.frame = self.originalRect;
  264. self.gridView.gridRect = self.zoomView.frame;
  265. _rotateAngle = 0;
  266. }
  267. //完成编辑
  268. - (void)doneClipClicked:(id)sender {
  269. [self dismissViewControllerAnimated:NO completion:nil];
  270. UIImage *clipImage = [self.zoomView.imageView sl_imageByViewInRect:[self rectOfGridOnImageByGridRect:_gridView.gridRect]];
  271. UIImage *roImage = [UIImage imageWithCGImage:clipImage.CGImage scale:[UIScreen mainScreen].scale orientation:self.imageOrientation];
  272. [[NSNotificationCenter defaultCenter] postNotificationName:@"sl_ImageClippingComplete" object:nil userInfo:@{@"image" : roImage}];
  273. }
  274. #pragma mark - SLGridViewDelegate
  275. //开始调整
  276. - (void)gridViewDidBeginResizing:(SLGridView *)gridView {
  277. CGPoint contentOffset = self.zoomView.contentOffset;
  278. if (self.zoomView.contentOffset.x < 0) contentOffset.x = 0;
  279. if (self.zoomView.contentOffset.y < 0) contentOffset.y = 0;
  280. [self.zoomView setContentOffset:contentOffset animated:NO];
  281. }
  282. //正在调整
  283. - (void)gridViewDidResizing:(SLGridView *)gridView {
  284. //放大到 >= gridRect
  285. [self zoomInToRect:gridView.gridRect];
  286. }
  287. // 结束调整
  288. - (void)gridViewDidEndResizing:(SLGridView *)gridView {
  289. CGRect gridRectOfImage = [self rectOfGridOnImageByGridRect:gridView.gridRect];
  290. //居中
  291. [UIView animateWithDuration:0.25
  292. delay:0.0
  293. options:UIViewAnimationOptionBeginFromCurrentState
  294. animations:^{
  295. CGSize newSize = CGSizeMake(self.view.sl_width - 2 * KGridLRMargin, (self.view.sl_width - 2 * KGridLRMargin)*gridView.gridRect.size.height/gridView.gridRect.size.width);
  296. if (newSize.height > self.gridView.maxGridRect.size.height) {
  297. newSize = CGSizeMake(self.gridView.maxGridRect.size.height*gridView.gridRect.size.width/gridView.gridRect.size.height, self.gridView.maxGridRect.size.height);
  298. self.zoomView.sl_size = newSize;
  299. self.zoomView.sl_y = KGridTopMargin;
  300. self.zoomView.sl_centerX = self.view.sl_width/2.0;
  301. }else {
  302. self.zoomView.sl_size = newSize;
  303. self.zoomView.center = CGPointMake(self.view.sl_width/2.0, (self.view.sl_height - KBottomMenuHeight)/2.0);
  304. }
  305. //重置最小缩放系数
  306. [self resetMinimumZoomScale];
  307. [self.zoomView setZoomScale:self.zoomView.zoomScale];
  308. // 调整contentOffset
  309. CGFloat zoomScale = self.zoomView.sl_width/gridView.gridRect.size.width;
  310. gridView.gridRect = self.zoomView.frame;
  311. [self.zoomView setZoomScale:self.zoomView.zoomScale * zoomScale];
  312. self.zoomView.contentOffset = CGPointMake(gridRectOfImage.origin.x*self.zoomView.zoomScale, gridRectOfImage.origin.y*self.zoomView.zoomScale);
  313. } completion:^(BOOL finished) {
  314. }];
  315. }
  316. #pragma mark - SLZoomViewDelegate
  317. - (void)zoomViewDidBeginMoveImage:(SLImageZoomView *)zoomView {
  318. self.gridView.showMaskLayer = NO;
  319. }
  320. - (void)zoomViewDidEndMoveImage:(SLImageZoomView *)zoomView {
  321. self.gridView.showMaskLayer = YES;
  322. }
  323. @end