9

Hi I'm working with the new TextKit API for iOS7 and I'm trying to produce a UITextView with an irregular shape. So far I have in a view controller:

-(void) loadView
{
    self.view = [[UIView alloc] initWithFrame:CGRectMake(0,0,320,548)];

    NSTextStorage *textStorage = [[NSTextStorage alloc] init];

    NSLayoutManager *layoutManager = [[NSLayoutManager alloc] init];
    [textStorage addLayoutManager: layoutManager];

    BaseTextContainer *textContainer = [[BaseTextContainer alloc] initWithSize:CGSizeMake(100, 100)];
    [layoutManager addTextContainer: textContainer];

    BaseTextView *textView = [[BaseTextView alloc] initWithFrame:CGRectMake(110,124, 100, 100) textContainer:textContainer];
    textView.backgroundColor = [UIColor blueColor];
    textView.editable = YES;
    [self.view addSubview:textView];
}

Then in my subclassed NSTextContainer, I want to have a mutablePath drawn as the shape of the text container, but not sure how to accomplish this. I have:

- (BOOL) isSimpleRectangularTextContainer
{
    return NO;
}

- (void) drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx
{
    NSLog(@"TEST");
    CGContextRef context = ctx;
    CGSize layerSize = layer.frame.size;

    CGAffineTransform transform = CGAffineTransformMakeScale(layerSize.width / self.initialSize.width, layerSize.height / self.initialSize.height);
    CGMutablePathRef newGraphicMutablePath = CGPathCreateMutableCopyByTransformingPath(self.mutablePath, &transform);
    CGContextAddPath(context, newGraphicMutablePath);

    CGPathRelease(newGraphicMutablePath);
    CGContextSetFillColorWithColor(context, [UIColor redColor].CGColor);
    CGContextDrawPath(context, kCGPathFill);
}

Just a bit confused about how to get this to work. I cannot find an example anywhere of an NSTextContainer with an irregular shape.

matt
  • 515,959
  • 87
  • 875
  • 1,141
Ser Pounce
  • 14,196
  • 18
  • 84
  • 169
  • What is to be irregular, the margins of the text or the border drawn around it? For example, I have written an example where the text is bounded by a circular margin. Is that what you mean? – matt Apr 25 '14 at 06:42
  • @matt - I'm trying to get some text to fit in an oval shape, much like a word balloon. – Ser Pounce Apr 25 '14 at 06:46
  • Then instead of a text view I recommend you use Text Kit to draw the text directly. – matt Apr 25 '14 at 06:57
  • Use exclusion paths to restrict where the text can appear in the text container. – matt Apr 25 '14 at 06:59
  • @matt - that's what I'm actually doing currently with CoreText, but wanted to see if I could switch to TextKit with a UITextView for compatibility with all languages and other characters that are giving me hell in CoreText (and also copy paste, dictation, etc). Is it not possible to have an ellipse shaped UITextView do you know? – Ser Pounce Apr 25 '14 at 07:08
  • It is possible, certainly. Use exclusion paths, as I just said. Text Kit is Text Kit, whether you use a text view or draw directly. – matt Apr 25 '14 at 07:21

1 Answers1

14

There is no need for all that code constructing the Text Kit stack, as you are not modifying the architecture of the stack. Just start with a normal UITextView - let's say it's self.textView - and then assign one or more UIBezierPath objects to its exclusion paths:

self.tv.textContainer.exclusionPaths = myArrayOfBezierPaths;

These paths are exclusion paths, so for an ellipse you will want to make four paths, each one describing a corner of the text container.

Alternatively, you can build the Text Kit stack yourself so as to insert your own text container subclass, and modify where the text is allowed to go by overriding lineFragmentForProposedRect:, perhaps similar to what I do here: https://github.com/mattneub/Programming-iOS-Book-Examples/blob/master/bk2ch10p537exclusionPath2/ch23p813textKitShapes/MyTextContainer.swift

Some experiments:

enter image description here

enter image description here

Neil
  • 1,165
  • 1
  • 14
  • 24
matt
  • 515,959
  • 87
  • 875
  • 1,141
  • @matt in your linked code, your test for being in the circle or not feels really inefficient. If your circle is centered at the origin, then you know that any point on the circle must satisfy x^2 + y^2 = r^2 and any point on or inside the circle must satisfy x^2 + y^2 <= r^2. If you want to move across a horizontal line from a test point keeping y constant, you just need to find the first x that satisfies x = sqrt(r^2 - y^2) assuming y^2 <= r^2. That would require far fewer iterations of your loop, and should be faster assuming sqrt is fast. Thanks for sharing your code by the way. – Joel Jul 09 '14 at 04:48
  • @matt You're link is broken since you updated the code to Swift. – Neil Sep 22 '14 at 20:34
  • @Neil and you can't figure out what the current link would be? – matt Sep 22 '14 at 20:36
  • @matt Figured it out just fine, but broken links are annoying – Neil Sep 22 '14 at 20:37