前言

本篇文章将分为两部分,一部分是静态文本分页,一部分是动态文本分页即边填写文本边进行文本的分页.

我们所采用的方案为:TextKit进行处理,通过glyphRangeForTextContainer方法获取文本内容视图可容纳的文本范围来对文本进行切割分页.

// Returns the range of characters which have been laid into the given container.  This is a less efficient method than the similar -textContainerForGlyphAtIndex:effectiveRange:.
- (NSRange)glyphRangeForTextContainer:(NSTextContainer *)container;

静态文本分页

1.文本视图配置

1.1 设置textContainer

  • 设置textContainer的尺寸为视图尺寸
  • 设置lineFragmentPadding为0,让文本两边距离视图为0,计算更为准确
 UITextView *textView = [[UITextView alloc] initWithFrame:CGRectMake(0, originY, kTextViewSize.width, kTextViewSize.height)];
 // textContainer的最大高度,实际生成的视图高度将比此值小
 textView.textContainer.size = CGSizeMake(CGRectGetWidth(textView.bounds), CGRectGetHeight(textView.bounds));
 // 设置文本内容的左右间距为0
 textView.textContainer.lineFragmentPadding = 0.f;

1.2 文本视图基础设置

设置文本上下边间距为0,让文本能够撑满视图

 textView.textContainerInset = UIEdgeInsetsZero;

设置文本视图连续布局

 // 允许连续布局
 textView.layoutManager.allowsNonContiguousLayout = NO;

1.3 文本视图完整配置

 UITextView *textView = [[UITextView alloc] initWithFrame:CGRectMake(0, originY, kTextViewSize.width, kTextViewSize.height)];
 textView.backgroundColor = [UIColor yellowColor];
 textView.textColor = [UIColor blackColor];
 // textContainer的最大高度,实际生成的视图高度将比此值小
 textView.textContainer.size = CGSizeMake(CGRectGetWidth(textView.bounds), CGRectGetHeight(textView.bounds));
 // 需将文本内容填充区域置0处理,计算更准确
 textView.textContainerInset = UIEdgeInsetsZero;
 // 设置文本内容的左右间距为0
 textView.textContainer.lineFragmentPadding = 0.f;
 textView.text = text;
 textView.font = [UIFont systemFontOfSize:16];
 // 允许连续布局
 textView.layoutManager.allowsNonContiguousLayout = NO;
 textView.userInteractionEnabled = NO;
 textView.contentSize = textView.bounds.size;

2.文本视图数据配置

通过glyphRangeForTextContainer获取可容纳文本范围,再截取出文本,即可获得视图可展示的内容.

 // 获取文本视图可容纳文本范围
 NSRange textRange = [textView.layoutManager glyphRangeForTextContainer:textView.textContainer];
 NSString *textViewText = [text substringWithRange:textRange];
 textView.text = textViewText;

3.关键代码展示

