UITextView:定制选中文字的菜单和…

本文介绍了如何在iOS应用中继承UITextView并定制选中文字的菜单,包括禁用默认的复制和全选选项,添加自定义的‘选择文字’和‘取消选中’菜单项。同时,展示了通过代码选中指定文字、让键盘主动显示以及实现输入框按return键的回车功能。

前言

先看一篇文章中的文字(原文出处:iOS 7系列译文:认识 TextKit):
iOS 4:iOS 3.2 发布仅仅几个月后就发布了,文本方面没有一丁点新功能。(个人经历:在 WWDC,我走近工程师们,告诉他们我想要一个完善的 iOS 文本布局系统。回答是:“哦…提交个请求。”不出所料…) 
iOS 5:文本方面没啥变化。(个人经历:在 WWDC,我和工程师们谈及 iOS 上文本系统。回答是:“我们没有看到太多的请求…” 靠!) 
iOS 6:有些动作了:属性文本编辑被加入了UITextView。很不幸的是,它很难定制。默认的UI有粗体、斜体和下划线。用户可以设置字体大小和颜色。粗看起来相当不错,但还是没法控制布局或者提供一个便利的途径来定制文本属性。然而对于(文本编辑)开发者,有一个大的新功能:可以继承 UITextView 了,这样的话,除了以前版本提供的键盘输入外,开发者可以“免费”获得文本选择功能。必须实现一个完全自定义的文本选择功能,可能是很多对非纯文本工具开发的尝试半途而废的原因。(个人经历:我,WWDC,工程师们。我想要一个 iOS 的文本系统。回答:“嗯。吖。是的。也许?看,它只是不执行…” 所以毕竟还是有希望,对吧?) 
iOS 7:终于来了,TextKit。

很久之前就写了关于本文所说的两个功能的Demo,现在才有空总结下,清下草稿箱里的笔记。
看了以上文字才知道原来定制选中文字菜单和选择文字是iOS 6才能获取的特性,以上文字出处的作者绝对是个元老级iOS开发者了,从iOS 2玩到了iOS 7。
Anyway,定制UITextView绝对是件好玩的事。


继承UITextView

1.定制选中文字的菜单

首先新建一个类,继承自UITextView,假设类名为MyTextView,关键代码如下:
[objc]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1.   
  2. (BOOL)canBecameFirstResponder  
  3.     return YES 
  4.  
  5.   
  6.   
  7. (BOOL)canPerformAction:(SEL)action withSender:(id)sender  
  8.     if (action == @selector(copy:)) // 菜单不能响应copy项  
  9.         return NO 
  10.      
  11.     else if (action == @selector(selectAll:)) // 菜单不能响应select all项  
  12.         return NO 
  13.      
  14.       
  15.     // 事实上一个return NO就可以将系统的所有菜单项全部关闭了  
  16.     return NO 
  17.  

以上第一个方法用来确保我们选中文字后的菜单可以弹出,第二个方法用来关闭菜单中所有系统的菜单项,如copy, select, select all等。

然后使用UIMenuController定制菜单:
[objc]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. // 自定义text view选中文字后的菜单  
  2. UIMenuItem *selectItem [[UIMenuItem alloc] initWithTitle:@"选择文字" action:@selector(callSelectText:)];  
  3. UIMenuItem *cancelItem [[UIMenuItem alloc] initWithTitle:@"取消选中" action:@selector(cancelSelection:)];  
  4. [UIMenuController sharedMenuController].menuItems @[selectItem, cancelItem];  

注意必须实现两个MenuItem的响应方法才能显示出菜单:
[objc]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. #pragma mark Menu Item Actions  
  2.   
  3. (void)callSelectText:(id)sender  
  4.     self.currentSelection_ self.myTextView.selectedRange 
  5.     self.selectOptionView.hidden NO 
  6.     [self.location_inputTextField becomeFirstResponder];  
  7.  
  8.   
  9. (void)cancelSelection:(id)sender  
  10.     self.myTextView.selectedRange NSRangeZero;  
  11.  



最终效果如下:


之前的项目没有要求定制菜单项的图像,直接看SDK的内容的话貌似也没有Image之类的属性或方法,所以深层次定制菜单项的内容不得而知了。


2.通过代码选中一段文字

