Core Text实现编辑的时候会用到的

首先说一下实现的原理,  首先当手指开始触摸屏幕  以及滑动的时候,  效果与画矩形框是一样的 因此  此时的代码也机会没有区别,

当手指松开后   在当前的矩形框处创建一个临时的textView ,并且背景变为灰色,textView    编辑结束, 在textView的  完成委托方法中    去掉灰色的背景  去掉临时的textView    在相同的位置上 利用coreText   显示出刚才编辑的内容

 

首先 手指触摸 会调用到

TextTool.m

?
1
2
3
4
5
6
7
8
9
10
11
- ( void )touchesBegan:(NSSet *)touches withEvent:(UIEvent *) event {
   UIView *touchedView = [ delegate viewForUseWithTool:self];
  //注意下面这句代码
   [touchedView endEditing:YES];
   // we only track one touch at a time for this tool.
   UITouch *touch = [[ event allTouches] anyObject];
   // remember the touch, and its original start point, for future
   [trackingTouches addObject:touch];
   CGPoint location = [touch locationInView:touchedView];
   [startPoints addObject:[NSValue valueWithCGPoint:location]];
}

 代码  除了注释的那一句 基本上与前面的矩形  是相同的  纪录下来  UITouch 的实例  以及 开始的点 

而注释那一句 得到的touchedView  就是dudelView 如果此处设置为yes   在textView 成为第一响应 弹出键盘后,我们可以直接触摸view  来取消TextView的第一响应状态

 

 

随着手指移动  不断调用到  touchMoved   但是 与前面矩形的实现一样 这里的touchMoved方法为空  画出临时矩形的实现代码在

drawTemporary 方法中

TextTool.m

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
- ( void )drawTemporary {
   if (self.completedPath) {
     [ delegate .strokeColor setStroke];
     [self.completedPath stroke];
   } else {
     UIView *touchedView = [ delegate viewForUseWithTool:self];
     for ( int i = 0; i<[trackingTouches count]; i++) {
       UITouch *touch = [trackingTouches objectAtIndex:i];
       CGPoint startPoint = [[startPoints objectAtIndex:i] CGPointValue];
       CGPoint endPoint = [touch locationInView:touchedView];
       CGRect rect = CGRectMake(startPoint.x, startPoint.y, endPoint.x - startPoint.x, endPoint.y - startPoint.y);
       UIBezierPath *path = [UIBezierPath bezierPathWithRect:rect];
       [ delegate .strokeColor setStroke];
       [path stroke];
     }
   }
}

 completedPath是一个UIBezierPath  的实例, 在touchEnded方法中 将最终矩形的路径 设置到其中, 所以在手指抬起来以前  都只会进入到else分支, 根据 起点与当前手指触摸的点 描出一个矩形来

 

 

当手指抬起来后  进入touchEnded方法,而主要的实现代码就在这里面

TextTool.m

