用户
 找回密码
 立即注册

发帖

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

[复制链接]
  • TA的每日心情
    慵懒
    2025-11-24 10:46
  • 27

    主题

    6

    回帖

    669

    积分

    管理员

    积分
    669
    发表于 2015-6-26 00:00:00
    最近在项目有遇到图片打标签的需求,即在图片上面添加相关标签(标签可移动、删除),然后生成一张新的图片,上传到服务器上。下面就来谈谈我的实现方式吧,如果你有更好的方法,麻烦告诉我,THX。
    实现过程
    我的思路是:Pan手势拖动标签;LongPress手势显示“删除”ItemMenu,点击删除item删除标签;新图片屏幕截图生成;图片展示在画布的方式自适应。首先将标签自定义一个View。
    7 `* i2 q* ^+ e- F" y& |- |! ~2 X
    1. #import <UIKit/UIKit.h>; W. h' a. H2 Z! f. ?. k/ W8 [
    2. 0 U9 u" S- e9 {9 R
    3. /// 通知$ e& Z) F! \* t
    4. extern NSString *const DDAnnotationViewDeleteNotifation;4 Y# L; M* J" W
    5. /// 距离父视图的边缘  u! J5 u" b* v
    6. #define marginSupView 5.0, `5 h$ n9 r  y  ?7 N
    7. ! e( Y" ^, a' P: D
    8. /**
      * X! ~- w! \- _$ B$ |
    9. *  UIImageView上的标注视图  m1 W) [5 p4 p* m, |2 a# H: D
    10. */2 {; n' U0 M5 H8 n+ i- {
    11. @interface DDAnnotationView : UIView
      % \4 M9 M" Z2 G1 T
    12. $ x* f! @0 C& \3 Y* I* W; b5 c% r
    13. /**
      . l  A- R# h. u2 u* r
    14. *  根据Image Frame 布局
      ! u5 J; B- a4 \( }1 C
    15. *
      ! s2 D2 ?* `2 r$ Q0 s7 J3 M1 Z
    16. *  @param frame   <#frame description#>
      6 p1 q5 u: a9 f( h
    17. *  @param details <#details description#>8 H2 R1 x/ u- T
    18. */2 e' H1 I  G1 u+ O7 E3 q
    19. - (void)layoutAllElement:(CGRect)frame details:(NSString *)details;
      3 m% i9 k# C6 E

    20. # t, s4 U$ V" D- L( ?6 ?; l+ d) x
    21. @end
    复制代码
    1. #import "DDAnnotationView.h"
      + m" f+ V* B" t* _% V9 W  Z5 v- s
    2. & {- I3 R6 f  y2 a/ p5 D5 P6 e
    3. NSString *const DDAnnotationViewDeleteNotifation = @"DDAnnotationViewDeleteNotifation";
      - t4 P: k# D( t0 {, x- F( q
    4.   ^- G% L% j. `$ S2 s  `- ]
    5. @interface DDAnnotationView ()
      - [  r0 N* |* L/ k
    6. /**
      & D% i/ o2 [! L. x
    7. *  背景图片. e' O& e( W7 I* `& @6 H. F# j. j1 s5 V
    8. */
      / I4 x+ i; i8 }# L2 x5 x6 O
    9. @property (nonatomic, strong) UIImageView *bgImageView;
      ! |6 e) r; O4 S4 `6 j  n
    10. /**
      6 L% B# ~; a8 h! t
    11. *  字体信息& u7 @; [9 E& V1 |, b$ ?
    12. */
      ' l0 v: x) G; d  f
    13. @property (nonatomic, strong) UILabel *textLabel;, J6 }5 Q# T0 F; I4 Y/ r
    14. 7 H" O7 e% t" ?! {3 z) b
    15. @end7 `/ y& q1 T2 ^+ v8 q" O

    16. $ d& B! ^, m! ~* i9 r
    17. @implementation DDAnnotationView
      # ~: F; ~. B* J! `1 \( Z, j# [

    18. ! e0 k' C9 I8 ~: ]% e
    19. - (void)dealloc {
      8 c& s) ?" u$ H3 \+ h+ e, S
    20.     [self.bgImageView removeFromSuperview];
      ( Q, q2 J) y$ G4 e
    21.     self.bgImageView = nil;4 Y& x( ~! V7 G$ ~2 T7 M2 ]9 j
    22.     [self.textLabel removeFromSuperview];
      + `* d7 v  g% F/ Q5 n6 Z
    23.     self.textLabel = nil;
      ) N) }% W) ?1 e1 i! J( g: B
    24. }! g7 F% _, G' B- P
    25. & n/ ~( C8 r% V+ L
    26. #pragma mark - init Method
      5 Z: z; L1 J# o- N9 r& L' m) f: C1 T
    27. - (id)initWithFrame:(CGRect)frame {
      + Z8 s7 f* c1 ~+ U" L
    28.     self = [super initWithFrame:frame];4 m( y6 i9 R, r4 ~0 R
    29.     if (self) {
        d3 D+ }  r# w( |$ ^2 a# u' m' n
    30.         self.userInteractionEnabled = YES;
      7 o/ L$ b3 o7 G! I4 A  N+ Z/ C
    31.         self.clipsToBounds = YES;. E5 C8 H4 k* F; n9 z6 T' }- A
    32.         self.bgImageView = [[UIImageView alloc] initWithFrame:CGRectZero];
      9 m8 ~5 U! K8 j! E: \4 M; r
    33.         self.bgImageView.backgroundColor = [UIColor clearColor];
      6 O7 k7 m" e8 Y" `, R: N' V
    34.         [self addSubview:self.bgImageView];
      # t; ]7 j% t, }2 z% s0 ?

    35. 8 v' s8 k. v4 M5 W4 e
    36.         self.textLabel = [[UILabel alloc] initWithFrame:CGRectZero];
      ! W; }3 ]: V2 w- A# [
    37.         self.textLabel.backgroundColor = [UIColor clearColor];, {7 ?# G0 W: A) e; m" \' ]' r5 h
    38.         self.textLabel.numberOfLines = 0;; h$ Z' Z: {" S  r
    39.         self.textLabel.textColor = [UIColor whiteColor];
      9 |4 {7 Y6 K- S0 D7 s: H; T
    40.         self.textLabel.font = [UIFont systemFontOfSize:12.0];% I7 e( _' ?; M5 o9 _  s  ~
    41.         self.textLabel.lineBreakMode = NSLineBreakByTruncatingTail;
      . M' K) c) o$ k: r3 z- {, z
    42.         [self.bgImageView addSubview:self.textLabel];% C+ r2 E) ~2 s4 s* R/ `& E' i
    43.     }( ]8 k8 z7 B& q  _
    44.     return self;0 O& X8 ^1 g, X; {: b3 |; ~3 o2 e
    45. }
      : b2 i& w+ J5 c8 a$ `$ l/ H1 a
    46. 6 |/ e8 ]% k$ s, z" S* X
    47. /**" b; w9 z# Q6 D, `! O
    48. *  根据字符串调整 AnnotationView 的位置
      7 J# @% e/ o! z. p% }3 V  ?( r: u3 b* c
    49. *
      1 ]! n9 R1 m4 e* O% x$ t
    50. *  @param frame   frame description5 @$ Q3 w; `7 q4 n% c5 u  }
    51. *  @param details details description" J3 E1 u, U' y% f
    52. */
      0 _7 I8 d9 q: Z/ ^
    53. - (void)layoutAllElement:(CGRect)frame details:(NSString *)details {
      , u6 \4 k* E: n- n
    54.     CGFloat originX = 25.0, kRignt = 5.0, labelWidth = 0;7 ]4 j' W9 X$ g* x
    55.     labelWidth = frame.size.width - originX - kRignt - marginSupView;
      ' k! I' y/ V; N- M

    56.   k1 m4 @& X0 i$ E; |% P0 q( j
    57.     CGSize detailsSize = [details sizeWithFont:self.textLabel.font constrainedToSize:CGSizeMake(labelWidth, MAXFLOAT) lineBreakMode:self.textLabel.lineBreakMode];
      * J9 l3 s  W! `4 A$ f3 K
    58.     NSInteger lines = detailsSize.height / [self.textLabel.font pointSize];
      ! p. L7 S. P4 m* r# O& j4 e! K: b
    59.     self.textLabel.text = details;; M) f& _) U: C1 X
    60.     if (detailsSize.width < labelWidth) {/ Q* ]7 z( @4 S5 I% \1 G% ^
    61.         labelWidth = detailsSize.width + originX + kRignt;3 K9 R/ d: J$ @; t) m; V1 b1 q# R
    62.     }0 Y# r7 u/ |. T( G6 p" p
    63.   [5 ]3 z- b/ p
    64.     NSString *backgroundName = @"Tag_background";- k, z6 H1 D, u8 u
    65.     if (lines > 1) {8 A9 }: z/ n0 h
    66.         // 大于1行6 v. f. c6 d  a1 Y* R
    67.         backgroundName = @"Tag_background2";* }0 h' A% n( @$ \( p2 {
    68.     }
      ' v* i7 @1 L( f5 M; o: C' Z2 Y0 L
    69.     UIImage *bgImage = [DDImageManager getImageWithNameByPath:backgroundName resizable:YES];
      9 I; m/ T0 ~8 g
    70.     backgroundName = nil;2 i$ O) t5 ]- q) J
    71.     if (labelWidth <= bgImage.size.width) {
      & a1 e1 t; x# d8 E4 G( F, d1 U: t
    72.         labelWidth = bgImage.size.width;  g+ w& r8 A1 Q* t5 S$ Y7 M
    73.     }
      3 ?. V8 M: L& W  n" j
    74.     self.bgImageView.frame = CGRectMake(0.0, 0.0, labelWidth,  detailsSize.height + kRignt);' `0 l4 ]( h. c5 F* D
    75.     self.bgImageView.image = bgImage;
      " L/ z6 t- V, X1 l1 N
    76.     bgImage = nil;
      : C% }1 m$ X% M# ~7 U6 u. \+ K
    77.     self.textLabel.frame = CGRectMake(originX, kRignt, detailsSize.width, detailsSize.height);
      7 X- M8 j+ m5 H# l" f7 ~: B$ {
    78.     self.textLabel.yt_centerY = self.bgImageView.yt_centerY;
      2 b" G6 j: o5 R, n8 ~7 C+ i
    79.     self.frame = (CGRect){frame.origin, self.bgImageView.yt_width, self.bgImageView.yt_height};! g/ d$ q8 \8 P7 s5 m  t$ X

    80. ; u) A% B$ N# W. `
    81.     // 长按手势
      ! w5 }5 h0 `, F0 G# Z
    82.     UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longPressGesture:)];4 B& [$ m9 H; y, T
    83.     [self addGestureRecognizer:longPress];
      + M! b- X2 E! Q/ f) r
    84.     longPress = nil;( I! C' ]' \  s- t. r' Z

    85. + ~+ A, V6 t* O  A4 o7 `: g! C
    86.     // 拖动手势7 m. z7 u2 c, p( c
    87.     UIPanGestureRecognizer *panGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePanGesture:)];' m8 f. i) h( g* M& c
    88.     [self addGestureRecognizer:panGestureRecognizer];
      6 p2 e2 M- S$ n. Z+ x$ m
    89.     panGestureRecognizer = nil;
      ; F0 w# L8 V% R& A( \: n
    90. }
      + D( R1 K# o5 U) ^2 s
    91. ! t' _, k# z2 q' @
    92. #pragma mark - 长按删除操作* b% D8 N% U; c! \  Q2 {& C0 A9 l
    93. - (BOOL)canBecomeFirstResponder {
      & ]4 v. M# C! O/ G8 A
    94.     return YES;7 e* i& e0 V7 v. C$ t- y+ h  t
    95. }# D: S- X. d# J, \! v' M
    96. 4 T6 K: O" z7 ^" v: O1 |. P
    97. - (BOOL)canPerformAction:(SEL)action withSender:(id)sender {& A3 P* [, |  t2 ]
    98.     return (action == @selector(deleteMethod:));
      ' k; K) f) `5 m/ a  \! R
    99. }2 n+ U! j  Y6 ]3 z2 F
    100. 9 b* G) W" O+ v; ~7 l  \* t
    101. - (void)showDeleteMenu {3 o- |" p( `* n: N
    102.     [self becomeFirstResponder];
      # e- ~6 ^* I6 p
    103.     UIMenuController *menuController = [UIMenuController sharedMenuController];  X3 z4 B) A7 O1 N
    104.     UIMenuItem *copyItem = [[UIMenuItem alloc] initWithTitle:@"删除" action:@selector(deleteMethod:)];1 _& U2 t+ C; ^1 i
    105.     menuController.menuItems = @[copyItem];3 d* ^' ?( N, _* B
    106.     [menuController setTargetRect:self.frame inView:self.superview];! b& X5 q7 }6 \4 b8 R3 [
    107.     [menuController setMenuVisible:YES animated:NO];
      2 M- }# F$ Q+ T7 U  w9 d- t# r! T4 Y
    108.     menuController = nil;- d4 U2 h6 E. f1 G4 Q3 d6 Y
    109. }& V; U6 X) d4 x% P6 D$ }+ H1 B

    110. " c( ~5 X% ~1 @& I: `  K3 ~: C
    111. /**
      & v: y- i* l4 ?/ b5 M3 C
    112. *  删除 方法
      # x9 j3 q9 s- M+ _2 l  z* \
    113. *
      3 Y' B9 j2 o0 g0 m9 ~; H+ @
    114. *  @param sender <#sender description#>
      3 K& O$ F9 O! ?! {
    115. */
      1 E- a8 k; Z$ ]- L4 K; n$ @& P+ O7 Z0 p
    116. - (void)deleteMethod:(id)sender {
      5 t) }' N& L" V2 R
    117.     if ([self superview]) {* E0 F/ ~7 s" j! ]; r
    118.         [self removeFromSuperview];) P8 s  \7 C% G3 ~
    119.         // 发送通知- y5 W4 F. {# q6 c3 D: E
    120.         [[NSNotificationCenter defaultCenter] postNotificationName:DDAnnotationViewDeleteNotifation object:nil];  b9 J; x8 ~0 {0 B7 u8 H: O
    121.     }
      # h8 c4 O- f% ^) |& h2 _( \
    122. }
      : H3 N2 b: L* l3 i- J! }0 s7 l
    123. 1 I/ D: R- n/ q/ S: Z3 ?8 S
    124. /**
      ) p! \( q8 I# x6 _
    125. *  长按手势- C# ^2 U0 R/ V$ K' j" e/ H" G
    126. */ O7 g, a2 m7 t) g' \
    127. *  @param longTap longTap description
      8 }8 V; f" ~( g, t* h. D
    128. */
      1 s6 X0 m4 b" N! X0 v! p- o/ [
    129. - (void)longPressGesture:(UILongPressGestureRecognizer *)longTap {
      1 t: t6 d- v" |, B: g4 ?- @2 J
    130.     if (longTap.state == UIGestureRecognizerStateEnded) {# Q5 M( I, q- L  H  b
    131.         return;9 ~: B9 R7 v9 ?) D+ c
    132.     }
      + R( g; o" @7 W5 }2 K2 B5 c
    133.     CGRect tmpRect = self.frame;
      ' T9 r: g0 O/ f( u1 x  s
    134.     if (tmpRect.size.height != 0) {
      ; ?' Z& a+ D3 k$ R' e: c
    135.         [self showDeleteMenu];
      2 X/ v3 S. U: e: [) R
    136.     }
      ) b! g/ e  y5 e6 T, ~
    137. }
      0 j2 c6 Y/ n1 D: a4 ^& b
    138. ! a. u* |& e0 T
    139. /**" K( q) j8 F. D+ Q9 @2 b
    140. *  拖动手势
      * m, a$ W; X2 B3 o: ^  ^1 s
    141. *
      8 `+ h# `5 s3 l
    142. *  @param panTap panTap description9 }" C( g5 X0 O) Y
    143. */
      ! |" X, u8 J$ T: `: z
    144. - (void)handlePanGesture:(UIPanGestureRecognizer *)panTap {1 f$ E* t" ~/ q9 l

    145. 9 ^$ q( t! a9 ^2 E5 @  S
    146.     UIView *panView = panTap.view;& |- y7 b6 b3 l$ l1 t2 W/ `6 h; b
    147.     CGPoint translation = [panTap translationInView:panView];
      # X* @1 T# w5 o' s8 `, l! N6 u
    148.     CGPoint newCenter = CGPointMake(panTap.view.center.x + translation.x, panTap.view.center.y + translation.y);! g. ^7 f, m/ {8 ^
    149.     // 设置拖动范围
      , \. c  }- _, R0 [
    150.     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)) {
      ( B) D' n  x; j# s
    151.         panTap.view.center = newCenter;: w0 g, P  V' J2 n% P9 w
    152.         [panTap setTranslation:CGPointZero inView:panView.superview];
      7 f6 j4 @2 r9 G! y3 T6 u# d& ^

    153. ' V6 U7 `& r0 G3 H; G8 C
    154.         UIView *sonView = nil;: ^& M6 \0 ^! j3 V* L: [0 l7 w
    155.         if ([[self.superview subviews] count] > 0) {
      - l6 K" o: Z: E' K
    156.             sonView = [[self.superview subviews] lastObject];6 F* Q/ v* _! S9 H5 f! W
    157.             if (sonView && sonView != self) {
      " }( G5 J# \" M3 Z( Q
    158.                 //放到最前面
        X9 P$ e! V+ M( k; G
    159.                 [self.superview bringSubviewToFront:self];
      6 Q. R( ~8 A4 K0 m' C" v
    160.             }7 I7 x% ^3 o) V- ]' K
    161.         }0 ~: M* Y- K4 v
    162.     }
      ' x. a( _# J6 N4 K' u* x
    163. }
    复制代码

    - V% ~0 K6 W$ p( _. T0 A
    好了,标签控件已经封装好了,现在的问题是图片有大有小呀,那该怎么显示?产品那边要求,在保证图片不拉伸的情况下,图片至少一边充满屏幕(要么上下充满、要么左右充满)。我是这么做的,首先,我获得Image数据后,不管怎么样都将图片缩放至我的画布大小(里面的逻辑应该是,如果图片宽带超过画布宽度,图片高度小于等于画布高度,则按照画布宽度缩放。以此类推)。缩放方法如下(UIImge 的 Category)。: R# }% U5 ?1 J  D7 P% y
    1. - (UIImage *)zoom:(CGSize)size {
      % |* N! X( ^8 ~) B4 ~4 R
    2.     CGSize imageSize = self.size;
      & D' L! S* V0 V: ?
    3.     CGFloat width = imageSize.width;
      / D; v7 b5 H) x! {6 R
    4.     CGFloat height = imageSize.height;+ q3 a  V3 c$ b( V
    5.    
      ) `9 n0 r4 C) C, b1 r* ?1 U
    6.     if (width <= size.width && height <= size.height) {2 c( N8 Y, ^6 L: {
    7.          return self;
      " ~  k: \- l: @" @" T! R
    8.     }
      3 Z& c. M8 K4 a4 n
    9.     if (width == 0 || height == 0) {1 _7 n, W  U& Q3 y( V& d# n
    10.         return self;" B8 v! a8 o9 B7 _/ L6 O6 q+ z# U
    11.     }' f: {. j6 A4 ]5 L
    12.     7 n; Q* i! h- E5 N5 ~4 y+ _% r
    13.     UIImage *newImage = nil;) t+ _: c; G: Q: b! H9 k: H% j
    14.     CGFloat widthFactor = size.width / width;# d; ^1 S" q4 M) [8 q& [0 l" k
    15.     CGFloat heightFactor = size.height / height;+ j; E4 k8 A% ?1 B; O# a3 T4 M# m
    16.     CGFloat scaleFactor = 0.0;
      - Y6 D0 s, w4 _& Y
    17.       M' C" A4 S1 P8 f- ?
    18.     if (widthFactor > heightFactor){
      4 S/ S9 d. v& _4 j
    19.         scaleFactor = heightFactor; * N3 P  A+ [$ p$ p$ P2 n1 P& ]
    20.     } else {
      ) o8 P3 }' ?" ]8 N
    21.         scaleFactor = widthFactor;
      5 h% Q; i/ `$ X# Z) U, u# {! J5 q0 j
    22.     }* m9 a1 a( ~4 Q/ V: q
    23.    
      ! e, a% y5 {  X, R: i0 d1 i2 m
    24.     CGFloat scaledWidth  = width * scaleFactor;
      0 r0 `% j9 [- E+ m+ F4 I6 X
    25.     CGFloat scaledHeight = height * scaleFactor;0 L; v1 W/ W9 G! Z, K5 R3 ^8 D' ]
    26.     CGSize newtargetSize = CGSizeMake(scaledWidth, scaledHeight);! E1 @, t& p7 O& `; @2 R
    27.     UIGraphicsBeginImageContext(newtargetSize); 1 E; [& j! a; g) K% l0 U
    28.    
      " m1 i" R& `$ O6 S- A9 m* V
    29.     CGRect thumbnailRect = CGRectZero;
      ) b' r& f" L: i, K
    30.     thumbnailRect.size.width  = scaledWidth;
      2 w: B' n+ A4 z& e" l, _
    31.     thumbnailRect.size.height = scaledHeight;
      - A! \' w% {" [" p7 K4 D- }
    32.    
      ; ?- j' E0 O, r
    33.     [self drawInRect:thumbnailRect];( T6 c2 v; ~& |# ^
    34.     newImage = UIGraphicsGetImageFromCurrentImageContext();7 N" k+ X9 p" ^1 I6 _+ I+ C
    35.    
      4 a& l* F2 c8 X0 A( K/ j
    36.     UIGraphicsEndImageContext();
      ; N! A2 M6 }5 N3 B, b5 z
    37.     return newImage;
        ?' b! ~6 W: ~2 C7 j+ k
    38. }
    复制代码
    截图方法很简单,因为标签添加在UIImageView上面,只要截图UIImageView即可。截图方法如下:/ m* ~, o0 B# T+ R& ]" V
    1. - (UIImage *)screenCapShowImage:(UIView *)tagView {
      ; I5 M- E- m5 E0 c+ l, `
    2.     CGFloat scale = [[UIScreen mainScreen] scale];8 p$ V+ ~8 F% h! i: p3 q% d5 h/ V
    3.     CGRect viewFrame = tagView.frame;3 z4 ~1 o5 C: |1 R
    4.     if (scale > 0.0) {
      : u0 t' J1 m" z2 Q
    5.         UIGraphicsBeginImageContextWithOptions(viewFrame.size, NO, scale);
      4 V( d  U' c2 S) M
    6.     } else {& m  C/ e4 ~8 }1 D8 g
    7.         UIGraphicsBeginImageContext(viewFrame.size);# O' o% d5 `8 B: V! `
    8.     }
      5 a: }1 {+ y$ t% p- e
    9.     [tagView.layer renderInContext:UIGraphicsGetCurrentContext()];$ ?2 L' r! I3 O& O* A/ F3 A
    10.     UIImage *theImage = UIGraphicsGetImageFromCurrentImageContext();
      * J/ L% f* |. _% r" p1 M
    11.     UIGraphicsEndImageContext();
      ! F. ^8 T  f' @% _
    12.     return theImage;
      ) u" A; d2 U% a! H9 X3 {
    13. }
    复制代码
    总结
    综上所述,这个功能的实现还是比较简单的,只要思路能行的通,实现过程中的一些问题基本上Google可以解决。
    参考链接& C& ~, i, @. y: F
    使用道具 举报 回复
    严禁恶意灌水!!!拒绝伸手党!!!
    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    ض