iOS 富文本风格NSMutableParagraphStyle、定制UITextView插入图片和定制复制

来源:互联网 发布:mac 安装sass出错 编辑:程序博客网 时间:2024/04/29 00:53

问题一
开发过程中,经常会遇到动态计算行高的问题, 

- (CGRect)boundingRectWithSize:(CGSize)size options:(NSStringDrawingOptions)options attributes:(nullableNSDictionary<NSString *, id> *)attributes context:(nullable NSStringDrawingContext *)contextNS_AVAILABLE(10_11, 7_0);

是苹果推荐的计算方法,显然会遇到段落格式问题,例如行间距、缩进等格式设置需求,attributes传进来的字典中,包含我们设置的字体及格式,其中NSParagraphStyleAttributeName是设置段落风格,NSFontAttributeName是设置字体。

ok,具体来看一下NSParagraphStyleAttributeName的功能。

[objc] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. //   NSParagraphStyleAttributeName 段落的风格(设置首行,行间距,对齐方式什么的)看自己需要什么属性,写什么    
  2.     NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];    
  3.     paragraphStyle.lineSpacing = 10;// 字体的行间距    
  4.     paragraphStyle.firstLineHeadIndent = 20.0f;//首行缩进    
  5.     paragraphStyle.alignment = NSTextAlignmentJustified;//(两端对齐的)文本对齐方式:(左,中,右,两端对齐,自然)    
  6.     paragraphStyle.lineBreakMode = NSLineBreakByTruncatingTail;//结尾部分的内容以……方式省略 ( "...wxyz" ,"abcd..." ,"ab...yz")    
  7.     paragraphStyle.headIndent = 20;//整体缩进(首行除外)    
  8.     paragraphStyle.tailIndent = 20;//    
  9.     paragraphStyle.minimumLineHeight = 10;//最低行高    
  10.     paragraphStyle.maximumLineHeight = 20;//最大行高    
  11.     paragraphStyle.paragraphSpacing = 15;//段与段之间的间距    
  12.     paragraphStyle.paragraphSpacingBefore = 22.0f;//段首行空白空间/* Distance between the bottom of the previous paragraph (or the end of its paragraphSpacing, if any) and the top of this paragraph. */    
  13.     paragraphStyle.baseWritingDirection = NSWritingDirectionLeftToRight;//从左到右的书写方向(一共➡️三种)    
  14.     paragraphStyle.lineHeightMultiple = 15;/* Natural line height is multiplied by this factor (if positive) before being constrained by minimum and maximum line height. */    
  15.     paragraphStyle.hyphenationFactor = 1;//连字属性 在iOS,唯一支持的值分别为0和1    

好了,现在就可以很轻松的计算某一段落高度,例如:

[objc] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. _descAtt = [[NSMutableAttributedString alloc] initWithString:_model.desc];  
  2.        UIFont *descFont = [UIFont PingFangSC_Regular_WithSize:12];  
  3.          
  4.        NSMutableParagraphStyle *descStyle = [[NSMutableParagraphStyle alloc]init];  
  5.        [descStyle setLineSpacing:1];//行间距  
  6.          
  7.        CGSize descSize = [_model.desc boundingRectWithSize:CGSizeMake(w, MAXFLOAT)  
  8.                                                    options:NSStringDrawingUsesLineFragmentOrigin  
  9.                                                 attributes:@{NSFontAttributeName:descFont,  
  10.                                                              NSParagraphStyleAttributeName :descStyle}  
  11.                                                    context:nil].size;  

另外,再介绍几个富文本处理的属性:

[objc] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. // NSFontAttributeName                设置字体属性,默认值:字体:Helvetica(Neue) 字号:12  
  2. // NSForegroundColorAttributeNam      设置字体颜色,取值为 UIColor对象,默认值为黑色  
  3. // NSBackgroundColorAttributeName     设置字体所在区域背景颜色,取值为 UIColor对象,默认值为nil, 透明色  
  4. // NSLigatureAttributeName            设置连体属性,取值为NSNumber 对象(整数),0 表示没有连体字符,1 表示使用默认的连体字符  
  5. // NSKernAttributeName                设定字符间距,取值为 NSNumber 对象(整数),正值间距加宽,负值间距变窄  
  6. // NSStrikethroughStyleAttributeName  设置删除线,取值为 NSNumber 对象(整数)  
  7. // NSStrikethroughColorAttributeName  设置删除线颜色,取值为 UIColor 对象,默认值为黑色  
  8. // NSUnderlineStyleAttributeName      设置下划线,取值为 NSNumber 对象(整数),枚举常量 NSUnderlineStyle中的值,与删除线类似  
  9. // NSUnderlineColorAttributeName      设置下划线颜色,取值为 UIColor 对象,默认值为黑色  
  10. // NSStrokeWidthAttributeName         设置笔画宽度,取值为 NSNumber 对象(整数),负值填充效果,正值中空效果  
  11. // NSStrokeColorAttributeName         填充部分颜色,不是字体颜色,取值为 UIColor 对象  
  12. // NSShadowAttributeName              设置阴影属性,取值为 NSShadow 对象  
  13. // NSTextEffectAttributeName          设置文本特殊效果,取值为 NSString 对象,目前只有图版印刷效果可用:  
  14. // NSBaselineOffsetAttributeName      设置基线偏移值,取值为 NSNumber (float),正值上偏,负值下偏  
  15. // NSObliquenessAttributeName         设置字形倾斜度,取值为 NSNumber (float),正值右倾,负值左倾  
  16. // NSExpansionAttributeName           设置文本横向拉伸属性,取值为 NSNumber (float),正值横向拉伸文本,负值横向压缩文本  
  17. // NSWritingDirectionAttributeName    设置文字书写方向,从左向右书写或者从右向左书写  
  18. // NSVerticalGlyphFormAttributeName   设置文字排版方向,取值为 NSNumber 对象(整数),0 表示横排文本,1 表示竖排文本  
  19. // NSLinkAttributeName                设置链接属性,点击后调用浏览器打开指定URL地址  
  20. // NSAttachmentAttributeName          设置文本附件,取值为NSTextAttachment对象,常用于文字图片混排  
  21. // NSParagraphStyleAttributeName      设置文本段落排版格式,取值为 NSParagraphStyle 对象  

