本篇文章将分为两部分,一部分是静态文本分页,一部分是动态文本分页即边填写文本边进行文本的分页.bash
咱们所采用的方案为:TextKit
进行处理,经过glyphRangeForTextContainer
方法获取文本内容视图可容纳的文本范围来对文本进行切割分页.app
// 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;
复制代码
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;
复制代码
textView.textContainerInset = UIEdgeInsetsZero;
复制代码
// 容许连续布局
textView.layoutManager.allowsNonContiguousLayout = NO;
复制代码
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;
复制代码
经过glyphRangeForTextContainer
获取可容纳文本范围,再截取出文本,便可得到视图可展现的内容.less
// 获取文本视图可容纳文本范围
NSRange textRange = [textView.layoutManager glyphRangeForTextContainer:textView.textContainer];
NSString *textViewText = [text substringWithRange:textRange];
textView.text = textViewText;
复制代码
获取文本数据,对文本进行一段一段截取以达到分页.布局
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;
}
复制代码
效果展现ui
这里咱们要实现的内容是:在文本框中填写内容,内容跟随文本的增多进行动态的分页,这里大部份内容实际上是跟静态文本分页是一致,不太同样的是多个文本框是均可以编辑的,也就是上一个文本框会影响到下一个文本框的内容展现.以及存在着编写拼音的特殊处理时对于markText
文本的处理.spa
咱们会有一个可填写的文本框,咱们填写文本框,将多余的文本进行添加新的文本框展现处理. 3d
咱们在textViewDidChange
的代理方法里进行一下操做代理
CGFloat realHeight = [textView sizeThatFits:CGSizeMake(CGRectGetWidth(textView.bounds), MAXFLOAT)].height;
// 判断是否须要分页
if (realHeight <= textViewSize.height) {
return;
}
// 进行分页处理
......
......
复制代码
markText
文本的处理.这边咱们能够看到,当文本框正在拼音时存在markText
,这个时候咱们须要对这个状况特殊处理.code
咱们临时对textContainer
的高度变高来容纳markText
文本,以后再调回原有高度.cdn
// 获取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;
复制代码
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];
}
复制代码
总的来讲咱们对于文本分页的步骤:
TextKit
提供的方法glyphRangeForTextContainer
得到文本框所能容纳的范围位置