获取文本数据,对文本进行一段一段截取以达到分页.

 NSString *text = @"有一次,在我参加的一个晚会上,主持人问一个小男孩:你长大以后要做什么样的人?孩子看看我们这些企业家,然后说:做企业家。在场的人忽地笑着鼓起了掌。我也拍了拍手,但听着并不舒服。我想,这孩子对于企业究竟知道多少呢?他是不是因为当着我们的面才说要当企业家的呢?他是不是受了大人的影响,以为企业家风光,都是有钱的人,才要当企业家的呢\n这一切当然都是一个谜。但不管怎样,作为一个人的人生志向,我以为当什么并不重要;不管是谁,最重要的是从小要立志做一个努力的人\n我小的时候也曾有人问过同样的问题,我的回答不外乎当教师、解放军和科学家之类。时光一晃流走了二十多年,当年的孩子,如今已是四十出头的大人。但仔细想一想,当年我在大人们跟前表白过的志向,实际一个也没有实现。我身边的其他人差不多也是如此。有的想当教师,后来却成了个体户;想当解放军的,有人竟做了囚犯。我上大学时有两个同窗好友,他们现在都是我国电子行业里才华出众的人,一个成长为“康佳”集团的老总,一个领导着TCL集团。我们三个不期而然地成为中国彩电骨干企业的经营者,可是当年大学毕业时,无论有多大的想像力,我们也不敢想十几年后会成现在的样子。一切都是我们在奋斗中见机行事,一步一步努力得来的。与其说我们是有理想的人,不如说我们是一直在努力的人。\n并非我们不重视理想,而是因为树雄心壮志易,为理想努力难,人生自古就如此。有谁会想到,十多年前的今天,我曾是一个在街头彷徨,为生存犯愁的人?当时的我,一无所有,前途渺茫,真不知路在何处。然而,我却没有灰心失望,回想起来,支撑着我走过这段坎坷岁月的正是我的意志品格。当许多人以为我已不行、该不行了的时候,我仍做着从地上爬起来的努力,我坚信人生就像马拉多纳踢球,往往是在快要倒下去的时候“进球”获得生机的。事实也正是如此,就在“山重水复疑无路”的时候,香港一家企业倒闭给了我东山再起的机会,使我能够与掌握世界最新技术的英国科技人员合作,开发技术先进的彩色电视机,从此一举走出困境。\n有人说,“努力”与“拥有”是人生一左一右的两道风景。但我以为,人生最美最不能逊色的风景应该是努力。努力是人生的一种精神状态,是对生命的一种赤子之情。努力是拥有之母,拥有是努力之子。一心努力可谓条条大路通罗马,只想获取可谓道路逼仄,天地窄小。所以,与其规定自己一定要成为一个什么样的人物,获得什么东西,不如磨练自己做一个努力的人。志向再高,没有努力,志向终难坚守;没有远大目标,因为努力,终会找到奋斗的方向。做一个努力的人,可以说是人生最切实际的目标,是人生最大的境界。\n许多人因为给自己定的目标太高太功利,因为难以成功而变得灰头土脸,最终灰心失望。究其原因,往往就是因为太关注拥有,而忽略做一个努力的人。对于今天的孩子们,如果只关注他们将来该做个什么样的人物,不把意志品质作为一个做人的目标提出来,最终我们只能培养出狭隘、自私、脆弱和境界不高的人。遗憾的是,我们在这方面做得并不尽如人意。";
 while (text.length > 0) {
  // 添加文本视图展示,并获得剩余文本
  text = [self addTextViewWithText:text originY:originY];
 }
- (NSString *)addTextViewWithText:(NSString *)text originY:(CGFloat)originY {
 UITextView *textView = [[UITextView alloc] initWithFrame:CGRectMake(0, originY, kTextViewSize.width, kTextViewSize.height)];
 ......
 
 ......
 ......
 // 获取文本视图可容纳文本范围
 NSRange textRange = [textView.layoutManager glyphRangeForTextContainer:textView.textContainer];
 NSString *textViewText = [text substringWithRange:textRange];
 textView.text = textViewText;
 [self.scView addSubview:textView];
 
 // 获取容纳不了的剩余文本
 NSString *remainText = [text substringFromIndex:NSMaxRange(textRange)];
 return remainText;
}

效果展示

动态文本分页

这里我们要实现的内容是:在文本框中填写内容,内容跟随文本的增多进行动态的分页,这里大部分内容其实是跟静态文本分页是一致,不太一样的是多个文本框是都可以编辑的,也就是上一个文本框会影响到下一个文本框的内容展示.以及存在着编写拼音的特殊处理时对于markText文本的处理.

1. 初始状态

我们会有一个可填写的文本框,我们填写文本框,将多余的文本进行添加新的文本框展示处理.

2. 完成状态

3. 关键代码展示

我们在textViewDidChange的代理方法里进行一下操作

3.1 获得文本实际高度来判断是否分页

 CGFloat realHeight = [textView sizeThatFits:CGSizeMake(CGRectGetWidth(textView.bounds), MAXFLOAT)].height;
 
 // 判断是否需要分页
 if (realHeight <= textViewSize.height) {
  return;
 }
 
 // 进行分页处理
 ......
 ......