?
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
- ( void )touchesEnded:(NSSet *)touches withEvent:(UIEvent *) event {
   UIView *touchedView = [ delegate viewForUseWithTool:self];
   for (UITouch *touch in [ event allTouches]) {
     // make a rect from the start point to the current point
     NSUInteger touchIndex = [trackingTouches indexOfObject:touch];
     // only if we actually remember the start of this touch...
     if (touchIndex != NSNotFound) {
       CGPoint startPoint = [[startPoints objectAtIndex:touchIndex] CGPointValue];
       CGPoint endPoint = [touch locationInView:touchedView];
       [trackingTouches removeObjectAtIndex:touchIndex];
       [startPoints removeObjectAtIndex:touchIndex];
       
       // detect short taps that are too small to contain any text;
       // these are probably accidents
       if (distanceBetween(startPoint, endPoint) < 50.0) return ;
       
       CGRect rect = CGRectMake(startPoint.x, startPoint.y, endPoint.x - startPoint.x, endPoint.y - startPoint.y);
       self.completedPath = [UIBezierPath bezierPathWithRect:rect];
       
       // draw a shaded area over the entire view, so that the user can
       // easily see where to focus their attention.
       UIView *backgroundShade = [[[UIView alloc] initWithFrame:touchedView.bounds] autorelease];
       backgroundShade.backgroundColor = [UIColor colorWithWhite:0.0 alpha:0.5];
       backgroundShade.tag = SHADE_TAG;
       backgroundShade.userInteractionEnabled = NO;
       [touchedView addSubview:backgroundShade];
       
       // now comes the fun part.  we make a temporary UITextView for the
       // actual text input.
       UITextView *textView = [[[UITextView alloc] initWithFrame:rect] autorelease];
       textView.font = delegate .font;
       [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardDidShow:) name:UIKeyboardDidShowNotification object :nil];
       [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object :nil];
       [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardDidHide:) name:UIKeyboardDidHideNotification object :nil];
       [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object :nil];
       
       // in case the chosen view is going to be below the keyboard, we need to
       // make an effort to determine how far the display area should slide when
       // the keyboard is going to be shown.
       //
       // keyboard heights:
       //
       // 352 landscape
       // 264 portrait
       CGFloat keyboardHeight = 0;
       UIInterfaceOrientation orientation = ((UIViewController*) delegate ).interfaceOrientation;
       if (UIInterfaceOrientationIsPortrait(orientation)) {
         keyboardHeight = 264;
       } else {
         keyboardHeight = 352;
       }
       CGRect viewBounds = touchedView.bounds;
       CGFloat rectMaxY = rect.origin.y + rect.size.height;
       CGFloat availableHeight = viewBounds.size.height - keyboardHeight;
       if (rectMaxY > availableHeight) {
         // calculate a slide distance so that the dragged box is centered vertically
         viewSlideDistance = rectMaxY - availableHeight;
       } else {
         viewSlideDistance = 0;
       }
       
       textView. delegate = self;
       [touchedView addSubview:textView];
       textView.editable = NO;
       textView.editable = YES;
       [textView becomeFirstResponder];
     }
   }
}

 首先 得到起点,终点,然后清空数组

if (distanceBetween(startPoint, endPoint) < 50.0) return;  进行了一个判断,  防止用户画的矩形太小,影响输入文字,

如果大小没问题,接下来  便将最终的矩形路径  赋值到 self.completedPath    在退出此方法后 进入drawTemporary后就会进入if分支

接着画出 灰色的背景, 此处的设置透明度为0.5,  并且为背景view设置tag为SHADE_TAG  这个标记用来在以后 编辑完成的时候通过 viewWithTag方法 找到这个背景的实例  并且 移除它

接下来便开始创建textView   其frame就是 刚在最终得到的矩形路径的位置信息

接着注册了一些通知, 即在键盘弹出,或者落下的时候 调用的几个方法

下面的一部分代码实现的功能是   如果键盘弹出 可能会挡住 textView 判断 是否挡住了 如果挡住了 需要向上移动多少距离,并且将这个距离 赋值给全局的 变量viewSlideDistance  如果没有挡住 设置为0

最后将textView显示出来,并且通过[textView becomeFirstResponder];  在其刚显示的时候 马上成为第一响应者,弹出键盘

 

 

 

下面两个方法在键盘弹出之前以及落下之前,调用  移动视图,防止textView 被挡

?
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
- ( void )keyboardWillShow:(NSNotification *)aNotification { 
   UIInterfaceOrientation orientation = ((UIViewController*) delegate ).interfaceOrientation;
     [UIView beginAnimations: @"viewSlideUp" context:NULL];
   UIView *view = [ delegate viewForUseWithTool:self];
   CGRect frame = [view frame];
   switch (orientation) {
     case UIInterfaceOrientationLandscapeLeft:
       frame.origin.x -= viewSlideDistance;
       break ;
     case UIInterfaceOrientationLandscapeRight:
       frame.origin.x += viewSlideDistance;
       break ;
     case UIInterfaceOrientationPortrait:
       frame.origin.y -= viewSlideDistance;
       break ;
     case UIInterfaceOrientationPortraitUpsideDown:
       frame.origin.y += viewSlideDistance;
       break ;
     default :
       break ;
   }
   [view setFrame:frame];
     [UIView commitAnimations];
}
- ( void )keyboardWillHide:(NSNotification *)aNotification {
   UIInterfaceOrientation orientation = ((UIViewController*) delegate ).interfaceOrientation;
     [UIView beginAnimations: @"viewSlideDown" context:NULL];
   UIView *view = [ delegate viewForUseWithTool:self];
   CGRect frame = [view frame];
   switch (orientation) {
     case UIInterfaceOrientationLandscapeLeft:
       frame.origin.x += viewSlideDistance;
       break ;
     case UIInterfaceOrientationLandscapeRight:
       frame.origin.x -= viewSlideDistance;
       break ;
     case UIInterfaceOrientationPortrait:
       frame.origin.y += viewSlideDistance;
       break ;
     case UIInterfaceOrientationPortraitUpsideDown:
       frame.origin.y -= viewSlideDistance;
       break ;
     default :
       break ;
   }
   [view setFrame:frame];
     [UIView commitAnimations];
}

 

 

 