——————————————————————————————————————————————————————————————————

问题二

一、设置textView的行间距1.如果只是静态显示textView的内容为设置的行间距,执行如下代码:

//    textview 改变字体的行间距     NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];     paragraphStyle.lineSpacing = 10;// 字体的行间距          NSDictionary *attributes = @{                                  NSFontAttributeName:[UIFont systemFontOfSize:15],                                  NSParagraphStyleAttributeName:paragraphStyle                                  };     textView.attributedText = [[NSAttributedString alloc] initWithString:@"输入你的内容" attributes:attributes]; 2.如果是想在输入内容的时候就按照设置的行间距进行动态改变,那就需要将上面代码放到textView的delegate方法里-(void)textViewDidChange:(UITextView *)textView{    //    textview 改变字体的行间距    NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];    paragraphStyle.lineSpacing = 20;// 字体的行间距        NSDictionary *attributes = @{                                 NSFontAttributeName:[UIFont systemFontOfSize:15],                                 NSParagraphStyleAttributeName:paragraphStyle                                 };    textView.attributedText = [[NSAttributedString alloc] initWithString:textView.text attributes:attributes]; } 一、设置textView的placeholder    UITextView上如何加上类似于UITextField的placeholder呢,其实在UITextView上加上一个UILabel或者UITextView,如果用UILable的话,会出现一个问题就是当placeholder的文字过长导致换行的时候就会出现问题,而用UITextView则可以有效避免此问题。- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text{    if (![text isEqualToString:@""])        {            _placeholderLabel.hidden = YES;        }     if ([text isEqualToString:@""] && range.location == 0 && range.length == 1)        {            _placeholderLabel.hidden = NO;        }    return YES;} 说明如下:  (1) _placeholderLabel 是加在UITextView后面的UITextView,_placeholderLabel要保证和真正的输入框的设置一样,字体设置成浅灰色,然后[_placeholderLabel setEditable:NO];真正的输入框要设置背景色透明,保证能看到底部的_placeholderLabel。    (2) [text isEqualToString:@""] 表示输入的是退格键    (3) range.location == 0 && range.length == 1 表示输入的是第一个字符
————————————————————————————————————————————————————————————————————

问题三

 

UITextView富文本、插入图片

直接看代码  

_textView  是定义的成员变量

[objc] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. _textView = [[UITextView alloc]init];  
  2.    _textView.font = [UIFont systemFontOfSize:13];  
  3.    _textView.backgroundColor = [UIColor lightGrayColor];  
  4.    _textView.text = [NSString stringWithFormat:@"settttttttttt :%@",self.countStr];  
  5.    _textView.frame = CGRectMake(20100200130);  
  6.    _textView.delegate = self;  
  7.    [self.view addSubview:_textView];  

通过代理方法  得到选中文字的起始位置和长度  通过定义成员变量的方式保存起来  代码如下

[objc] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. - (void)textViewDidChangeSelection:(UITextView *)textView {  
  2.       
  3.     /** 
  4.      * 可以通过得到复制的长度进行判断是否有进行复制操作  再通过位置、长度进行字体变化 
  5.      */  
  6.     _loc = (int)textView.selectedRange.location;  
  7.     _len = (int)textView.selectedRange.length;  
  8.       
  9. }  

富文本   让选中的字体加粗或者改变颜色都可以  代码中是点击按钮触发字体选中改变方法

[objc] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. - (void)btnClick{  
  2.       
  3.     if (_len) {    
  4.           
  5.         //怎么让选中的字体加粗呢  
  6.         NSMutableAttributedString *AttributedStr = [[NSMutableAttributedString alloc]initWithString:_textView.text];  
  7.         [AttributedStr addAttribute:NSFontAttributeName  
  8.            
  9.                               value:[UIFont boldSystemFontOfSize:15.0]  
  10.            
  11.                               range:NSMakeRange(_loc, _len)];  
  12.           
  13.         _textView.attributedText = AttributedStr;  
  14.           
  15.           
  16.           
  17.     }  
  18.       
  19. }  

图片插入  代码中也是通过按钮触发方法  点击按钮 复制一张图片到光标位置

[objc] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. - (void)copyBtnClick{  
  2.       
  3.     NSMutableAttributedString *string = [[NSMutableAttributedString alloc] initWithAttributedString:_textView.attributedText];  
  4.       
  5.     NSTextAttachment *textAttachment = [[NSTextAttachment alloc] initWithData:nil ofType:nil] ;  
  6.     textAttachment.image = [UIImage imageNamed:@"111"]; //要添加的图片  
  7.       
  8.     NSAttributedString *textAttachmentString = [NSAttributedString attributedStringWithAttachment:textAttachment] ;  
  9.       
  10.     [string insertAttributedString:textAttachmentString atIndex:_textView.selectedRange.location];//index为用户指定要插入图片的位置  
  11.     _textView.attributedText = string;  
  12.   
  13. }  

————————————————————————————————————————

问题四

继承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(nonatomic) NSRange 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 = 0  
  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 = 5  
  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. }  



Demo已经上传,有兴趣的可以下载看看:点此进入下载页


0 0
原创粉丝点击