最近做一個項目,里邊要做圖片處理功能,其中就有圖片單指旋轉,縮放。由於之前還沒做過這樣的功能,於是乎找了下相關的資料,終於找到了一種好的實現方案。於是就仿照美圖秀秀里邊貼紙的功能做了一個demo。。。以下貼一些主要實現代碼。。。。
/*****頭文件*********/
#import <UIKit/UIKit.h>
@interface ImageEditView : UIView
// 背景圖片
@property (nonatomic, weak, readonly) UIImageView *imageView;
// 添加水平圖片(可旋轉縮放)
- (void)addWatermarkImage:(UIImage *)watermarkImage;
// 結束編輯狀態
- (void)endEditing;
@end
/*****實現文件*********/
#import "ImageEditView.h"
@interface ImageEditView ()<UIGestureRecognizerDelegate>{
UIImageView *_imageView;
}
// 編輯水平圖片的數組
@property (nonatomic, strong) NSMutableArray *imageViews;
// 當前正在編輯的水印圖片
@property (nonatomic, weak) UIImageView *currentEditintImageView;
// 刪除按鈕
@property (nonatomic, weak) UIButton *deleteBtn;
// 旋轉縮放按鈕,此處命名不太好
@property (nonatomic, weak) UIImageView *editBtn;
// 記錄上一個觸摸點
@property (nonatomic, assign) CGPoint previousPoint;
// 記錄是否觸發旋轉縮放按鈕
@property (nonatomic, assign, getter=isEditGusture) BOOL editGusture;
@end
@implementation ImageEditView
#pragma mark -懶加載
- (UIImageView *)imageView
{
if (!_imageView) {
UIImageView *imageView = [[UIImageView alloc] init];
_imageView = imageView;
[self insertSubview:imageView atIndex:0];
}
return _imageView;
}
- (UIButton *)deleteBtn
{
if (!_deleteBtn) {
UIButton *deleteBtn = [[UIButton alloc] init];
[deleteBtn addTarget:self action:@selector(delete) forControlEvents:UIControlEventTouchUpInside];
[deleteBtn setImage:[UIImage imageNamed:@"icon_delete"] forState:UIControlStateNormal];
deleteBtn.hidden = YES;
[self addSubview:deleteBtn];
_deleteBtn = deleteBtn;
}
return _deleteBtn;
}
// 刪除按鈕點擊
- (void)delete
{
[self.currentEditintImageView removeFromSuperview];
[self.imageViews removeObject:self.currentEditintImageView];
self.currentEditintImageView = nil;
[self hideEditingBtn:YES];
}
- (UIImageView *)editBtn
{
if (!_editBtn) {
UIImageView *editBtn = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"btn_watermark_scale"]];
editBtn.contentMode = UIViewContentModeCenter;
editBtn.userInteractionEnabled = NO;
editBtn.hidden = YES;
[self addSubview:editBtn];
_editBtn = editBtn;
}
return _editBtn;
}
- (NSMutableArray *)imageViews
{
if (!_imageViews) {
_imageViews = [NSMutableArray array];
}
return _imageViews;
}
- (void)awakeFromNib
{
[super awakeFromNib];
[self setup];
}
- (instancetype)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame]) {
[self setup];
}
return self;
}
#pragma mark -初始化
- (void)setup
{
self.clipsToBounds = YES;
// 此處添加各種手勢,注意,是添加到self 上
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tap:)];
[self addGestureRecognizer:tap];
UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(pan:)];
[self addGestureRecognizer:pan];
UIPinchGestureRecognizer *pinch = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(pinch:)];
pinch.delegate = self;
[self addGestureRecognizer:pinch];
UIRotationGestureRecognizer *rotate = [[UIRotationGestureRecognizer alloc] initWithTarget:self action:@selector(rotate:)];
rotate.delegate = self;
[self addGestureRecognizer:rotate];
}
#pragma mark -手勢
// 雙指旋轉,比較簡單,大牛請忽略
- (void)rotate:(UIRotationGestureRecognizer *)rotate
{
if (!self.currentEditintImageView) {
// 判斷觸摸點是否在水平圖片上
self.currentEditintImageView = [self imageViewInLocation:[rotate locationInView:self]];
if (!self.currentEditintImageView) return; // 不在就直接return
}
//觸摸點在水印圖片上
if (rotate.state == UIGestureRecognizerStateBegan) {
// 正在編輯,先隱藏兩個編輯按鈕
[self hideEditingBtn:YES];
}else if (rotate.state == UIGestureRecognizerStateEnded) {
if (!self.currentEditintImageView) return;
// 結束編輯,顯示兩個編輯按鈕
[self hideEditingBtn:NO];
}else {
// 做旋轉處理
self.currentEditintImageView.transform = CGAffineTransformRotate(self.currentEditintImageView.transform, rotate.rotation);
[rotate setRotation:0];
}
// [self resetBorder];
}
// 以下都是雙指縮放手勢,重點是在Pan平移手勢的那個方法,直接跳過此處吧
- (void)pinch:(UIPinchGestureRecognizer *)pinch
{
if (pinch.state == UIGestureRecognizerStateBegan) {
UIImageView *imgView = [self imageViewInLocation:[pinch locationInView:self]];
if (!self.currentEditintImageView && !imgView) return;
if (imgView) {
self.currentEditintImageView = imgView;
}
[self hideEditingBtn:YES];
}else if (pinch.state == UIGestureRecognizerStateEnded) {
if (!self.currentEditintImageView) return;
[self hideEditingBtn:NO];
}else {
self.currentEditintImageView.transform = CGAffineTransformScale(self.currentEditintImageView.transform, pinch.scale, pinch.scale);
[pinch setScale:1];
}
// [self resetBorder];
}
- (void)tap:(UITapGestureRecognizer *)tap
{
UIImageView *imgView = [self imageViewInLocation:[tap locationInView:self]];
self.currentEditintImageView = imgView;
[self hideEditingBtn:!imgView];
// [self resetBorder];
}
// 重點來了,此處涉及一些反三角函數計算,感覺又回到高中時代了。。。
- (void)pan:(UIPanGestureRecognizer *)pan
{
// 此處注意了,若要使用單指做旋轉縮放,就必須要添加平移的手勢了,通過平移手勢偏移量計算對應的縮放比例以及旋轉角度。。。。
// 開始和結束處理跟上面手勢一樣。。
if (pan.state == UIGestureRecognizerStateBegan) {
UIImageView *imgView = [self imageViewInLocation:[pan locationInView:self]];
if (!self.currentEditintImageView && !imgView) return;
if (imgView) {
self.currentEditintImageView = imgView;
}
CGPoint loc = [pan locationInView:self];
self.editGusture = CGRectContainsPoint(self.editBtn.frame, loc);
[self hideEditingBtn:YES];
self.previousPoint = loc;
}else if (pan.state == UIGestureRecognizerStateEnded) {
if (!self.currentEditintImageView) return;
[self hideEditingBtn:NO];
self.previousPoint = [pan locationInView:self];
}else {
/***********此處是處理平移手勢過程*********/
if (self.isEditGusture) { // 由開始的觸摸點判斷是否觸發旋轉縮放按鈕。。
// 拖拽編輯按鈕處理
// 獲得當前點
CGPoint currentTouchPoint = [pan locationInView:self];
// 當前編輯水印圖片的中心點
CGPoint center = self.currentEditintImageView.center;
// 這句由當前點到中心點連成的線段跟上一個點到中心店連成的線段反算出偏移角度
CGFloat angleInRadians = atan2f(currentTouchPoint.y - center.y, currentTouchPoint.x - center.x) - atan2f(self.previousPoint.y - center.y, self.previousPoint.x - center.x);
// 計算出偏移角度之后就可以做對應的旋轉角度啦
CGAffineTransform t = CGAffineTransformRotate(self.currentEditintImageView.transform, angleInRadians);
// 下面是計算縮放比例,其實很簡單
1. 先計算兩線段的長度。
CGFloat previousDistance = [self distanceWithPoint:center otherPoint:self.previousPoint];
CGFloat currentDistance = [self distanceWithPoint:center otherPoint:currentTouchPoint];
2.然后兩長度的比值就是縮放比例啦,簡單吧。
CGFloat scale = currentDistance / previousDistance;
// 然后設置兩者結合后的transform賦值給當前水平圖片即可
t = CGAffineTransformScale(t, scale, scale);
self.currentEditintImageView.transform = t;
}else {
// 此處觸發的是平移手勢。。。
if (!self.currentEditintImageView) return;
CGPoint t = [pan translationInView:self.currentEditintImageView];
self.currentEditintImageView.transform = CGAffineTransformTranslate(self.currentEditintImageView.transform, t.x, t.y);
[pan setTranslation:CGPointZero inView:self.currentEditintImageView];
}
self.previousPoint = [pan locationInView:self];
}
// [self resetBorder];
// CGRect rect = CGRectApplyAffineTransform(self.currentEditintImageView.frame, self.currentEditintImageView.transform);
// NSLog(@"%@", NSStringFromCGRect(rect));
}
#pragma mark -私有方法
// 傳入一個點,判斷點是否在水平圖片上
- (UIImageView *)imageViewInLocation:(CGPoint)loc
{
for (UIImageView *imgView in self.imageViews) {
if (CGRectContainsPoint(imgView.frame, loc)) {
[self bringSubviewToFront:imgView];
return imgView;
}
}
return nil;
}
- (void)hideEditingBtn:(BOOL)hidden
{
self.deleteBtn.hidden = hidden;
self.editBtn.hidden = hidden;
if (!hidden) {
self.deleteBtn.center = self.currentEditintImageView.newTopLeft;
self.editBtn.center = self.currentEditintImageView.newBottomRight;
}
}
- (CGFloat)distanceWithPoint:(CGPoint)point otherPoint:(CGPoint)otherPoint
{
return sqrt(pow(point.x - otherPoint.x, 2) + pow(point.y - otherPoint.y, 2));
}
#pragma mark -系統方法
- (void)layoutSubviews
{
[super layoutSubviews];
CGFloat btnWH = 40;
self.deleteBtn.bounds = CGRectMake(0, 0, btnWH, btnWH);
self.editBtn.bounds = CGRectMake(0, 0, btnWH, btnWH);
if (self.imageView.image.size.width < self.width && self.imageView.image.size.height < self.height) {
self.imageView.size = self.imageView.image.size;
self.imageView.center = CGPointMake(self.width * 0.5, self.height * 0.5);
}else {
CGFloat w = 0;
CGFloat h = 0;
if (self.imageView.image.size.width < self.imageView.image.size.height) {
h = self.height;
w = h * self.imageView.image.size.width / self.imageView.image.size.height;
}else {
w = self.width;
h = w * self.imageView.image.size.height / self.imageView.image.size.width;
}
self.imageView.size = CGSizeMake(w, h);
self.imageView.center = CGPointMake(self.width * 0.5, self.height * 0.5);
}
}
#pragma mark -公共方法
- (void)addWatermarkImage:(UIImage *)watermarkImage
{
UIImageView *imageView = [[UIImageView alloc] initWithImage:watermarkImage];
imageView.center = CGPointMake(self.frame.size.width * 0.5, self.frame.size.height * 0.5);
[self addSubview:imageView];
[self.imageViews addObject:imageView];
}
- (void)endEditing
{
self.currentEditintImageView = nil;
[self hideEditingBtn:YES];
// [self resetBorder];
}
#pragma mark -UIGestureDelegate
//這個是手勢代理,允許同時響應多個手勢。。。這個不多說了。
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
return YES;
}
好了,一個簡單的demo就這樣完成了,具體請到本人github https://github.com/ac1217 上面有詳細代碼