iOS文本分頁實現

本篇文章將分爲兩部分,一部分是靜態文本分頁,一部分是動態文本分頁即邊填寫文本邊進行文本的分頁.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;
複製代碼

靜態文本分頁

1.文本視圖配置

1.1 設置textContainer

  • 設置textContainer的尺寸爲視圖尺寸
  • 設置lineFragmentPadding0,讓文本兩邊距離視圖爲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獲取可容納文本範圍,再截取出文本,便可得到視圖可展現的內容.less

// 獲取文本視圖可容納文本範圍
    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;
}
複製代碼

效果展現ui

動態文本分頁

這裏咱們要實現的內容是:在文本框中填寫內容,內容跟隨文本的增多進行動態的分頁,這裏大部份內容實際上是跟靜態文本分頁是一致,不太同樣的是多個文本框是均可以編輯的,也就是上一個文本框會影響到下一個文本框的內容展現.以及存在着編寫拼音的特殊處理時對於markText文本的處理.spa

1. 初始狀態

咱們會有一個可填寫的文本框,咱們填寫文本框,將多餘的文本進行添加新的文本框展現處理. 3d

2. 完成狀態

3. 關鍵代碼展現

咱們在textViewDidChange的代理方法裏進行一下操做代理

3.1 得到文本實際高度來判斷是否分頁

CGFloat realHeight = [textView sizeThatFits:CGSizeMake(CGRectGetWidth(textView.bounds), MAXFLOAT)].height;
    
    // 判斷是否須要分頁
    if (realHeight <= textViewSize.height) {
        return;
    }
    
    // 進行分頁處理
    ......
    ......
複製代碼

3.2 存在着編寫拼音的特殊處理時對於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;
複製代碼

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步的操做,直到不能再分頁爲止
相關文章
相關標籤/搜索