问题一
开发过程中,经常会遇到动态计算行高的问题,
- (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的功能。
-
- NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
- paragraphStyle.lineSpacing = 10;
- paragraphStyle.firstLineHeadIndent = 20.0f;
- paragraphStyle.alignment = NSTextAlignmentJustified;
- paragraphStyle.lineBreakMode = NSLineBreakByTruncatingTail;
- paragraphStyle.headIndent = 20;
- paragraphStyle.tailIndent = 20;
- paragraphStyle.minimumLineHeight = 10;
- paragraphStyle.maximumLineHeight = 20;
- paragraphStyle.paragraphSpacing = 15;
- paragraphStyle.paragraphSpacingBefore = 22.0f;
- paragraphStyle.baseWritingDirection = NSWritingDirectionLeftToRight;
- paragraphStyle.lineHeightMultiple = 15;
- paragraphStyle.hyphenationFactor = 1;
好了,现在就可以很轻松的计算某一段落高度,例如:
- _descAtt = [[NSMutableAttributedString alloc] initWithString:_model.desc];
- UIFont *descFont = [UIFont PingFangSC_Regular_WithSize:12];
-
- NSMutableParagraphStyle *descStyle = [[NSMutableParagraphStyle alloc]init];
- [descStyle setLineSpacing:1];
-
- CGSize descSize = [_model.desc boundingRectWithSize:CGSizeMake(w, MAXFLOAT)
- options:NSStringDrawingUsesLineFragmentOrigin
- attributes:@{NSFontAttributeName:descFont,
- NSParagraphStyleAttributeName :descStyle}
- context:nil].size;
另外,再介绍几个富文本处理的属性:
——————————————————————————————————————————————————————————————————
问题二
一、设置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的placeholderUITextView上如何加上类似于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 是定义的成员变量
- _textView = [[UITextView alloc]init];
- _textView.font = [UIFont systemFontOfSize:13];
- _textView.backgroundColor = [UIColor lightGrayColor];
- _textView.text = [NSString stringWithFormat:@"settttttttttt :%@",self.countStr];
- _textView.frame = CGRectMake(20, 100, 200, 130);
- _textView.delegate = self;
- [self.view addSubview:_textView];
通过代理方法 得到选中文字的起始位置和长度 通过定义成员变量的方式保存起来 代码如下 - - (void)textViewDidChangeSelection:(UITextView *)textView {
-
-
-
-
- _loc = (int)textView.selectedRange.location;
- _len = (int)textView.selectedRange.length;
-
- }
富文本 让选中的字体加粗或者改变颜色都可以 代码中是点击按钮触发字体选中改变方法 - - (void)btnClick{
-
- if (_len) {
-
-
- NSMutableAttributedString *AttributedStr = [[NSMutableAttributedString alloc]initWithString:_textView.text];
- [AttributedStr addAttribute:NSFontAttributeName
-
- value:[UIFont boldSystemFontOfSize:15.0]
-
- range:NSMakeRange(_loc, _len)];
-
- _textView.attributedText = AttributedStr;
-
-
-
- }
-
- }
图片插入 代码中也是通过按钮触发方法 点击按钮 复制一张图片到光标位置 - - (void)copyBtnClick{
-
- NSMutableAttributedString *string = [[NSMutableAttributedString alloc] initWithAttributedString:_textView.attributedText];
-
- NSTextAttachment *textAttachment = [[NSTextAttachment alloc] initWithData:nil ofType:nil] ;
- textAttachment.image = [UIImage imageNamed:@"111"];
-
- NSAttributedString *textAttachmentString = [NSAttributedString attributedStringWithAttachment:textAttachment] ;
-
- [string insertAttributedString:textAttachmentString atIndex:_textView.selectedRange.location];
- _textView.attributedText = string;
-
- }
————————————————————————————————————————
问题四
继承UITextView
1.定制选中文字的菜单
首先新建一个类,继承自UITextView,假设类名为MyTextView,关键代码如下:
-
- - (BOOL)canBecameFirstResponder {
- return YES;
- }
-
-
- - (BOOL)canPerformAction:(SEL)action withSender:(id)sender {
- if (action == @selector(copy:)) {
- return NO;
- }
- else if (action == @selector(selectAll:)) {
- return NO;
- }
-
-
- return NO;
- }
以上第一个方法用来确保我们选中文字后的菜单可以弹出,第二个方法用来关闭菜单中所有系统的菜单项,如copy, select, select all等。
然后使用UIMenuController定制菜单:
-
- UIMenuItem *selectItem = [[UIMenuItem alloc] initWithTitle:@"选择文字" action:@selector(callSelectText:)];
- UIMenuItem *cancelItem = [[UIMenuItem alloc] initWithTitle:@"取消选中" action:@selector(cancelSelection:)];
- [UIMenuController sharedMenuController].menuItems = @[selectItem, cancelItem];
注意必须实现两个MenuItem的响应方法才能显示出菜单:
- #pragma mark - Menu Item Actions
-
- - (void)callSelectText:(id)sender {
- self.currentSelection_ = self.myTextView.selectedRange;
- self.selectOptionView.hidden = NO;
- [self.location_inputTextField becomeFirstResponder];
- }
-
- - (void)cancelSelection:(id)sender {
- self.myTextView.selectedRange = NSRangeZero;
- }
最终效果如下:
之前的项目没有要求定制菜单项的图像,直接看SDK的内容的话貌似也没有Image之类的属性或方法,所以深层次定制菜单项的内容不得而知了。
2.通过代码选中一段文字
这个很简单,直接改变UITextView的selectedRange属性的值就可以了:
- @property(nonatomic) NSRange selectedRange;
例如我们点击选择文字后弹出一个文字选择的输入视图,这个我用一个XIB文件定制:
小心了,将xib中的UI组件和View Controller中的Outlet连接时,在代码中要先从xib文件中加载视图,才能使用其中的UI组件,例如:
- NSArray *nibViews = [[NSBundle mainBundle] loadNibNamed:@"SelectOptionView" owner:self options:nil];
- self.selectOptionView = nibViews[0];
- self.selectOptionView.center = CGPointMake(self.view.center.x, self.view.bounds.size.height / 3);
- self.selectOptionView.hidden = YES;
- [self.view addSubview:self.selectOptionView];
-
-
- self.location_inputTextField.delegate = self;
- self.length_inputTextField.delegate = self;
如果将
self.location_inputTextField.delegate =self;
self.length_inputTextField.delegate =self;
这两行代码置于loadNibNamed方法之前,那么两个文本输入框的delegate将为空(因为他们本身都是空,还没有加载)。
选择文字的Action代码为:
- #pragma mark - Select View Actions
-
- - (IBAction)selectText:(id)sender {
- NSInteger loc = self.location_inputTextField.text.integerValue;
- NSInteger len = self.length_inputTextField.text.integerValue;
- NSUInteger textLength = self.myTextView.text.length;
- if (loc < 0 || len < 0 || loc > textLength || len > textLength) {
- UIAlertView *alerView = [[UIAlertView alloc] initWithTitle:@"错误"
- message:@"输入出错,输入的数不能小于0和大于文本长度"
- delegate:nil
- cancelButtonTitle:@"确定" otherButtonTitles:nil, nil nil];
- [alerView show];
- return;
- }
- self.currentSelection_ = NSMakeRange(loc, len);
- [self finishSelectingText];
- }
-
- - (IBAction)cancelSelectText:(id)sender {
- [self finishSelectingText];
- }
-
- - (void)finishSelectingText {
- [self.location_inputTextField resignFirstResponder];
- [self.length_inputTextField resignFirstResponder];
- self.selectOptionView.hidden = YES;
-
- [self.myTextView becomeFirstResponder];
- self.myTextView.selectedRange = self.currentSelection_;
- }
没错,只要一句self.myTextView.selectedRange =self.currentSelection_;就可以了。
另外,我们可以在UITextView的以下方法中监听到某段文字被选中:
- #pragma mark - UITextView Delegate
-
- - (void)textViewDidChangeSelection:(UITextView *)textView {
- NSLog(@"Selection changed");
-
- NSLog(@"loc = %d", self.myTextView.selectedRange.location);
- NSLog(@"len = %d", self.myTextView.selectedRange.length);
- }
运行结果:
控制台输出如下:
- 2014-02-16 23:33:56.197 MyTextView[4890:70b] Selection changed
- 2014-02-16 23:33:56.198 MyTextView[4890:70b] loc = 507
- 2014-02-16 23:33:56.198 MyTextView[4890:70b] len = 0
- 2014-02-16 23:33:56.334 MyTextView[4890:70b] Selection changed
- 2014-02-16 23:33:56.335 MyTextView[4890:70b] loc = 507
- 2014-02-16 23:33:56.335 MyTextView[4890:70b] len = 5
- 2014-02-16 23:34:05.291 MyTextView[4890:70b] Selection changed
- 2014-02-16 23:34:05.292 MyTextView[4890:70b] loc = 10
- 2014-02-16 23:34:05.292 MyTextView[4890:70b] len = 100
3.让键盘主动出现
为了让用户更省心,我们可以在一个带输入框的视图出现时就让键盘弹出来,而不用用户再点一下输入框了。方法很简单,就一行代码:
- [self.location_inputTextField becomeFirstResponder];
4.两个输入框按return时仿回车功能
有多个输入框,在一个输入框中按了return,然后好像在网站输入框中按了回车,直接跳到下一个输入框,这个也非常简单,就是resignFirstResponder和becomeFirstResponder方法结合使用而已,在UITextField的委托方法中实现:
- #pragma mark - UITextField Delegate
-
- - (BOOL)textFieldShouldReturn:(UITextField *)textField {
- if ([self.location_inputTextField isFirstResponder]) {
- [self.location_inputTextField resignFirstResponder];
- [self.length_inputTextField becomeFirstResponder];
- }
- else if ([self.length_inputTextField isFirstResponder]) {
- [self.length_inputTextField resignFirstResponder];
- }
- return YES;
- }
Demo已经上传,有兴趣的可以下载看看:点此进入下载页