猎豆优选

ZLImageEditTool.m 33KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958
  1. //
  2. // ZLImageEditTool.m
  3. // ZLPhotoBrowser
  4. //
  5. // Created by long on 2018/5/5.
  6. // Copyright © 2018年 long. All rights reserved.
  7. //
  8. #import "ZLImageEditTool.h"
  9. #import "ZLPhotoConfiguration.h"
  10. #import "ZLFilterItem.h"
  11. #import "ZLClipItem.h"
  12. #import "ZLFilterTool.h"
  13. #import "UIImage+ZLPhotoBrowser.h"
  14. #import "UIButton+EnlargeTouchArea.h"
  15. #import "ZLBrushBoardImageView.h"
  16. #import "ZLDrawItem.h"
  17. @interface ZLImageEditTool ()
  18. {
  19. NSInteger _layoutCount;
  20. ZLImageEditType _type;
  21. ZLPhotoConfiguration *_configuration;
  22. ZLImageEditType _selectToolType;
  23. //计算imageView尺寸时是否交换宽高(旋转图片90°及270°时候值为YES)
  24. BOOL _exchangeImageWH;
  25. //是否正在旋转图片
  26. BOOL _isRotatingImage;
  27. ZLClippingCircle *_ltView;
  28. ZLClippingCircle *_lbView;
  29. ZLClippingCircle *_rtView;
  30. ZLClippingCircle *_rbView;
  31. CGSize _originSize;
  32. }
  33. @property (nonatomic, strong) ZLBrushBoardImageView *imageView;
  34. @property (nonatomic, strong) UIButton *cancelBtn;
  35. @property (nonatomic, strong) UIButton *doneBtn;
  36. @property (nonatomic, strong) UIView *bottomView;
  37. @property (nonatomic, strong) UIButton *filterBtn;
  38. @property (nonatomic, strong) UIButton *drawBtn;
  39. @property (nonatomic, strong) UIButton *revokeBtn;
  40. @property (nonatomic, strong) UIButton *rotateBtn;
  41. @property (nonatomic, strong) UIButton *clipBtn;
  42. @property (nonatomic, strong) UIButton *rotateRatioBtn;
  43. @property (nonatomic, strong) UIScrollView *clipMenu;
  44. @property (nonatomic, strong) UIScrollView *filterMenu;
  45. @property (nonatomic, strong) UIScrollView *drawMenu;
  46. @property (nonatomic, strong) ZLGridLayar *gridLayer;
  47. @property (nonatomic, strong) ZLClipItem *selectClipItem;
  48. @property (nonatomic, strong) ZLClipRatio *selectClipRatio;
  49. @property (nonatomic, assign) CGRect clippingRect;
  50. @property (nonatomic, strong) ZLFilterItem *selectFilterItem;
  51. @property (nonatomic, strong) ZLDrawItem *selectDrawItem;
  52. @end
  53. @implementation ZLImageEditTool
  54. - (void)dealloc
  55. {
  56. [[GPUImageContext sharedImageProcessingContext].framebufferCache purgeAllUnassignedFramebuffers];
  57. // NSLog(@"---- %s", __FUNCTION__);
  58. }
  59. - (instancetype)initWithFrame:(CGRect)frame
  60. {
  61. return [self initWithEditType:ZLImageEditTypeDraw |
  62. ZLImageEditTypeMosaic |
  63. ZLImageEditTypeFilter |
  64. ZLImageEditTypeClip |
  65. ZLImageEditTypeRotate
  66. image:nil
  67. configuration:nil];
  68. }
  69. - (instancetype)initWithCoder:(NSCoder *)coder
  70. {
  71. return [self initWithEditType:ZLImageEditTypeDraw |
  72. ZLImageEditTypeMosaic |
  73. ZLImageEditTypeFilter |
  74. ZLImageEditTypeClip |
  75. ZLImageEditTypeRotate
  76. image:nil
  77. configuration:nil];
  78. }
  79. - (instancetype)initWithEditType:(ZLImageEditType)type image:(UIImage *)image configuration:(ZLPhotoConfiguration *)configuration
  80. {
  81. if (self = [super initWithFrame:[UIScreen mainScreen].bounds]) {
  82. _type = type;
  83. _configuration = configuration;
  84. self.editImage = image;
  85. [self setupUI];
  86. }
  87. return self;
  88. }
  89. - (void)setEditImage:(UIImage *)editImage
  90. {
  91. _editImage = editImage;
  92. _originSize = editImage.size;
  93. _imageView.image = editImage;
  94. _layoutCount = 0;
  95. [self setNeedsLayout];
  96. }
  97. - (void)layoutSubviews
  98. {
  99. [super layoutSubviews];
  100. _layoutCount++;
  101. UIEdgeInsets inset = UIEdgeInsetsZero;
  102. if (@available(iOS 11, *)) {
  103. inset = self.superview.safeAreaInsets;
  104. }
  105. self.cancelBtn.frame = CGRectMake(15+inset.left, inset.top+10, GetMatchValue(GetLocalLanguageTextValue(ZLPhotoBrowserCancelText), 15, YES, 30), 30);
  106. CGFloat doneBtnW = GetMatchValue(GetLocalLanguageTextValue(ZLPhotoBrowserDoneText), 15, YES, 30);
  107. self.doneBtn.frame = CGRectMake(kViewWidth-doneBtnW-inset.right-15, inset.top+10, doneBtnW, 30);
  108. [self setImageViewFrame:_layoutCount != 1];
  109. if (_selectToolType & ZLImageEditTypeClip) {
  110. self.gridLayer.frame = self.imageView.bounds;
  111. [self clippingRatioDidChange];
  112. }
  113. self.bottomView.frame = CGRectMake(0, kViewHeight-44-inset.bottom, kViewWidth, 44);
  114. NSInteger toolCount = [self toolCount];
  115. CGFloat disW = kViewWidth/(toolCount+1);
  116. CGFloat bx = disW;
  117. CGFloat bottomBtnW = 40;
  118. if (_type & ZLImageEditTypeFilter) {
  119. self.filterBtn.frame = CGRectMake(bx-bottomBtnW/2, 2, bottomBtnW, bottomBtnW);
  120. bx += disW;
  121. }
  122. if (_type & ZLImageEditTypeDraw) {
  123. self.drawBtn.frame = CGRectMake(bx-bottomBtnW/2, 2, bottomBtnW, bottomBtnW);
  124. bx += disW;
  125. }
  126. if (_type & ZLImageEditTypeRotate) {
  127. self.rotateBtn.frame = CGRectMake(bx-bottomBtnW/2, 2, bottomBtnW, bottomBtnW);
  128. bx += disW;
  129. }
  130. if (_type & ZLImageEditTypeClip) {
  131. self.clipBtn.frame = CGRectMake(bx-bottomBtnW/2, 2, bottomBtnW, bottomBtnW);
  132. bx += disW;
  133. }
  134. CGFloat menuMaxY = CGRectGetMinY(self.bottomView.frame);
  135. _filterMenu.frame = CGRectMake(inset.left, menuMaxY-80, kViewWidth-inset.left-inset.right, 80);
  136. _drawMenu.frame = CGRectMake(inset.left+30, menuMaxY-40, kViewWidth-inset.left-inset.right-30-60, 40);
  137. _revokeBtn.frame = CGRectMake(kViewWidth-60-inset.right, menuMaxY-40, 40, 40);
  138. BOOL hideClipRatioView = _configuration.hideClipRatiosToolBar ?: [self shouldHideClipRatioView];
  139. if (hideClipRatioView) {
  140. _rotateRatioBtn.hidden = YES;
  141. _clipMenu.hidden = YES;
  142. } else {
  143. _rotateRatioBtn.superview.frame = CGRectMake(kViewWidth-70-inset.right, menuMaxY-80, 70, 80);
  144. _clipMenu.frame = CGRectMake(inset.left, menuMaxY-80, kViewWidth-70-inset.left-inset.right, 80);
  145. }
  146. }
  147. - (void)setImageViewFrame:(BOOL)animate
  148. {
  149. if (!_editImage) return;
  150. UIEdgeInsets inset = UIEdgeInsetsZero;
  151. if (@available(iOS 11, *)) {
  152. inset = self.superview.safeAreaInsets;
  153. }
  154. BOOL showMenu = NO;
  155. if (_selectToolType & ZLImageEditTypeClip) {
  156. showMenu = ![self shouldHideClipRatioView];
  157. } else if (_selectToolType & (ZLImageEditTypeFilter | ZLImageEditTypeDraw)) {
  158. showMenu = YES;
  159. }
  160. //隐藏时 底部工具条高44,间距设置4即可,不隐藏时,比例view高度80,则为128
  161. CGFloat flag = showMenu ? 128 : 48;
  162. CGFloat diffXMarigin = showMenu ? 40 : 20;
  163. CGFloat w = kViewWidth-diffXMarigin-inset.left-inset.right;
  164. CGFloat maxH = kViewHeight-flag-inset.bottom-inset.top-50;
  165. CGFloat imgW = _exchangeImageWH ? _originSize.height : _originSize.width;
  166. CGFloat imgH = _exchangeImageWH ? _originSize.width : _originSize.height;
  167. CGFloat h = w * imgH / imgW;
  168. if (h > maxH) {
  169. h = maxH;
  170. w = h * imgW / imgH;
  171. }
  172. CGRect frame = CGRectMake((kViewWidth-w)/2, (kViewHeight-h-flag+40)/2, w, h);
  173. if (animate && !_isRotatingImage) {
  174. [UIView animateWithDuration:0.2 animations:^{
  175. _imageView.frame = frame;
  176. }];
  177. } else {
  178. _imageView.frame = frame;
  179. }
  180. }
  181. //当裁剪比例只有 custom 或者 1:1 的时候隐藏比例视图
  182. - (BOOL)shouldHideClipRatioView
  183. {
  184. if (_configuration.clipRatios.count <= 1) {
  185. NSInteger value1 = [_configuration.clipRatios.firstObject[ClippingRatioValue1] integerValue];
  186. NSInteger value2 = [_configuration.clipRatios.firstObject[ClippingRatioValue2] integerValue];
  187. if ((value1==0 && value2==0) || (value1==1 && value2==1)) {
  188. return YES;
  189. }
  190. }
  191. return NO;
  192. }
  193. - (void)setupUI
  194. {
  195. self.cancelBtn = [UIButton buttonWithType:UIButtonTypeCustom];
  196. self.cancelBtn.titleLabel.font = [UIFont systemFontOfSize:15];
  197. [self.cancelBtn setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
  198. [self.cancelBtn setTitle:GetLocalLanguageTextValue(ZLPhotoBrowserCancelText) forState:UIControlStateNormal];
  199. [self.cancelBtn addTarget:self action:@selector(cancelBtn_click) forControlEvents:UIControlEventTouchUpInside];
  200. [self.cancelBtn setEnlargeEdgeWithTop:0 right:10 bottom:10 left:0];
  201. [self addSubview:self.cancelBtn];
  202. self.doneBtn = [UIButton buttonWithType:UIButtonTypeCustom];
  203. [self.doneBtn setTitle:GetLocalLanguageTextValue(ZLPhotoBrowserDoneText) forState:UIControlStateNormal];
  204. [self.doneBtn setTitleColor:_configuration.bottomBtnsNormalTitleColor forState:UIControlStateNormal];
  205. self.doneBtn.titleLabel.font = [UIFont systemFontOfSize:15];
  206. self.doneBtn.layer.masksToBounds = YES;
  207. self.doneBtn.layer.cornerRadius = 3.0f;
  208. [self.doneBtn addTarget:self action:@selector(btnDone_click) forControlEvents:UIControlEventTouchUpInside];
  209. [self.doneBtn setEnlargeEdgeWithTop:0 right:0 bottom:10 left:10];
  210. [self addSubview:_doneBtn];
  211. //imageView
  212. self.imageView = [[ZLBrushBoardImageView alloc] init];
  213. self.imageView.image = _editImage;
  214. self.imageView.contentMode = UIViewContentModeScaleAspectFit;
  215. [self addSubview:self.imageView];
  216. UIPanGestureRecognizer *panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panGridView:)];
  217. self.imageView.userInteractionEnabled = YES;
  218. [self.imageView addGestureRecognizer:panGesture];
  219. //下方视图
  220. self.bottomView = [[UIView alloc] init];
  221. [self addSubview:self.bottomView];
  222. {
  223. if (_type & ZLImageEditTypeClip) {
  224. self.clipBtn = [UIButton buttonWithType:UIButtonTypeCustom];
  225. [self.clipBtn setImage:GetImageWithName(@"zl_clip") forState:UIControlStateNormal];
  226. [self.clipBtn addTarget:self action:@selector(clipBtn_click) forControlEvents:UIControlEventTouchUpInside];
  227. [self.bottomView addSubview:self.clipBtn];
  228. }
  229. if (_type & ZLImageEditTypeRotate) {
  230. self.rotateBtn = [UIButton buttonWithType:UIButtonTypeCustom];
  231. [self.rotateBtn setImage:GetImageWithName(@"zl_rotateimage") forState:UIControlStateNormal];
  232. [self.rotateBtn addTarget:self action:@selector(rotateImageBtn_click) forControlEvents:UIControlEventTouchUpInside];
  233. [self.bottomView addSubview:self.rotateBtn];
  234. }
  235. if (_type & ZLImageEditTypeFilter) {
  236. self.filterBtn = [UIButton buttonWithType:UIButtonTypeCustom];
  237. [self.filterBtn setImage:GetImageWithName(@"zl_filter") forState:UIControlStateNormal];
  238. [self.filterBtn addTarget:self action:@selector(filterBtn_click) forControlEvents:UIControlEventTouchUpInside];
  239. [self.bottomView addSubview:self.filterBtn];
  240. }
  241. if (_type & ZLImageEditTypeDraw) {
  242. self.drawBtn = [UIButton buttonWithType:UIButtonTypeCustom];
  243. [self.drawBtn setImage:GetImageWithName(@"zl_draw") forState:UIControlStateNormal];
  244. [self.drawBtn addTarget:self action:@selector(drawBtn_click) forControlEvents:UIControlEventTouchUpInside];
  245. [self.bottomView addSubview:self.drawBtn];
  246. }
  247. }
  248. [self sendSubviewToBack:self.imageView];
  249. }
  250. - (ZLClippingCircle*)clippingCircleWithTag:(NSInteger)tag
  251. {
  252. ZLClippingCircle *view = [[ZLClippingCircle alloc] initWithFrame:CGRectMake(0, 0, 50, 50)];
  253. view.backgroundColor = [UIColor clearColor];
  254. view.bgColor = [UIColor whiteColor];
  255. view.tag = tag;
  256. UIPanGestureRecognizer *panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panCircleView:)];
  257. [view addGestureRecognizer:panGesture];
  258. [self addSubview:view];
  259. return view;
  260. }
  261. - (NSInteger)toolCount
  262. {
  263. NSArray *arr = @[@(ZLImageEditTypeClip), @(ZLImageEditTypeRotate), @(ZLImageEditTypeFilter), @(ZLImageEditTypeDraw), @(ZLImageEditTypeMosaic)];
  264. NSInteger count = 0;
  265. for (NSNumber *num in arr) {
  266. if (_type & num.integerValue) {
  267. count++;
  268. }
  269. }
  270. return count;
  271. }
  272. #pragma mark - menu
  273. - (void)setupFilterMenu
  274. {
  275. if (_filterMenu) return;
  276. //这只是初始坐标,实际坐标在viewdidlayoutsubviews里面布局
  277. _filterMenu = [[UIScrollView alloc] initWithFrame:CGRectZero];
  278. _filterMenu.backgroundColor = [UIColor clearColor];
  279. _filterMenu.showsHorizontalScrollIndicator = NO;
  280. _filterMenu.clipsToBounds = NO;
  281. [self addSubview:_filterMenu];
  282. CGFloat W = 70;
  283. CGFloat H = 80;
  284. CGFloat x = 0;
  285. CGSize imgSize = self.editImage.size;
  286. CGFloat maxW = MIN(imgSize.width, imgSize.height);
  287. UIImage *iconImage = [self scaleImage:self.editImage toSize:CGSizeMake(W * imgSize.width/maxW, W * imgSize.height/maxW)];
  288. for (int i = ZLFilterTypeOriginal; i <= ZLFilterTypeColorInvert; i++) {
  289. ZLFilterItem *item = [[ZLFilterItem alloc] initWithFrame:CGRectMake(x, 0, W, H) Image:iconImage filterType:i target:self action:@selector(tapFilter:)];
  290. [_filterMenu addSubview:item];
  291. if (!self.selectFilterItem) {
  292. self.selectFilterItem = item;
  293. }
  294. x += W;
  295. }
  296. _filterMenu.contentSize = CGSizeMake(MAX(x, _filterMenu.frame.size.width+1), 0);
  297. }
  298. - (void)setupDrawMenu
  299. {
  300. if (_drawMenu) return;
  301. //这只是初始坐标,实际坐标在viewdidlayoutsubviews里面布局
  302. _drawMenu = [[UIScrollView alloc] initWithFrame:CGRectZero];
  303. _drawMenu.backgroundColor = [UIColor clearColor];
  304. _drawMenu.showsHorizontalScrollIndicator = NO;
  305. _drawMenu.clipsToBounds = NO;
  306. [self addSubview:_drawMenu];
  307. self.revokeBtn = [UIButton buttonWithType:UIButtonTypeCustom];
  308. [self.revokeBtn setImage:GetImageWithName(@"zl_revoke") forState:UIControlStateNormal];
  309. [self.revokeBtn addTarget:self action:@selector(revokeBtn_click) forControlEvents:UIControlEventTouchUpInside];
  310. [self addSubview:self.revokeBtn];
  311. CGFloat W = 40;
  312. CGFloat H = 40;
  313. CGFloat x = 0;
  314. for (int i = ZLDrawItemColorTypeWhite; i <= ZLDrawItemColorTypePurple; i++) {
  315. ZLDrawItem *item = [[ZLDrawItem alloc] initWithFrame:CGRectMake(x, 0, W, H) colorType:i target:self action:@selector(tapDrawColor:)];
  316. [_drawMenu addSubview:item];
  317. if (!self.selectDrawItem) {
  318. self.selectDrawItem = item;
  319. }
  320. x += W;
  321. }
  322. _drawMenu.contentSize = CGSizeMake(MAX(x, _drawMenu.frame.size.width+1), 0);
  323. }
  324. - (void)setupClipMenu
  325. {
  326. if (_clipMenu) return;
  327. self.gridLayer = [[ZLGridLayar alloc] init];
  328. self.gridLayer.bgColor = [[UIColor blackColor] colorWithAlphaComponent:.5];
  329. self.gridLayer.gridColor = [UIColor whiteColor];
  330. [self.imageView.layer addSublayer:self.gridLayer];
  331. _ltView = [self clippingCircleWithTag:0];
  332. _lbView = [self clippingCircleWithTag:1];
  333. _rtView = [self clippingCircleWithTag:2];
  334. _rbView = [self clippingCircleWithTag:3];
  335. //这只是初始坐标,实际坐标在viewdidlayoutsubviews里面布局
  336. _clipMenu = [[UIScrollView alloc] initWithFrame:CGRectZero];
  337. _clipMenu.backgroundColor = [UIColor clearColor];
  338. _clipMenu.showsHorizontalScrollIndicator = NO;
  339. _clipMenu.clipsToBounds = NO;
  340. [self addSubview:_clipMenu];
  341. //旋转按钮
  342. UIView *view = [[UIView alloc] init];
  343. view.backgroundColor = [[UIColor whiteColor] colorWithAlphaComponent:.8];
  344. view.hidden = YES;
  345. [self addSubview:view];
  346. _rotateRatioBtn = [UIButton buttonWithType:UIButtonTypeCustom];
  347. _rotateRatioBtn.frame = CGRectMake(15, 20, 40, 40);
  348. _rotateRatioBtn.imageView.contentMode = UIViewContentModeScaleAspectFit;
  349. [_rotateRatioBtn setBackgroundImage:GetImageWithName(@"zl_btn_rotate") forState:UIControlStateNormal];
  350. [_rotateRatioBtn addTarget:self action:@selector(rotateRadioBtn_click) forControlEvents:UIControlEventTouchUpInside];
  351. [view addSubview:_rotateRatioBtn];
  352. CGFloat W = 70;
  353. CGFloat H = 80;
  354. CGFloat x = 0;
  355. CGSize imgSize = self.editImage.size;
  356. CGFloat maxW = MIN(imgSize.width, imgSize.height);
  357. UIImage *iconImage = [self scaleImage:self.editImage toSize:CGSizeMake(W * imgSize.width/maxW, W * imgSize.height/maxW)];
  358. //如需要其他比例,请按照格式自行设置
  359. for(NSDictionary *info in _configuration.clipRatios){
  360. CGFloat val1 = [info[@"value1"] floatValue];
  361. CGFloat val2 = [info[@"value2"] floatValue];
  362. ZLClipRatio *ratio = [[ZLClipRatio alloc] initWithValue1:val1 value2:val2];
  363. ratio.titleFormat = info[@"titleFormat"];
  364. ratio.isLandscape = val1 > val2;
  365. ZLClipItem *item = [[ZLClipItem alloc] initWithFrame:CGRectMake(x, 0, W, H) image:iconImage target:self action:@selector(tapRadio:)];
  366. item.ratio = ratio;
  367. [item refreshViews];
  368. [_clipMenu addSubview:item];
  369. x += W;
  370. if (!self.selectClipItem){
  371. self.selectClipItem = item;
  372. }
  373. }
  374. _clipMenu.contentSize = CGSizeMake(MAX(x, _clipMenu.frame.size.width+1), 0);
  375. }
  376. #pragma mark - btn action
  377. - (void)cancelBtn_click
  378. {
  379. if (self.cancelEditBlock) {
  380. self.cancelEditBlock();
  381. }
  382. }
  383. - (void)btnDone_click
  384. {
  385. UIImage *image = [self clipImage];
  386. if (self.doneEditBlock) {
  387. self.doneEditBlock(image);
  388. }
  389. }
  390. - (void)clipBtn_click
  391. {
  392. [self switchTool:ZLImageEditTypeClip];
  393. }
  394. - (void)rotateImageBtn_click
  395. {
  396. if (_isRotatingImage) {
  397. //旋转过程中不接受再次旋转
  398. return;
  399. }
  400. _isRotatingImage = YES;
  401. _editImage = [_editImage rotate:UIImageOrientationLeft];
  402. UIImage *newImage = [_imageView.image rotate:UIImageOrientationLeft];
  403. _exchangeImageWH = !_exchangeImageWH;
  404. [self switchCircleAndGridLayerShowStatus:NO];
  405. [UIView animateWithDuration:0.25 animations:^{
  406. _imageView.transform = CGAffineTransformMakeRotation(-M_PI_2);
  407. } completion:^(BOOL finished) {
  408. _imageView.image = newImage;
  409. [self setImageViewFrame:NO];
  410. _imageView.transform = CGAffineTransformIdentity;
  411. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  412. _isRotatingImage = NO;
  413. });
  414. }];
  415. [self.class cancelPreviousPerformRequestsWithTarget:self selector:@selector(showCircleAndGrid) object:nil];
  416. if (_selectToolType & ZLImageEditTypeClip) {
  417. [self performSelector:@selector(showCircleAndGrid) withObject:nil afterDelay:0.5];
  418. }
  419. }
  420. - (void)showCircleAndGrid
  421. {
  422. [self switchCircleAndGridLayerShowStatus:YES];
  423. }
  424. - (void)rotateRadioBtn_click
  425. {
  426. for (ZLClipItem *item in _clipMenu.subviews){
  427. if([item isKindOfClass:[ZLClipItem class]]){
  428. [item changeOrientation];
  429. }
  430. }
  431. if (self.selectClipRatio.ratio != 0 &&
  432. self.selectClipRatio.ratio != 1){
  433. [self clippingRatioDidChange];
  434. }
  435. }
  436. - (void)filterBtn_click
  437. {
  438. [self switchTool:ZLImageEditTypeFilter];
  439. }
  440. - (void)drawBtn_click
  441. {
  442. [self switchTool:ZLImageEditTypeDraw];
  443. }
  444. - (void)switchTool:(ZLImageEditType)type
  445. {
  446. if (_selectToolType & type) {
  447. _selectToolType = 0;
  448. } else {
  449. _selectToolType = type;
  450. }
  451. if (!self.editImage) return;
  452. switch (_selectToolType) {
  453. case ZLImageEditTypeClip:
  454. [self setupClipMenu];
  455. break;
  456. case ZLImageEditTypeFilter:
  457. [self setupFilterMenu];
  458. break;
  459. case ZLImageEditTypeDraw:
  460. [self setupDrawMenu];
  461. break;
  462. default:
  463. break;
  464. }
  465. [self setNeedsLayout];
  466. if (_selectToolType == 0) {
  467. _filterMenu.hidden = YES;
  468. _clipMenu.hidden = YES;
  469. _rotateRatioBtn.superview.hidden = YES;
  470. [self switchBtnStatus:nil];
  471. [self switchCircleAndGridLayerShowStatus:NO];
  472. return;
  473. }
  474. if (_selectToolType & ZLImageEditTypeFilter) {
  475. _filterMenu.hidden = NO;
  476. _drawMenu.hidden = YES;
  477. _clipMenu.hidden = YES;
  478. _rotateRatioBtn.superview.hidden = YES;
  479. self.imageView.drawEnable = NO;
  480. [self switchCircleAndGridLayerShowStatus:NO];
  481. [self switchBtnStatus:self.filterBtn];
  482. } else if (_selectToolType & ZLImageEditTypeDraw) {
  483. _filterMenu.hidden = YES;
  484. _drawMenu.hidden = NO;
  485. _clipMenu.hidden = YES;
  486. _rotateRatioBtn.superview.hidden = YES;
  487. self.imageView.drawEnable = YES;
  488. [self switchCircleAndGridLayerShowStatus:NO];
  489. [self switchBtnStatus:self.drawBtn];
  490. } else if (_selectToolType & ZLImageEditTypeClip) {
  491. _filterMenu.hidden = YES;
  492. _drawMenu.hidden = YES;
  493. _clipMenu.hidden = NO;
  494. _rotateRatioBtn.superview.hidden = NO;
  495. self.imageView.drawEnable = NO;
  496. [self switchCircleAndGridLayerShowStatus:YES];
  497. [self switchBtnStatus:self.clipBtn];
  498. }
  499. }
  500. - (void)switchBtnStatus:(UIButton *)btn
  501. {
  502. UIButton *b = [UIButton new];
  503. NSArray *arr = @[self.filterBtn?: b, self.drawBtn?: b, self.rotateBtn?: b, self.clipBtn?: b];
  504. for (UIButton *b in arr) {
  505. if (b == btn) {
  506. [b setBackgroundColor:[UIColor colorWithWhite:1 alpha:.15]];
  507. } else {
  508. [b setBackgroundColor:[UIColor clearColor]];
  509. }
  510. }
  511. }
  512. - (void)switchCircleAndGridLayerShowStatus:(BOOL)show
  513. {
  514. if (!show) {
  515. self.clippingRect = CGRectZero;
  516. }
  517. _gridLayer.hidden = !show;
  518. _ltView.hidden = !show;
  519. _lbView.hidden = !show;
  520. _rtView.hidden = !show;
  521. _rbView.hidden = !show;
  522. }
  523. #pragma mark - 滤镜
  524. - (void)tapFilter:(UITapGestureRecognizer *)sender
  525. {
  526. ZLFilterItem *item = (ZLFilterItem *)sender.view;
  527. if (self.selectFilterItem == item) return;
  528. self.selectFilterItem = item;
  529. //加滤镜
  530. self.imageView.image = [ZLFilterTool filterImage:_editImage filterType:item.filterType];
  531. }
  532. - (void)setSelectFilterItem:(ZLFilterItem *)selectFilterItem
  533. {
  534. if (selectFilterItem != _selectFilterItem) {
  535. _selectFilterItem.backgroundColor = [UIColor clearColor];
  536. _selectFilterItem = selectFilterItem;
  537. _selectFilterItem.backgroundColor = [UIColor colorWithWhite:1 alpha:.15];
  538. }
  539. }
  540. #pragma mark - draw
  541. - (void)tapDrawColor:(UITapGestureRecognizer *)sender
  542. {
  543. ZLDrawItem *item = (ZLDrawItem *)sender.view;
  544. if (self.selectDrawItem == item) return;
  545. self.selectDrawItem = item;
  546. }
  547. - (void)setSelectDrawItem:(ZLDrawItem *)selectDrawItem
  548. {
  549. if (selectDrawItem != _selectDrawItem) {
  550. _selectDrawItem.selected = NO;
  551. _selectDrawItem = selectDrawItem;
  552. _selectDrawItem.selected = YES;
  553. self.imageView.drawColor = _selectDrawItem.color;
  554. }
  555. }
  556. - (void)revokeBtn_click
  557. {
  558. [self.imageView revoke];
  559. }
  560. #pragma mark - 裁剪
  561. - (void)tapRadio:(UITapGestureRecognizer *)sender
  562. {
  563. ZLClipItem *item = (ZLClipItem *)sender.view;
  564. self.selectClipItem = item;
  565. }
  566. - (void)setSelectClipItem:(ZLClipItem *)selectClipItem
  567. {
  568. if (selectClipItem != _selectClipItem){
  569. _selectClipItem.backgroundColor = [UIColor clearColor];
  570. _selectClipItem = selectClipItem;
  571. _selectClipItem.backgroundColor = [UIColor colorWithWhite:1 alpha:.15];
  572. if(selectClipItem.ratio.ratio==0){
  573. self.selectClipRatio = nil;
  574. } else {
  575. self.selectClipRatio = selectClipItem.ratio;
  576. }
  577. }
  578. }
  579. - (void)setSelectClipRatio:(ZLClipRatio *)selectClipRatio
  580. {
  581. if(selectClipRatio != _selectClipRatio){
  582. _selectClipRatio = selectClipRatio;
  583. [self clippingRatioDidChange];
  584. }
  585. }
  586. - (void)clippingRatioDidChange
  587. {
  588. CGRect rect = _imageView.bounds;
  589. if (self.selectClipRatio) {
  590. CGFloat H = rect.size.width * self.selectClipRatio.ratio;
  591. if (H <= rect.size.height) {
  592. rect.size.height = H;
  593. } else {
  594. rect.size.width *= rect.size.height / H;
  595. }
  596. rect.origin.x = (_imageView.bounds.size.width - rect.size.width) / 2;
  597. rect.origin.y = (_imageView.bounds.size.height - rect.size.height) / 2;
  598. }
  599. [self setClippingRect:rect animated:YES];
  600. }
  601. - (void)setClippingRect:(CGRect)clippingRect animated:(BOOL)animated
  602. {
  603. if (animated) {
  604. [UIView animateWithDuration:0.2 animations:^{
  605. _ltView.center = [self convertPoint:CGPointMake(clippingRect.origin.x, clippingRect.origin.y) fromView:_imageView];
  606. _lbView.center = [self convertPoint:CGPointMake(clippingRect.origin.x, clippingRect.origin.y+clippingRect.size.height) fromView:_imageView];
  607. _rtView.center = [self convertPoint:CGPointMake(clippingRect.origin.x+clippingRect.size.width, clippingRect.origin.y) fromView:_imageView];
  608. _rbView.center = [self convertPoint:CGPointMake(clippingRect.origin.x+clippingRect.size.width, clippingRect.origin.y+clippingRect.size.height) fromView:_imageView];
  609. }
  610. ];
  611. CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"clippingRect"];
  612. animation.duration = 0.2;
  613. animation.fromValue = [NSValue valueWithCGRect:_clippingRect];
  614. animation.toValue = [NSValue valueWithCGRect:clippingRect];
  615. [_gridLayer addAnimation:animation forKey:nil];
  616. _gridLayer.clippingRect = clippingRect;
  617. _clippingRect = clippingRect;
  618. [_gridLayer setNeedsDisplay];
  619. } else {
  620. self.clippingRect = clippingRect;
  621. }
  622. }
  623. - (void)setClippingRect:(CGRect)clippingRect
  624. {
  625. _clippingRect = clippingRect;
  626. _ltView.center = [self convertPoint:CGPointMake(_clippingRect.origin.x, _clippingRect.origin.y) fromView:_imageView];
  627. _lbView.center = [self convertPoint:CGPointMake(_clippingRect.origin.x, _clippingRect.origin.y+_clippingRect.size.height) fromView:_imageView];
  628. _rtView.center = [self convertPoint:CGPointMake(_clippingRect.origin.x+_clippingRect.size.width, _clippingRect.origin.y) fromView:_imageView];
  629. _rbView.center = [self convertPoint:CGPointMake(_clippingRect.origin.x+_clippingRect.size.width, _clippingRect.origin.y+_clippingRect.size.height) fromView:_imageView];
  630. _gridLayer.clippingRect = clippingRect;
  631. [_gridLayer setNeedsDisplay];
  632. }
  633. #pragma mark - 拖动
  634. - (void)panCircleView:(UIPanGestureRecognizer*)sender
  635. {
  636. CGPoint point = [sender locationInView:_imageView];
  637. CGPoint dp = [sender translationInView:_imageView];
  638. CGRect rct = self.clippingRect;
  639. const CGFloat W = _imageView.frame.size.width;
  640. const CGFloat H = _imageView.frame.size.height;
  641. CGFloat minX = 0;
  642. CGFloat minY = 0;
  643. CGFloat maxX = W;
  644. CGFloat maxY = H;
  645. CGFloat ratio = (sender.view.tag == 1 || sender.view.tag==2) ? -self.selectClipRatio.ratio : self.selectClipRatio.ratio;
  646. switch (sender.view.tag) {
  647. case 0: // upper left
  648. {
  649. maxX = MAX((rct.origin.x + rct.size.width) - 0.1 * W, 0.1 * W);
  650. maxY = MAX((rct.origin.y + rct.size.height) - 0.1 * H, 0.1 * H);
  651. if (ratio!=0) {
  652. CGFloat y0 = rct.origin.y - ratio * rct.origin.x;
  653. CGFloat x0 = -y0 / ratio;
  654. minX = MAX(x0, 0);
  655. minY = MAX(y0, 0);
  656. point.x = MAX(minX, MIN(point.x, maxX));
  657. point.y = MAX(minY, MIN(point.y, maxY));
  658. if(-dp.x*ratio + dp.y > 0){ point.x = (point.y - y0) / ratio; }
  659. else{ point.y = point.x * ratio + y0; }
  660. } else {
  661. point.x = MAX(minX, MIN(point.x, maxX));
  662. point.y = MAX(minY, MIN(point.y, maxY));
  663. }
  664. rct.size.width = rct.size.width - (point.x - rct.origin.x);
  665. rct.size.height = rct.size.height - (point.y - rct.origin.y);
  666. rct.origin.x = point.x;
  667. rct.origin.y = point.y;
  668. break;
  669. }
  670. case 1: // lower left
  671. {
  672. maxX = MAX((rct.origin.x + rct.size.width) - 0.1 * W, 0.1 * W);
  673. minY = MAX(rct.origin.y + 0.1 * H, 0.1 * H);
  674. if (ratio!=0) {
  675. CGFloat y0 = (rct.origin.y + rct.size.height) - ratio* rct.origin.x ;
  676. CGFloat xh = (H - y0) / ratio;
  677. minX = MAX(xh, 0);
  678. maxY = MIN(y0, H);
  679. point.x = MAX(minX, MIN(point.x, maxX));
  680. point.y = MAX(minY, MIN(point.y, maxY));
  681. if(-dp.x*ratio + dp.y < 0){ point.x = (point.y - y0) / ratio; }
  682. else{ point.y = point.x * ratio + y0; }
  683. } else {
  684. point.x = MAX(minX, MIN(point.x, maxX));
  685. point.y = MAX(minY, MIN(point.y, maxY));
  686. }
  687. rct.size.width = rct.size.width - (point.x - rct.origin.x);
  688. rct.size.height = point.y - rct.origin.y;
  689. rct.origin.x = point.x;
  690. break;
  691. }
  692. case 2: // upper right
  693. {
  694. minX = MAX(rct.origin.x + 0.1 * W, 0.1 * W);
  695. maxY = MAX((rct.origin.y + rct.size.height) - 0.1 * H, 0.1 * H);
  696. if (ratio!=0) {
  697. CGFloat y0 = rct.origin.y - ratio * (rct.origin.x + rct.size.width);
  698. CGFloat yw = ratio * W + y0;
  699. CGFloat x0 = -y0 / ratio;
  700. maxX = MIN(x0, W);
  701. minY = MAX(yw, 0);
  702. point.x = MAX(minX, MIN(point.x, maxX));
  703. point.y = MAX(minY, MIN(point.y, maxY));
  704. if(-dp.x*ratio + dp.y > 0){ point.x = (point.y - y0) / ratio; }
  705. else{ point.y = point.x * ratio + y0; }
  706. } else {
  707. point.x = MAX(minX, MIN(point.x, maxX));
  708. point.y = MAX(minY, MIN(point.y, maxY));
  709. }
  710. rct.size.width = point.x - rct.origin.x;
  711. rct.size.height = rct.size.height - (point.y - rct.origin.y);
  712. rct.origin.y = point.y;
  713. break;
  714. }
  715. case 3: // lower right
  716. {
  717. minX = MAX(rct.origin.x + 0.1 * W, 0.1 * W);
  718. minY = MAX(rct.origin.y + 0.1 * H, 0.1 * H);
  719. if (ratio!=0) {
  720. CGFloat y0 = (rct.origin.y + rct.size.height) - ratio * (rct.origin.x + rct.size.width);
  721. CGFloat yw = ratio * W + y0;
  722. CGFloat xh = (H - y0) / ratio;
  723. maxX = MIN(xh, W);
  724. maxY = MIN(yw, H);
  725. point.x = MAX(minX, MIN(point.x, maxX));
  726. point.y = MAX(minY, MIN(point.y, maxY));
  727. if(-dp.x*ratio + dp.y < 0){ point.x = (point.y - y0) / ratio; }
  728. else{ point.y = point.x * ratio + y0; }
  729. } else {
  730. point.x = MAX(minX, MIN(point.x, maxX));
  731. point.y = MAX(minY, MIN(point.y, maxY));
  732. }
  733. rct.size.width = point.x - rct.origin.x;
  734. rct.size.height = point.y - rct.origin.y;
  735. break;
  736. }
  737. default:
  738. break;
  739. }
  740. self.clippingRect = rct;
  741. }
  742. - (void)panGridView:(UIPanGestureRecognizer*)sender
  743. {
  744. if (_selectToolType != ZLImageEditTypeClip) {
  745. return;
  746. }
  747. static BOOL dragging = NO;
  748. static CGRect initialRect;
  749. if (sender.state==UIGestureRecognizerStateBegan) {
  750. CGPoint point = [sender locationInView:_imageView];
  751. dragging = CGRectContainsPoint(_clippingRect, point);
  752. initialRect = self.clippingRect;
  753. } else if(dragging) {
  754. CGPoint point = [sender translationInView:_imageView];
  755. CGFloat left = MIN(MAX(initialRect.origin.x + point.x, 0), _imageView.frame.size.width-initialRect.size.width);
  756. CGFloat top = MIN(MAX(initialRect.origin.y + point.y, 0), _imageView.frame.size.height-initialRect.size.height);
  757. CGRect rct = self.clippingRect;
  758. rct.origin.x = left;
  759. rct.origin.y = top;
  760. self.clippingRect = rct;
  761. }
  762. }
  763. #pragma mark - 裁剪
  764. - (UIImage *)clipImage
  765. {
  766. CGFloat zoomScale = _imageView.bounds.size.width / _imageView.image.size.width;
  767. CGRect rct = CGRectEqualToRect(self.clippingRect, CGRectZero) ? _imageView.bounds : self.clippingRect;
  768. rct.size.width /= zoomScale;
  769. rct.size.height /= zoomScale;
  770. rct.origin.x /= zoomScale;
  771. rct.origin.y /= zoomScale;
  772. CGPoint origin = CGPointMake(-rct.origin.x, -rct.origin.y);
  773. UIImage *img = nil;
  774. UIGraphicsBeginImageContextWithOptions(rct.size, NO, _imageView.image.scale);
  775. [_imageView.image drawAtPoint:origin];
  776. img = UIGraphicsGetImageFromCurrentImageContext();
  777. UIGraphicsEndImageContext();
  778. return img;
  779. }
  780. - (UIImage *)scaleImage:(UIImage *)image toSize:(CGSize)size
  781. {
  782. NSData *data = UIImageJPEGRepresentation(image, 1.0);
  783. return [self scaledlmageWithData:data withSize:size scale:1.0 orientation:image.imageOrientation];
  784. }
  785. - (UIImage *)scaledlmageWithData:(NSData *)data withSize:(CGSize)size scale:(CGFloat)scale orientation:(UIImageOrientation)orientation {
  786. CGFloat maxPixelSize = MAX(size.width, size.height);
  787. CGImageSourceRef sourceRef = CGImageSourceCreateWithData((__bridge CFDataRef)data, nil);
  788. NSDictionary *options = @{(__bridge id)kCGImageSourceCreateThumbnailFromImageAlways:(__bridge id)kCFBooleanTrue,
  789. (__bridge id)kCGImageSourceThumbnailMaxPixelSize:[NSNumber numberWithFloat:maxPixelSize]};
  790. CGImageRef imageRef = CGImageSourceCreateThumbnailAtIndex(sourceRef, 0, (__bridge CFDictionaryRef)options);
  791. UIImage *resultlmage = [UIImage imageWithCGImage:imageRef scale:scale orientation:orientation];
  792. CGImageRelease(imageRef);
  793. CFRelease(sourceRef);
  794. return resultlmage;
  795. }
  796. @end