10

I want to load long text in TextViews of different Views. The text should be divided to pages when it reaches end of the textviews. And the next textview must start with the continuation of the text.

I have gone through a lot of answers. But all specifies about limiting text content when entering data. I want to display paged data in TextView. And there wont be any entering or editing in it. So the delegate methods regarding that won't work.

I tried to load text of fixed length. but it is not correct when the no.of paragraphs varies.

So what i am trying to find is , get notified when the the text reaches end of textview's capacity or getting the capacity of textview as per no lines/ no.of characters. Is there any way to do this ???

Update

As per the comments i have got, i searched a lot and reached at NSTextStorage, NSTextLayoutManager and NSTextContainer

I have found this link which will help to easily implement pagination

http://sketchytech.blogspot.co.uk/2013/11/paging-and-paginating-easy-way-with.html

I this example they have created 4 objects using loop. It can be said as they have splitted the string to 4 parts and displayed in 4 uitextviews of a scrollview.

I am trying to set a condition to split to textcontainers according to the length of string. I trying to create a condition by considering the total length of main string & the text displayed in textviews.

enter image description here

But each textview' text length is same as the total length of the main string. So how i could get the length text displayed in each textcontainer of textview ????

Karan Alangat
  • 2,154
  • 4
  • 25
  • 56
  • 1
    I think your best bet would not be going by the `length` of the text but rather by the `height`. You can get this characteristic from `[NSString sizeWithAttributes:(NSDictionary*)attributes]` – Cameron Askew Feb 08 '14 at 10:15
  • Based on your problem, you're probably gonna have to use `sizeWithAttributes` along with some custom "chunkenizing" tool, pbb binary search for where to crop the string. – Cameron Askew Feb 08 '14 at 10:17
  • 1
    I am not sure about this, but shouldn't the textView's contentSize be a valid value to consider when evaluating when to move to next textView? I guess the `paste` action might cause problems with that. – n00bProgrammer Feb 08 '14 at 10:20
  • Right, if the textView can only fit 50 chars (simple case no new line characters etc) and you paste 100 chars, you need to find where to crop..unless there's some way to grab paste action in a separate method as normal editing action – Cameron Askew Feb 08 '14 at 10:21
  • 1
    Building on that, what if 300 chars are pasted? what if they delete from textview 1, does text from textview 2 move back into textview 1? Please expand on the details of your problem – Cameron Askew Feb 08 '14 at 10:25
  • Thanks for the fast reply . . . .@Cameron Askew, @n00bProgrammer – Karan Alangat Feb 08 '14 at 10:39
  • @CameronAskew i am searching on [NSString sizeWithAttributes:(NSDictionary*)attributes]. . – Karan Alangat Feb 08 '14 at 11:16
  • @n00bProgrammer ContentSize property gives only the size of the textview, not the no.of characters it can hold. am i correct ?? so how will it be helpful ? – Karan Alangat Feb 08 '14 at 11:26
  • What I would do is use a single `NSString` that holds ALL the text, and break it according to the limit of your textView, and set the broken string as text in the respective textViews. Doing it in the `textViewDidChange` method makes sense, but it simply means you perform operation (or at least check if the conditions are satisfied) each time the text is changed. – n00bProgrammer Feb 08 '14 at 16:56
  • combinedString = [NSString stringWithFormat:@"%@%@%@", textViewA.text, textViewB.text, textViewC.text]. Then use substring from range to break it into strings for each textView – n00bProgrammer Feb 08 '14 at 16:57
  • @n00bProgrammer but how to set the limit how can i get the limit of a textview ??? i am getting size of uitextview as height & width , not as no.of characters or no.of lines it can hold. so how can i set the limit ??? when i set the limit according to no.of characters, it does not fit to textview always when the no.of paragraphs varies. – Karan Alangat Feb 11 '14 at 05:44

7 Answers7

3

You can create a CTFramesetter and call CTFramesetterSuggestFrameSizeWithConstraints. You will then find the range of text that fits within a given CGSize. Then manipulate the Text in accordance to the range and you are done.

Reference - CoreText.

Ravi_Parmar
  • 12,319
  • 3
  • 25
  • 36
  • Thanks for your reply. . . But it is somewhat more complex. I required some simple methods that i got from another answer . .. . – Karan Alangat Feb 21 '14 at 07:14
2

I had to do measurements of text in a UITextView a while ago for overlaying views on top of some words in a UITextView on iOS 5. With the UITextView changes for iOS 7 I no longer have to do this. The way I did measurement may be what you need to break up your text. I don't have a good working code to give so I will have to describe the algorithm as best I can remember it.

This algorithm does not work when there are variable line heights.

  1. I found by trial error that the text width I needed to match against was the width of the text view minus 16.
  2. Do a binary search on substrings of the text to find the maximum substring that fits in the UITexView where the substring breaks at word boundaries. You can use NSLinguistic tagger to get word boundaries.
  3. On each of the substring call [substr sizeWithFont:font constrainedToSize:sizeConstraint lineBreakMode:NSLineBreakByWordWrapping] where the size constraints in the width from step 1 and a very large height.
  4. You will need to continue the search until you can identify the word boundary where the size with constraints for word1 gives you a height that fits in your view and the immediately next word gives you a height that does not fit in your view. You will know that this is where the UITextView will do word wrapping that would be too big to fit on one of your pages.
  5. Break your text on the word boundary from step 4 and do the same for the next page.
Stephen Johnson
  • 5,293
  • 1
  • 23
  • 37
1

Here are two element:

A good explanation/sample for multi column with ios 7 text kit:

https://github.com/ShinobiControls/iOS7-day-by-day/blob/master/21-multi-column-textkit/21-multi-column-textkit.md

Based on that sample and the following question:

