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