0
votes

I render a chunk of text in a NSAttributedString, already formatted with attributes that render correctly, into a multicolumn, multipage format using TextKit as follows:

NSUInteger lastRenderedGlyph = 0;
CGFloat currentXOffset = 0;

float width = screenDimensions.size.width;
float height = screenDimensions.size.height;

NSInteger columnCount = 1;
NSInteger pageCount = 1;
NSInteger counter = 1;
self.storage = [[NSTextStorage alloc] initWithAttributedString:self.attrString];
self.manager = [[NSLayoutManager alloc] init];
[self.manager setAllowsNonContiguousLayout:YES];
[self.storage addLayoutManager:self.manager];

// loop through glyphs and layout text in multicolumn, multipage layout
while (lastRenderedGlyph < self.manager.numberOfGlyphs) {

    if (columnCount == 3)
    {
        columnCount = 1;
        pageCount++;
    }
    float textViewFrame_y = 10.0;
    float textViewFrame_height = height - 70.0;

    CGRect textViewFrame = CGRectMake(currentXOffset, textViewFrame_y,
                                      width / 2,
                                      textViewFrame_height);
    CGSize columnSize = CGSizeMake(CGRectGetWidth(textViewFrame) - 20,
                                   CGRectGetHeight(textViewFrame) - 10);

    // Create text container for the column
    NSTextContainer *textContainer = [[NSTextContainer alloc] initWithSize:columnSize];

    // add text container to the layout manager
    [self.manager addTextContainer:textContainer];

    // create the UITextView for this column        
    UITextView *textView = [[UITextView alloc] initWithFrame:textViewFrame
                                               textContainer:textContainer];

    textView.font = [UIFont fontWithName:@"Georgia" size:currentFontSize];    
    textView.scrollEnabled = NO;
    textView.editable = NO;
    textView.dataDetectorTypes = UIDataDetectorTypeAll;
    textView.delegate = self;
    textView.selectable = YES;

    // Add the UITextView to the scrollview/page
    [self.scrollView addSubview:textView];

    // Increase the current offset so we know where to put the next column
    currentXOffset += CGRectGetWidth(textViewFrame);

    counter++;
    columnCount ++;

    // And find the index of the glyph we've just rendered
    lastRenderedGlyph = NSMaxRange([self.manager  glyphRangeForTextContainer:textView.textContainer]);
}

Prior to doing this, however, I need to add two additional attributed strings into the existing NSAttributedString (self.attrString in this example), as follows:

UIFont *font_title = [UIFont fontWithName:@"Georgia" size:defaultFontSize+title_delta];
NSDictionary *attributes_title = [NSDictionary dictionaryWithObject:font_title forKey:NSFontAttributeName];

UIFont *font_author = [UIFont fontWithName:@"Georgia" size:authorFontSize];
NSDictionary *attributes_author = [NSDictionary dictionaryWithObject:font_author forKey:NSFontAttributeName];

// title attributed string
NSString *fullTitle = [NSString stringWithFormat:@"%@%@",@"Moby Dick",@"\n"];

NSMutableAttributedString *mainTitleAttributed = [[NSMutableAttributedString alloc] initWithString:fullTitle attributes:attributes_title];

// author attributed string
NSString *fullAuthor = [NSString stringWithFormat:@"%@%@", @"Herman Melville", @"\n"];
NSMutableAttributedString *authorAttributed = [[NSMutableAttributedString alloc] initWithString:fullAuthor attributes:attributes_author];

// put it all together
[self.attrString insertAttributedString:authorAttributed atIndex:0];
[self.attrString insertAttributedString:mainTitleAttributed atIndex:0];

When I review the contents of the attributed string that results, I get the following:

Moby Dick {NSFont = " font-family: \"Georgia\"; font-weight: normal; font-style: normal; font-size: 28.00pt";}Herman Melville{NSFont = " font-family: \"Georgia\"; font-weight: normal; font-style: normal; font-size: 12.00pt"; }Lorem ipsum dolor et { NSColor = "UIDeviceWhiteColorSpace 0 1"; NSFont = " font-family: \"Times New Roman\"; font-weight: normal; font-style: normal; font-size: 12.00pt"; NSParagraphStyle = "Alignment 4, LineSpacing 0, ParagraphSpacing 12, ParagraphSpacingBefore 0, HeadIndent 0, TailIndent 0, FirstLineHeadIndent 0, LineHeight 0/0, LineHeightMultiple 0, LineBreakMode 0, Tabs (\n 28L,\n 56L,\n 84L,\n 112L,\n
140L,\n 168L,\n 196L,\n 224L,\n 252L,\n 280L,\n
308L,\n 336L\n), DefaultTabInterval 36, Blocks (null), Lists (null), BaseWritingDirection -1, HyphenationFactor 0, TighteningFactor 0, HeaderLevel 0"; }more lorem ipsum etc etc...

i.e. it looks like the attributes have been added correctly. However, when this is rendered using TextKit (see the first chunk of code), the text size and different font of the merged text components (the title and author in this example) fail to render as such, and simply get assigned the same basic attributes as the following text.

If, instead of using TextKit, I create a single UITextView and render the NSAttributedString directly into that view (i.e. not using a NSLayoutManager, etc), the font and size differences in the inserted attributed strings DO render correctly.

My question: why does the added text fail to render correctly when using TextKit, but not when working directly with a UITextView? I need to work with TextKit for its multicolumn and exclusion path support.

1

1 Answers

0
votes

I don't think your attrbuted string has proper attributes assigned to different chunks of texts. Try instead of using insertAttributedString:atIndex use the following approach:

  • Create a string with placeholders '{Title} {Author}' (for example) and assign attributes to these placeholders
  • Replace placeholders with different plain strings using replaceCharactersInRange (make sure you do this in reverse order - first replacing Author placeholder, then Title)

UPDATE: After further discussion in comments we figured out the problem with the code. After assigning attributed string to the textView author was calling textView.font = ... and this was cleaning all attributes.