Tyler McAtee Tyler McAtee - 20 days ago 4x
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?


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?


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];