How do I locate the CGRect for a substring of text in a UILabel?

You can try something like that:

- (void)layoutTextContainers
{
    NSUInteger lastRenderedGlyph = 0;
    CGFloat currentXOffset = 0;
    while (lastRenderedGlyph < _layoutManager.numberOfGlyphs) {
        CGRect textViewFrame = CGRectMake(currentXOffset, 10,
                                          CGRectGetWidth(self.view.bounds) / 2,
                                          CGRectGetHeight(self.view.bounds) - 20);
        CGSize columnSize = CGSizeMake(CGRectGetWidth(textViewFrame) - 20,
                                       CGRectGetHeight(textViewFrame) - 10);

        NSTextContainer *textContainer = [[NSTextContainer alloc] initWithSize:columnSize];
        [_layoutManager addTextContainer:textContainer];

        // And a text view to render it
        UITextView *textView = [[UITextView alloc] initWithFrame:textViewFrame
                                                   textContainer:textContainer];
        textView.scrollEnabled = NO;
        [self.scrollView addSubview:textView];



        // Increase the current offset
        currentXOffset += CGRectGetWidth(textViewFrame);

        // And find the index of the glyph we've just rendered
        lastRenderedGlyph = NSMaxRange([_layoutManager glyphRangeForTextContainer:textContainer]);

        NSLog(@"Last rendered glyph %i",lastRenderedGlyph);

        NSLog(@"Textview length %i",textView.text.length);

        NSRange range = {lastRenderedGlyph-1, lastRenderedGlyph};

        NSRange glyphRange;

        // Convert the range for glyphs.
        [_layoutManager characterRangeForGlyphRange:range actualGlyphRange:&glyphRange];

        NSLog(@"Glyph rect %@",NSStringFromCGRect([_layoutManager boundingRectForGlyphRange:glyphRange inTextContainer:textContainer]));
    }

    // Need to update the scrollView size
    CGSize contentSize = CGSizeMake(currentXOffset, CGRectGetHeight(self.scrollView.bounds));
    self.scrollView.contentSize = contentSize;
}

output is:

 Newspaper[89217:a0b] Last rendered glyph 711
 Newspaper[89217:a0b] Textview length 2585
 Newspaper[89217:a0b] Glyph rect {{121.556, 515.3761}, {13.444, 14.315979}}
 Newspaper[89217:a0b] Last rendered glyph 1441
 Newspaper[89217:a0b] Textview length 2585
 Newspaper[89217:a0b] Glyph rect {{129.15199, 515.3761}, {5.8480072, 14.315979}}
 Newspaper[89217:a0b] Last rendered glyph 2155
 Newspaper[89217:a0b] Textview length 2585
 Newspaper[89217:a0b] Glyph rect {{111.80001, 515.3761}, {23.199989, 14.315979}}
 Newspaper[89217:a0b] Last rendered glyph 2585
 Newspaper[89217:a0b] Textview length 2585
 Newspaper[89217:a0b] Glyph rect {{92.720001, 329.26801}, {3.552002, 14.31601}}

Glyph Rect doesn't looks good (i'm interested to see it working correctly) but you get the latest glyph displayed.

Hope it helps!

Community
  • 1
  • 1
Alban
  • 1,624
  • 11
  • 21
0

How about assigning the total text to all of the text views. Then when ever we are moving from one text to another text view we just use the below function of the text view.

- (void)scrollRangeToVisible:(NSRange)range;

You would be deciding what that range should be initially, and then keep on altering it for every iteration until you reach end of string.

Rajesh
  • 850
  • 9
  • 18
  • 1
    hi,so how i can set the range. i want textview full of text . when i set the range manually, it varies when the no.of paragraphs varies. Do u have any idea about getting the length of visible text in textview ?? SO i can set range by measuring that . . . . – Karan Alangat Feb 14 '14 at 05:59
  • @KaranAlangat I would say trial and error should give you that optimum range. Because that is easy and quick until you get a good answer. One drawback in my approach is if at all there is paragraph ending or starting within that range , there may be some empty place left on top or bottom. Only you would be the best judge of how it looks once you apply it. – Rajesh Feb 14 '14 at 06:39
0

I did something like that using the library DTCoreText.

DTCoreText includes layout helpers with them was easy to cut the text in columns and create the different textviews. I created an example on github showing how can be done:

https://github.com/brovador/DTCoreTextColumnsExample

Hope it helps

brovador
  • 31
  • 3
0

Option 2 does work in iOS with the proper parameters.

NSAttributedString *attrStr = ... // your attributed string
CGFloat width = 300; // whatever your desired width is
CGRect rect = [attrStr boundingRectWithSize:CGSizeMake(width, 10000) options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading context:nil];

Without the proper values for the options parameter you will get the wrong height.

got this answer from here

Community
  • 1
  • 1
  • 1
    It does work !!!! I got the CGRect required for the nsattributedstring i had. Then using that i created condition by comparing it with my textview size . . . . Thanks Kuriakose & rmaddy – Karan Alangat Feb 21 '14 at 07:12
  • 1
    @KaranAlangat can u tell me which condition have u used ? or give me sone idea. Thanx – Kalpesh Apr 21 '14 at 06:42
  • What do u really need. I am sorry to say this method returns false values some times & Apple has explained about it already. – Karan Alangat Apr 21 '14 at 07:20
-1

Use following method :

 NSString *textEntered = [[[tvQuestion.text copy] autorelease] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];

        //Added for 450 character restriction 
        if([textEntered length] > 450) {
            UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Message" message:kMoreThan450CharactersForQuestion delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];
            [alertView performSelectorOnMainThread:@selector(show) withObject:nil waitUntilDone:YES];
            [alertView release];
        }
Divya Bhaloidiya
  • 5,018
  • 2
  • 25
  • 45