0%

图片打标签的一种实现方式

最近在项目有遇到图片打标签的需求,即在图片上面添加相关标签(标签可移动、删除),然后生成一张新的图片,上传到服务器上。下面就来谈谈我的实现方式吧,如果你有更好的方法,麻烦告诉我,THX。

###实现过程
我的思路是:Pan手势拖动标签;LongPress手势显示“删除”ItemMenu,点击删除item删除标签;新图片屏幕截图生成;图片展示在画布的方式自适应。首先将标签自定义一个View。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#import <UIKit/UIKit.h>

/// 通知
extern NSString *const DDAnnotationViewDeleteNotifation;
/// 距离父视图的边缘
#define marginSupView 5.0

/**
* UIImageView上的标注视图
*/
@interface DDAnnotationView : UIView

/**
* 根据Image Frame 布局
*
* @param frame <#frame description#>
* @param details <#details description#>
*/
- (void)layoutAllElement:(CGRect)frame details:(NSString *)details;

@end

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
#import "DDAnnotationView.h"

NSString *const DDAnnotationViewDeleteNotifation = @"DDAnnotationViewDeleteNotifation";

@interface DDAnnotationView ()
/**
* 背景图片
*/
@property (nonatomic, strong) UIImageView *bgImageView;
/**
* 字体信息
*/
@property (nonatomic, strong) UILabel *textLabel;

@end

@implementation DDAnnotationView

- (void)dealloc {
[self.bgImageView removeFromSuperview];
self.bgImageView = nil;
[self.textLabel removeFromSuperview];
self.textLabel = nil;
}

#pragma mark - init Method
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
self.userInteractionEnabled = YES;
self.clipsToBounds = YES;
self.bgImageView = [[UIImageView alloc] initWithFrame:CGRectZero];
self.bgImageView.backgroundColor = [UIColor clearColor];
[self addSubview:self.bgImageView];

self.textLabel = [[UILabel alloc] initWithFrame:CGRectZero];
self.textLabel.backgroundColor = [UIColor clearColor];
self.textLabel.numberOfLines = 0;
self.textLabel.textColor = [UIColor whiteColor];
self.textLabel.font = [UIFont systemFontOfSize:12.0];
self.textLabel.lineBreakMode = NSLineBreakByTruncatingTail;
[self.bgImageView addSubview:self.textLabel];
}
return self;
}

/**
* 根据字符串调整 AnnotationView 的位置
*
* @param frame frame description
* @param details details description
*/
- (void)layoutAllElement:(CGRect)frame details:(NSString *)details {
CGFloat originX = 25.0, kRignt = 5.0, labelWidth = 0;
labelWidth = frame.size.width - originX - kRignt - marginSupView;

CGSize detailsSize = [details sizeWithFont:self.textLabel.font constrainedToSize:CGSizeMake(labelWidth, MAXFLOAT) lineBreakMode:self.textLabel.lineBreakMode];
NSInteger lines = detailsSize.height / [self.textLabel.font pointSize];
self.textLabel.text = details;
if (detailsSize.width < labelWidth) {
labelWidth = detailsSize.width + originX + kRignt;
}

NSString *backgroundName = @"Tag_background";
if (lines > 1) {
// 大于1行
backgroundName = @"Tag_background2";
}
UIImage *bgImage = [DDImageManager getImageWithNameByPath:backgroundName resizable:YES];
backgroundName = nil;
if (labelWidth <= bgImage.size.width) {
labelWidth = bgImage.size.width;
}
self.bgImageView.frame = CGRectMake(0.0, 0.0, labelWidth, detailsSize.height + kRignt);
self.bgImageView.image = bgImage;
bgImage = nil;
self.textLabel.frame = CGRectMake(originX, kRignt, detailsSize.width, detailsSize.height);
self.textLabel.yt_centerY = self.bgImageView.yt_centerY;
self.frame = (CGRect){frame.origin, self.bgImageView.yt_width, self.bgImageView.yt_height};

// 长按手势
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longPressGesture:)];
[self addGestureRecognizer:longPress];
longPress = nil;

// 拖动手势
UIPanGestureRecognizer *panGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePanGesture:)];
[self addGestureRecognizer:panGestureRecognizer];
panGestureRecognizer = nil;
}

#pragma mark - 长按删除操作
- (BOOL)canBecomeFirstResponder {
return YES;
}

- (BOOL)canPerformAction:(SEL)action withSender:(id)sender {
return (action == @selector(deleteMethod:));
}