3.2 存在着编写拼音的特殊处理时对于markText文本的处理.

这边我们可以看到,当文本框正在拼音时存在markText,这个时候我们需要对这个情况特殊处理.

我们临时对textContainer的高度变高来容纳markText文本,之后再调回原有高度.

  // 获取mark文本以及相关位置大小
  NSString *markText = [textView textInRange:textView.markedTextRange];
  NSInteger location = [textView offsetFromPosition:textView.beginningOfDocument toPosition:textView.markedTextRange.start];
  NSRange markTextRange = NSMakeRange(location, markText.length);
  NSString *primaryLang = [[textView textInputMode] primaryLanguage];
  
  BOOL isZHHans = [primaryLang isEqualToString:@"zh-Hans"];
  
    // 判断是否是在拼音
  if (isZHHans && markTextRange.length != 0) {
    // 临时调高container高度
    textView.textContainer.size = CGSizeMake(textViewSize.width, realHeight);
    BOOL isContainENCharacter = NO;
    for (int i = 0; i < markText.length;   i) {
      unichar character = [markText characterAtIndex:i];
      NSString *string = [NSString stringWithCharacters:&character length:1];
      if ([string isLetter]) {
        isContainENCharacter = YES;
        break;
      }
    }
    
    if (isContainENCharacter) {
      return;
    }
  }
  
  // 调回原有尺寸
  textView.textContainer.size = textViewSize;

3.3 对文本分页

NSRange range = [textView.layoutManager glyphRangeForTextContainer:textView.textContainer];
textView.text = [textViewText substringWithRange:range];

[self handleBelowTextViewWithAboveTextView:textView totalText:[textViewText substringFromIndex:textView.text.length]];

这里我们无法确定文本是否只影响下一文本框,所以我们这边会递归执行该方法到最后文本不再多余时结束递归.

- (void)handleBelowTextViewWithAboveTextView:(UITextView *)textView totalText:(NSString *)textViewText {
  NSInteger sectionIndex = textView.tag - kMarkTag;
  // 判断是否已存在下一视图
  UITextView *belowTextView = [self.scView viewWithTag:kMarkTag   sectionIndex   1];
  if (belowTextView) {
    // 原有的文本添加到后面
    NSString *oriText = belowTextView.text;
    NSMutableString *mString = [[NSMutableString alloc] initWithString:textViewText];
    [mString appendString:oriText];
    belowTextView.text = mString.copy;
  } else {
    belowTextView = [self contentTextViewWithIndex:  sectionIndex];
    belowTextView.text = textViewText;
  }
  
  [self.scView addSubview:belowTextView];
  self.scView.contentSize = CGSizeMake(self.scView.bounds.size.width, CGRectGetMaxY(belowTextView.frame));
  
  CGFloat realBelowHeight = [belowTextView sizeThatFits:CGSizeMake(CGRectGetWidth(belowTextView.bounds), MAXFLOAT)].height;
  if (realBelowHeight <= belowTextView.bounds.size.height) {
    [belowTextView becomeFirstResponder];
    return;
  }
  
  belowTextView.textContainer.size = belowTextView.bounds.size;
  NSRange range = [belowTextView.layoutManager glyphRangeForTextContainer:belowTextView.textContainer];
  NSString *currentTmpBelowText = belowTextView.text;
  belowTextView.text = [currentTmpBelowText substringWithRange:range];
  NSString *remainText = [currentTmpBelowText substringFromIndex:belowTextView.text.length];
  
  // 再次执行方法,直到没有多余文本
  [self handleBelowTextViewWithAboveTextView:belowTextView totalText:remainText];
}

总结

总的来说我们对于文本分页的步骤:

  1. 判断文本的高度是否高于当前文本框的高度,如果不高于则不需要分页
  2. 通过TextKit提供的方法glyphRangeForTextContainer获得文本框所能容纳的范围位置
  3. 对文本进行截取,对剩余文本进行再次执行第2步的操作,直到不能再分页为止

