CTLineGetTypographicBounds crash

  • I'm looking to add some highlighting to my Core Text View, and implemented the following which comes straight from Apple's SimpleTextInput code example (https://developer.apple.com/library/iOs/#samplecode/SimpleTextInput/Introdu
    ction/Intro.html
    ):

    // Helper method for drawing the current selection range (as a simple filled rect)
    - (void)drawRangeAsSelection:(NSRange)selectionRange
    {
    // If not in editing mode, we do not draw selection rects
        if (!self.editing)
            return;

        // If selection range empty, do not draw
        if (selectionRange.length == 0 || selectionRange.location == NSNotFound)
            return;

    // set the fill color to the selection color
        [[SimpleCoreTextView selectionColor] setFill];

    // Iterate over the lines in our CTFrame, looking for lines that intersect
    // with the given selection range, and draw a selection rect for each intersection
        NSArray *lines = (NSArray *) CTFrameGetLines(_frame);
        for (int i = 0; i < [lines count]; i++) {
            CTLineRef line = (CTLineRef) [lines objectAtIndex:i];
            CFRange lineRange = CTLineGetStringRange(line);
            NSRange range = NSMakeRange(lineRange.location, lineRange.length);
            NSRange intersection = [self RangeIntersection:range withSecond:selectionRange];
            if (intersection.location != NSNotFound && intersection.length > 0) {
      // The text range for this line intersects our selection range
                CGFloat xStart = CTLineGetOffsetForStringIndex(line, intersection.location, NULL);
                CGFloat xEnd = CTLineGetOffsetForStringIndex(line, intersection.location + intersection.length, NULL);
                CGPoint origin;
      // Get coordinate and bounds information for the intersection text range
                CTFrameGetLineOrigins(_frame, CFRangeMake(i, 0), &origin);
                CGFloat ascent, descent;
                CTLineGetTypographicBounds(line, &ascent, &descent, NULL);            <<<<<<<<<===========================  BOOM!
      // Create a rect for the intersection and draw it with selection color
                CGRect selectionRect = CGRectMake(xStart, origin.y - descent, xEnd - xStart, ascent + descent);
                UIRectFill(selectionRect);
            }
        }
    }

    But every time, I get an EXC_BAD_ACCESS error for the CTLineGetTypographicBounds() call.  I triple checked that _frame and line are valid at this point, and the debugger output shows what it is supposed to be (correct string, attributes, etc).

    Here's the output from bt:

    (lldb) bt
    * thread #1: tid = 0x1c03, 0x0027bbd0 CoreText`CTLineGetTypographicBounds + 29, stop reason = EXC_BAD_ACCESS (code=2, address=0x60)
        frame #0: 0x0027bbd0 CoreText`CTLineGetTypographicBounds + 29
        frame #1: 0x000469a4 MyApp`-[MyView drawRangeAsSelection:](self=0x4576f000, _cmd=0x00000000, selectionRange=(null)) + 628 at MyView.m:461

    Any suggestions why this could be happening?

    Thanks,

    - Koen.
  • On May 22, 2013, at 8:05 AM, Koen van der Drift <koenvanderdrift...> wrote:

    > I'm looking to add some highlighting to my Core Text View, and implemented the following which comes straight from Apple's SimpleTextInput code example (https://developer.apple.com/library/iOs/#samplecode/SimpleTextInput/Introdu
    ction/Intro.html
    ):
    >
    > // Helper method for drawing the current selection range (as a simple filled rect)
    > - (void)drawRangeAsSelection:(NSRange)selectionRange
    > {
    > // If not in editing mode, we do not draw selection rects
    > if (!self.editing)
    > return;
    >
    > // If selection range empty, do not draw
    > if (selectionRange.length == 0 || selectionRange.location == NSNotFound)
    > return;
    >
    > // set the fill color to the selection color
    > [[SimpleCoreTextView selectionColor] setFill];
    >
    > // Iterate over the lines in our CTFrame, looking for lines that intersect
    > // with the given selection range, and draw a selection rect for each intersection
    > NSArray *lines = (NSArray *) CTFrameGetLines(_frame);
    > for (int i = 0; i < [lines count]; i++) {
    > CTLineRef line = (CTLineRef) [lines objectAtIndex:i];
    > CFRange lineRange = CTLineGetStringRange(line);
    > NSRange range = NSMakeRange(lineRange.location, lineRange.length);
    > NSRange intersection = [self RangeIntersection:range withSecond:selectionRange];
    > if (intersection.location != NSNotFound && intersection.length > 0) {
    > // The text range for this line intersects our selection range
    > CGFloat xStart = CTLineGetOffsetForStringIndex(line, intersection.location, NULL);
    > CGFloat xEnd = CTLineGetOffsetForStringIndex(line, intersection.location + intersection.length, NULL);
    > CGPoint origin;
    > // Get coordinate and bounds information for the intersection text range
    > CTFrameGetLineOrigins(_frame, CFRangeMake(i, 0), &origin);
    > CGFloat ascent, descent;
    > CTLineGetTypographicBounds(line, &ascent, &descent, NULL);            <<<<<<<<<===========================  BOOM!
    > // Create a rect for the intersection and draw it with selection color
    > CGRect selectionRect = CGRectMake(xStart, origin.y - descent, xEnd - xStart, ascent + descent);
    > UIRectFill(selectionRect);
    > }
    > }
    > }
    >
    >
    >
    > But every time, I get an EXC_BAD_ACCESS error for the CTLineGetTypographicBounds() call.  I triple checked that _frame and line are valid at this point, and the debugger output shows what it is supposed to be (correct string, attributes, etc).
    >
    > Here's the output from bt:
    >
    > (lldb) bt
    > * thread #1: tid = 0x1c03, 0x0027bbd0 CoreText`CTLineGetTypographicBounds + 29, stop reason = EXC_BAD_ACCESS (code=2, address=0x60)
    > frame #0: 0x0027bbd0 CoreText`CTLineGetTypographicBounds + 29
    > frame #1: 0x000469a4 MyApp`-[MyView drawRangeAsSelection:](self=0x4576f000, _cmd=0x00000000, selectionRange=(null)) + 628 at MyView.m:461
    >
    >
    > Any suggestions why this could be happening?

    The preceding call looks like it could be problematic:

    > CTFrameGetLineOrigins(_frame, CFRangeMake(i, 0), &origin);

    If you want a single line origin (CGPoint), looks like the length of the range passed in should be 1, not 0.

    From https://developer.apple.com/library/mac/#documentation/Carbon/reference/CTF
    rameRef/Reference/reference.html


    > range
    > The range of line origins you wish to copy. If the length of the range is 0, then the copy operation continues from the start index of the range to the last line origin.
  • On May 22, 2013, at 10:05 AM, Michael Babin <mbabin...> wrote:

    > The preceding call looks like it could be problematic:
    >
    >> CTFrameGetLineOrigins(_frame, CFRangeMake(i, 0), &origin);
    >
    > If you want a single line origin (CGPoint), looks like the length of the range passed in should be 1, not 0.

    Well spotted, that fixed it, thank you very much!

    I now get a bunch of <Error>: CGContextFillRects: invalid context 0x0 errors, which makes sense since this snippet does not have something like:

        CGContextRef context = UIGraphicsGetCurrentContext();

    I replaced UIRectFill(selectionRect) with CGContextFillRect(context, selectionRect);

    But I still get the errors, and nothing is drawn.

    - Koen.
  • On May 22, 2013, at 10:16 AM, Koen van der Drift <koenvanderdrift...> wrote:

    > I now get a bunch of <Error>: CGContextFillRects: invalid context 0x0 errors, which makes sense since this snippet does not have something like:
    >
    > CGContextRef context = UIGraphicsGetCurrentContext();
    >
    > I replaced UIRectFill(selectionRect) with CGContextFillRect(context, selectionRect);
    >
    > But I still get the errors, and nothing is drawn.

    Easy fix, I had to call - (void)drawRangeAsSelection:(NSRange)selectionRange from within drawRect.

    - Koen.
previous month may 2013 next month
MTWTFSS
    1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31    
Go to today