- (void)showDeleteMenu {
[self becomeFirstResponder];
UIMenuController *menuController = [UIMenuController sharedMenuController];
UIMenuItem *copyItem = [[UIMenuItem alloc] initWithTitle:@"删除" action:@selector(deleteMethod:)];
menuController.menuItems = @[copyItem];
[menuController setTargetRect:self.frame inView:self.superview];
[menuController setMenuVisible:YES animated:NO];
menuController = nil;
}

/**
* 删除 方法
*
* @param sender <#sender description#>
*/
- (void)deleteMethod:(id)sender {
if ([self superview]) {
[self removeFromSuperview];
// 发送通知
[[NSNotificationCenter defaultCenter] postNotificationName:DDAnnotationViewDeleteNotifation object:nil];
}
}

/**
* 长按手势
*
* @param longTap longTap description
*/
- (void)longPressGesture:(UILongPressGestureRecognizer *)longTap {
if (longTap.state == UIGestureRecognizerStateEnded) {
return;
}
CGRect tmpRect = self.frame;
if (tmpRect.size.height != 0) {
[self showDeleteMenu];
}
}

/**
* 拖动手势
*
* @param panTap panTap description
*/
- (void)handlePanGesture:(UIPanGestureRecognizer *)panTap {

UIView *panView = panTap.view;
CGPoint translation = [panTap translationInView:panView];
CGPoint newCenter = CGPointMake(panTap.view.center.x + translation.x, panTap.view.center.y + translation.y);
// 设置拖动范围
if ((newCenter.y >= panView.yt_height / 2) && (newCenter.y <= panView.superview.yt_height - panView.yt_height / 2) && (newCenter.x >= panView.yt_width / 2) && (newCenter.x <= panView.superview.yt_width - panView.yt_width / 2)) {
panTap.view.center = newCenter;
[panTap setTranslation:CGPointZero inView:panView.superview];

UIView *sonView = nil;
if ([[self.superview subviews] count] > 0) {
sonView = [[self.superview subviews] lastObject];
if (sonView && sonView != self) {
//放到最前面
[self.superview bringSubviewToFront:self];
}
}
}
}

好了,标签控件已经封装好了,现在的问题是图片有大有小呀,那该怎么显示?产品那边要求,在保证图片不拉伸的情况下,图片至少一边充满屏幕(要么上下充满、要么左右充满)。我是这么做的,首先,我获得Image数据后,不管怎么样都将图片缩放至我的画布大小(里面的逻辑应该是,如果图片宽带超过画布宽度,图片高度小于等于画布高度,则按照画布宽度缩放。以此类推)。缩放方法如下(UIImge 的 Category)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
- (UIImage *)zoom:(CGSize)size {
CGSize imageSize = self.size;
CGFloat width = imageSize.width;
CGFloat height = imageSize.height;

if (width <= size.width && height <= size.height) {
return self;
}
if (width == 0 || height == 0) {
return self;
}

UIImage *newImage = nil;
CGFloat widthFactor = size.width / width;
CGFloat heightFactor = size.height / height;
CGFloat scaleFactor = 0.0;

if (widthFactor > heightFactor){
scaleFactor = heightFactor;
} else {
scaleFactor = widthFactor;
}

CGFloat scaledWidth = width * scaleFactor;
CGFloat scaledHeight = height * scaleFactor;
CGSize newtargetSize = CGSizeMake(scaledWidth, scaledHeight);
UIGraphicsBeginImageContext(newtargetSize);

CGRect thumbnailRect = CGRectZero;
thumbnailRect.size.width = scaledWidth;
thumbnailRect.size.height = scaledHeight;

[self drawInRect:thumbnailRect];
newImage = UIGraphicsGetImageFromCurrentImageContext();

UIGraphicsEndImageContext();
return newImage;
}

截图方法很简单,因为标签添加在UIImageView上面,只要截图UIImageView即可。截图方法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
- (UIImage *)screenCapShowImage:(UIView *)tagView {
CGFloat scale = [[UIScreen mainScreen] scale];
CGRect viewFrame = tagView.frame;
if (scale > 0.0) {
UIGraphicsBeginImageContextWithOptions(viewFrame.size, NO, scale);
} else {
UIGraphicsBeginImageContext(viewFrame.size);
}
[tagView.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *theImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return theImage;
}

###总结
综上所述,这个功能的实现还是比较简单的,只要思路能行的通,实现过程中的一些问题基本上Google可以解决。

###参考链接
https://github.com/yackle/CLImageEditor
http://nshipster.com/uimenucontroller