这个很简单,直接改变UITextView的selectedRange属性的值就可以了:
[objc]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. @property(nonatomicNSRange selectedRange;  

例如我们点击选择文字后弹出一个文字选择的输入视图,这个我用一个XIB文件定制:


小心了,将xib中的UI组件和View Controller中的Outlet连接时,在代码中要先从xib文件中加载视图,才能使用其中的UI组件,例如:
[objc]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. NSArray *nibViews [[NSBundle mainBundle] loadNibNamed:@"SelectOptionView" owner:self options:nil];  
  2. self.selectOptionView nibViews[0];  
  3. self.selectOptionView.center CGPointMake(self.view.center.xself.view.bounds.size.height 3);  
  4. self.selectOptionView.hidden YES 
  5. [self.view addSubview:self.selectOptionView];  
  6.   
  7. // 要先加载了nib,IBOutlet才有意义,然后再设置其属性  
  8. self.location_inputTextField.delegate self 
  9. self.length_inputTextField.delegate   self 

如果将

    self.location_inputTextField.delegate =self;

    self.length_inputTextField.delegate   =self;

这两行代码置于loadNibNamed方法之前,那么两个文本输入框的delegate将为空(因为他们本身都是空,还没有加载)。

选择文字的Action代码为:
[objc]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. #pragma mark Select View Actions  
  2.   
  3. (IBAction)selectText:(id)sender  
  4.     NSInteger loc self.location_inputTextField.text.integerValue 
  5.     NSInteger len self.length_inputTextField.text.integerValue 
  6.     NSUInteger textLength self.myTextView.text.length 
  7.     if (loc 0 || len 0 || loc textLength || len textLength)  
  8.         UIAlertView *alerView [[UIAlertView alloc] initWithTitle:@"错误"  
  9.                                                            message:@"输入出错,输入的数不能小于0和大于文本长度"  
  10.                                                           delegate:nil  
  11.                                                  cancelButtonTitle:@"确定" otherButtonTitles:nil, nil nil];  
  12.         [alerView show];  
  13.         return 
  14.      
  15.     self.currentSelection_ NSMakeRange(loc, len);  
  16.     [self finishSelectingText];  
  17.  
  18.   
  19. (IBAction)cancelSelectText:(id)sender  
  20.     [self finishSelectingText];  
  21.  
  22.   
  23. (void)finishSelectingText  
  24.     [self.location_inputTextField resignFirstResponder];  
  25.     [self.length_inputTextField resignFirstResponder];  
  26.     self.selectOptionView.hidden YES 
  27.       
  28.     [self.myTextView becomeFirstResponder];  
  29.     self.myTextView.selectedRange self.currentSelection_ 
  30.  

没错,只要一句self.myTextView.selectedRange =self.currentSelection_;就可以了。

另外,我们可以在UITextView的以下方法中监听到某段文字被选中:
[objc]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. #pragma mark UITextView Delegate  
  2.   
  3. (void)textViewDidChangeSelection:(UITextView *)textView  
  4.     NSLog(@"Selection changed");  
  5.       
  6.     NSLog(@"loc %d"self.myTextView.selectedRange.location);  
  7.     NSLog(@"len %d"self.myTextView.selectedRange.length);  
  8.  

运行结果:



控制台输出如下:
[plain]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. 2014-02-16 23:33:56.197 MyTextView[4890:70b] Selection changed  
  2. 2014-02-16 23:33:56.198 MyTextView[4890:70b] loc 507  
  3. 2014-02-16 23:33:56.198 MyTextView[4890:70b] len  
  4. 2014-02-16 23:33:56.334 MyTextView[4890:70b] Selection changed  
  5. 2014-02-16 23:33:56.335 MyTextView[4890:70b] loc 507  
  6. 2014-02-16 23:33:56.335 MyTextView[4890:70b] len  
  7. 2014-02-16 23:34:05.291 MyTextView[4890:70b] Selection changed  
  8. 2014-02-16 23:34:05.292 MyTextView[4890:70b] loc 10  
  9. 2014-02-16 23:34:05.292 MyTextView[4890:70b] len 100  


3.让键盘主动出现

为了让用户更省心,我们可以在一个带输入框的视图出现时就让键盘弹出来,而不用用户再点一下输入框了。方法很简单,就一行代码:
[objc]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. [self.location_inputTextField becomeFirstResponder];  


4.两个输入框按return时仿回车功能

有多个输入框,在一个输入框中按了return,然后好像在网站输入框中按了回车,直接跳到下一个输入框,这个也非常简单,就是resignFirstResponder和becomeFirstResponder方法结合使用而已,在UITextField的委托方法中实现:
[objc]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. #pragma mark UITextField Delegate  
  2.   
  3. (BOOL)textFieldShouldReturn:(UITextField *)textField  
  4.     if ([self.location_inputTextField isFirstResponder])  
  5.         [self.location_inputTextField resignFirstResponder];  
  6.         [self.length_inputTextField becomeFirstResponder];  
  7.      
  8.     else if ([self.length_inputTextField isFirstResponder])  
  9.         [self.length_inputTextField resignFirstResponder];  
  10.      
  11.     return YES 
  12. }  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值