好了,以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对Devmax的支持。

iOS实现文本分页的方法示例的更多相关文章

  1. HTML5在微信内置浏览器下右上角菜单的调整字体导致页面显示错乱的问题

    HTML5在微信内置浏览器下,在右上角菜单的调整字体导致页面显示错乱的问题,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧

  2. iOS实现拖拽View跟随手指浮动效果

    这篇文章主要为大家详细介绍了iOS实现拖拽View跟随手指浮动,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  3. ios – containerURLForSecurityApplicationGroupIdentifier:在iPhone和Watch模拟器上给出不同的结果

    我使用默认的XCode模板创建了一个WatchKit应用程序.我向iOSTarget,WatchkitAppTarget和WatchkitAppExtensionTarget添加了应用程序组权利.(这是应用程序组名称:group.com.lombax.fiveminutes)然后,我尝试使用iOSApp和WatchKitExtension访问共享文件夹URL:延期:iOS应用:但是,测试NSURL

  4. ios – Testflight无法安装应用程序

    我有几个测试人员注册了testflight并连接了他们的设备……他们有不同的ios型号……但是所有这些都有同样的问题.当他们从“safari”或“testflight”应用程序本身单击应用程序的安装按钮时……达到约90%并出现错误消息…

  5. ibm-mobilefirst – 在iOS 7.1上获取“无法安装应用程序,因为证书无效”错误

    当我的客户端将他们的设备更新到iOS7.1,然后尝试从AppCenter更新我们的应用程序时,我收到了上述错误.经过一番搜索,我找到了一个类似问题的帖子here.但是后来因为我在客户端使用AppCenter更新应用程序的环境中,我无法使用USB插件并为他们安装应用程序.在发布支持之前,是否有通过AppCenter进行下载的解决方法?

  6. ios – 视图的简单拖放?

    我正在学习iOS,但我找不到如何向UIView添加拖放行为.我试过了:它说“UIView没有可见的接口声明选择器addTarget”此外,我尝试添加平移手势识别器,但不确定这是否是我需要的它被称为,但不知道如何获得事件的坐标.在iOS中注册移动事件回调/拖放操作的标准简单方法是什么?

  7. ios – 什么控制iTunes中iPhone应用程序支持的语言列表?

    什么控制iPhone应用程序的iTunes页面中支持的语言?

  8. ios – 获得APNs响应BadDeviceToken或Unregistered的可能原因是什么?

    我知道设备令牌在某些时候是有效的.用户如何使其设备令牌变坏?从关于“未注册”的文档:Thedevicetokenisinactiveforthespecifiedtopic.这是否意味着应用程序已被删除?.您应该看到四种分发方法:如果您选择AppStore或Enterprise,您将在后面的对话框中看到Xcode将APNS权利更改为生产:如果选择AdHoc或Development,则aps-environment下的文本将是开发,然后应与后端的配置匹配.

  9. ios – 当我关闭应用程序时,我从调试器获得消息:由于信号15而终止

    我怎么能解决这个问题,我不知道这个链接MypreviousproblemaboutCoredata对我的问题有影响吗?当我cmd应用程序的Q时,将出现此消息.Messagefromdebugger:Terminatedduetosignal15如果谁知道我以前的问题的解决方案,请告诉我.解决方法>来自调试器的消息:每当用户通过CMD-Q(退出)或STOP手动终止应用程序(无论是在iOS模拟器中还是

  10. ios – NSUbiquityIdentityDidChangeNotification和SIGKILL

    当应用程序被发送到后台时,我们会删除观察者吗?我遇到的问题是,当UbiquityToken发生变化时,应用程序终止,因为用户已经更改了iCloud设置.你们如何设法订阅这个通知,如果你不这样做,你会做什么来跟踪当前登录的iCloud用户?

随机推荐

  1. iOS实现拖拽View跟随手指浮动效果

    这篇文章主要为大家详细介绍了iOS实现拖拽View跟随手指浮动,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  2. iOS – genstrings:无法连接到输出目录en.lproj

    使用我桌面上的项目文件夹,我启动终端输入:cd然后将我的项目文件夹拖到终端,它给了我路径.然后我将这行代码粘贴到终端中找.-name*.m|xargsgenstrings-oen.lproj我在终端中收到此错误消息:genstrings:无法连接到输出目录en.lproj它多次打印这行,然后说我的项目是一个目录的路径?没有.strings文件.对我做错了什么的想法?

  3. iOS 7 UIButtonBarItem图像没有色调

    如何确保按钮图标采用全局色调?解决方法只是想将其转换为根注释,以便为“回答”复选标记提供更好的上下文,并提供更好的格式.我能想出这个!

  4. ios – 在自定义相机层的AVFoundation中自动对焦和自动曝光

    为AVFoundation定制图层相机创建精确的自动对焦和曝光的最佳方法是什么?

  5. ios – Xcode找不到Alamofire,错误:没有这样的模块’Alamofire’

    我正在尝试按照github(https://github.com/Alamofire/Alamofire#cocoapods)指令将Alamofire包含在我的Swift项目中.我创建了一个新项目,导航到项目目录并运行此命令sudogeminstallcocoapods.然后我面临以下错误:搜索后我设法通过运行此命令安装cocoapodssudogeminstall-n/usr/local/bin

  6. ios – 在没有iPhone6s或更新的情况下测试ARKit

    我在决定下载Xcode9之前.我想玩新的框架–ARKit.我知道要用ARKit运行app我需要一个带有A9芯片或更新版本的设备.不幸的是我有一个较旧的.我的问题是已经下载了新Xcode的人.在我的情况下有可能运行ARKit应用程序吗?那个或其他任何模拟器?任何想法或我将不得不购买新设备?解决方法任何iOS11设备都可以使用ARKit,但是具有高质量AR体验的全球跟踪功能需要使用A9或更高版本处理器的设备.使用iOS11测试版更新您的设备是必要的.

  7. 将iOS应用移植到Android

    我们制作了一个具有2000个目标c类的退出大型iOS应用程序.我想知道有一个最佳实践指南将其移植到Android?此外,由于我们的应用程序大量使用UINavigation和UIView控制器,我想知道在Android上有类似的模型和实现.谢谢到目前为止,guenter解决方法老实说,我认为你正在计划的只是制作难以维护的糟糕代码.我意识到这听起来像很多工作,但从长远来看它会更容易,我只是将应用程序的概念“移植”到android并从头开始编写.

  8. ios – 在Swift中覆盖Objective C类方法

    我是Swift的初学者,我正在尝试在Swift项目中使用JSONModel.我想从JSONModel覆盖方法keyMapper,但我没有找到如何覆盖模型类中的Objective-C类方法.该方法的签名是:我怎样才能做到这一点?解决方法您可以像覆盖实例方法一样执行此操作,但使用class关键字除外:

  9. ios – 在WKWebView中获取链接URL

    我想在WKWebView中获取tapped链接的url.链接采用自定义格式,可触发应用中的某些操作.例如HTTP://我的网站/帮助#深层链接对讲.我这样使用KVO:这在第一次点击链接时效果很好.但是,如果我连续两次点击相同的链接,它将不报告链接点击.是否有解决方法来解决这个问题,以便我可以检测每个点击并获取链接?任何关于这个的指针都会很棒!解决方法像这样更改addobserver在observeValue函数中,您可以获得两个值

  10. ios – 在Swift的UIView中找到UILabel

    我正在尝试在我的UIViewControllers的超级视图中找到我的UILabels.这是我的代码:这是在Objective-C中推荐的方式,但是在Swift中我只得到UIViews和CALayer.我肯定在提供给这个方法的视图中有UILabel.我错过了什么?我的UIViewController中的调用:解决方法使用函数式编程概念可以更轻松地实现这一目标.

返回
顶部