Tyler McAtee Tyler McAtee - 2 months ago 10
Objective-C Question

Don't display subviews outside of UIBezierPath

I have one UIView which is drawn with:

- (void)drawRect:(CGRect)rect
{
UIBezierPath *bezierPath = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 0, self.widthOfLine, self.heightOfLine)];

[bezierPath fill];
}


and whose frame is centered within the iOS window. I want to place a bunch of smaller (5px by 5px) views within this view randomly, which I am doing fine with arc4random() function and drawing the views. However, I can't seem to figure out how to cut off all views outside the bezierPathWithOvalInRect, it will just display them everywhere in the first UIView's frame. Anyone know what to do for this?

edit:

For clarity, I have defined another view as:

- (void)drawRect:(CGRect)rect
{
UIBezierPath *drawPath = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(self.xPosition, self.yPosition, self.width, self.width)];
[[UIColor whiteColor] setFill];
[drawPath fill];
}


and I am adding a large number of these to the first view as subviews, but I want them to not be displayed outside the oval bezier path. Is there a way to a) only add them within the oval bezier path as opposed to within the entire frame, or b) a way to clip all that are outside the oval from the view?

Answer

In order to clip all subviews to a particular UIBezierPath, you'll want to set the mask property of the view's layer property. If you set the layer.mask to a CAShapeLayer of that path, then all subviews will be cropped to that path.

I use the following code to do just that in my app:

    // create your view that'll hold all the subviews that
    // need to be clipped to the path
    CGRect anyRect = ...;
    UIView* clippedView = [[UIView alloc] initWithFrame:anyRect];
    clippedView.clipsToBounds = YES;
    // now create the shape layer that we'll use to clip
    CAShapeLayer* maskingLayer = [CAShapeLayer layer];
    [maskingLayer setPath:bezierPath.CGPath];
    maskingLayer.frame = backingImageHolder.bounds;
    // set the mask property of the layer, and this will
    // make sure that all subviews are only visible inside
    // this path
    clippedView.layer.mask = maskingLayer;

    // now any subviews you add won't show outside of that path
    UIView* anySubview = ...;
    [clippedView addSubview:anySubview];