6

I have an iPhone application and I need to implement the following method:

+(UITextView *)textView:(UITextView *) withCuttedRect:(CGRect)r

This method must cut (fill with [UIColor clearColor]) the rect r from UITextView and return UITextView object.

The user will see the view behind UITextView from the cutted holes.

How can it be done?

Tunaki
  • 132,869
  • 46
  • 340
  • 423
user1385666
  • 357
  • 1
  • 7
  • 17

1 Answers1

6

When you would have something like:

 +(UITextView *)textView:(UITextView *)textView withCuttedRect:(CGRect)r {
}

you actually can simply access the textview's layer from core animation with

 textView.layer

What you then can to is set a mask for clipping. These masks work the following way: you generally draw a black shape, and that stays the same, the rest will be clipped (ok, you actually can also do some things on the alpha channel but roughly that is it).

So you need a black rectangle as a mask, with a rectangle within the rectangle which is free. for that you can approximately do

 CAShapeLayer *mask = [[CAShapeLayer alloc] init];
 mask.frame = self.textView.layer.bounds;
 CGRect biggerRect = CGRectMake(mask.frame.origin.x, mask.frame.origin.y, mask.frame.size.width, mask.frame.size.height);
 CGRect smallerRect = CGRectMake(50.0f, 50.0f, 10.0f, 10.0f);

 UIBezierPath *maskPath = [UIBezierPath bezierPath];
[maskPath moveToPoint:CGPointMake(CGRectGetMinX(biggerRect), CGRectGetMinY(biggerRect))];
[maskPath addLineToPoint:CGPointMake(CGRectGetMinX(biggerRect), CGRectGetMaxY(biggerRect))];
[maskPath addLineToPoint:CGPointMake(CGRectGetMaxX(biggerRect), CGRectGetMaxY(biggerRect))];
[maskPath addLineToPoint:CGPointMake(CGRectGetMaxX(biggerRect), CGRectGetMinY(biggerRect))];
[maskPath addLineToPoint:CGPointMake(CGRectGetMinX(biggerRect), CGRectGetMinY(biggerRect))];

[maskPath moveToPoint:CGPointMake(CGRectGetMinX(smallerRect), CGRectGetMinY(smallerRect))];
[maskPath addLineToPoint:CGPointMake(CGRectGetMinX(smallerRect), CGRectGetMaxY(smallerRect))];
[maskPath addLineToPoint:CGPointMake(CGRectGetMaxX(smallerRect), CGRectGetMaxY(smallerRect))];
[maskPath addLineToPoint:CGPointMake(CGRectGetMaxX(smallerRect), CGRectGetMinY(smallerRect))];
[maskPath addLineToPoint:CGPointMake(CGRectGetMinX(smallerRect), CGRectGetMinY(smallerRect))];

 mask.path = maskPath.CGPath;
[mask setFillRule:kCAFillRuleEvenOdd];
 mask.fillColor = [[UIColor blackColor] CGColor];
 self.textView.layer.mask = mask;

The code above is also disused by Crop a CAShapeLayer retrieving the external path

The idea why that filling works that way is nicely explained in Quartz 2D Programming Guide in the section "Filling a Path"

Community
  • 1
  • 1
Joerg Simon
  • 421
  • 2
  • 7
  • Nice! And just a note, for doing the same with a `CGContext`, you add the path the same as you did, and then you fill it with `CGContextEOFillPath(context)`. :) Where EO means even odd, not to be confused with EndOfFile (which is how it scans for me). – Kaolin Fire Mar 14 '13 at 01:39
  • 1
    You can trim off a few lines of code here by using `[UIBezierPath bezierPathWithRect:biggerRect]` instead of the basic `[UIBezierPath bezierPath]`. This will initialize the maskPath with the biggerRect pre-added. That way you only need to use moveToPoint: and addLineToPoint: to add the second rect. – Sebastien Martin Feb 10 '14 at 04:18