当我们编辑完了内容后  我们可以点击屏幕的其他地方(我们设置了[dudelView endEditing:YES])     或者点击键盘上的落下键盘按钮   这时 进入textView的  delegate

 

 

?
1
2
3
4
5
6
7
8
9
10
11
- ( void )textViewDidEndEditing:(UITextView *)textView {
   //NSLog(@"textViewDidEndEditing");
   TextDrawingInfo *info = [TextDrawingInfo textDrawingInfoWithPath:completedPath text:textView.text strokeColor: delegate .strokeColor font: delegate .font];
   [ delegate addDrawable:info];
   self.completedPath = nil;
   UIView *superView = [textView superview];
   [[superView viewWithTag:SHADE_TAG] removeFromSuperview];
   [textView resignFirstResponder];
   [textView removeFromSuperview];
   [[NSNotificationCenter defaultCenter] removeObserver:self];
}

 

 这里  用到了一个新类TextDrawingInfo  同上一章  的PathDrawingInfo  类似,都是 存放一个完整操作的  全部信息,由于上一章我们做的是绘图操作,因此PathDrawingInfo 存放的是每次绘图操作的信息,  而这次我们主要向利用coreText在屏幕上显示文字  那么之前绘制的矩形的信息 此时已经没有用 我门要  取得  CoreText 需要的信息,即我们这次文字操作的信息  存到TextDrawingInfo 中去  并且添加到 dudelView  的 drawables中  最后  将没用的东西全部从屏幕上移除    

 

最后看以下  TextDrawingInfo  中的draw方法是怎么把文字绘制上去的

 

?
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
- ( void )draw {
   CGContextRef context = UIGraphicsGetCurrentContext();
 
   NSMutableAttributedString *attrString = [[[NSMutableAttributedString alloc] initWithString:self.text] autorelease];
   [attrString addAttribute:(NSString *)(kCTForegroundColorAttributeName) value:(id)self.strokeColor.CGColor range:NSMakeRange(0, [self.text length])];
   
   CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)attrString);
   
   CTFrameRef frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, [attrString length]), self.path.CGPath, NULL);
   CFRelease(framesetter);
   //CFRelease(attrString);
   if (frame) {
     CGContextSaveGState(context);
     
     // Core Text wants to draw our text upside-down!  This flips it the
     // right way.
     CGContextTranslateCTM(context, 0, path.bounds.origin.y);
     CGContextScaleCTM(context, 1, -1);
     CGContextTranslateCTM(context, 0, -(path.bounds.origin.y + path.bounds.size.height));
 
     CTFrameDraw(frame, context);
 
     CGContextRestoreGState(context);
 
     CFRelease(frame);
   }
}

 首先获得当前的上下文 

创建一个属性自字符串NSMutableAttributedString  并设置他的颜色以及其他属性

利用该属性字符串 创建一个CTFramesetterRef

再创建一个CTFrameRef

释放之前创建的CTFramesetterRef  对象framesetter

由于CoreText 是来自于Mac OS X的  它在绘图的时候 认为坐标轴是倒置的,所以在没ios中会产生倒置的效果,这里要转化以下才能正常显示

 

 

 

 

 

学习这一章 主要学习它的思路     怎么实现在一个view上   画出一个矩形框后 就是一个testView  并且编辑完成后  在相应的位置相识出编辑的内容  当然我们也可以利用 一些其他的图文混排的库  比如DTcoreText

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值