UIView with rounded corners: how to clip subviews correctly?

16,200

Solution 1

I haven't tried it, but I think you could use CALayer's mask property to do this. You'd have to draw your rounded rectangle into a layer that was set as the mask to your view layer.

Solution 2

It is possible (and, in fact, very easy) to specify particular rounded corners without resorting to drawRect:, or manually drawing a partially rounded rect into a layer. See my answer on a similar question.

Share:
16,200

Related videos on Youtube

Krumelur
Author by

Krumelur

Freelancer, Xamarin Hacker. Computers (gaming and developing) have always been part of my life, starting with the famous C64 and moving on to the PC a few years later. For quite some time, an iPhone has been my companion. I'm excited about developing mobile applications, especially with Xamarin.

Updated on June 04, 2022

Comments

  • Krumelur
    Krumelur almost 2 years

    I created a subclass of UIView that overrides drawRect: and uses AddArcToPoint() to draw rounded corners. (I don't want to use the layer's corner radius property because I need to define which corners have to be rounded.) The problem I cannot get over however: if I add a subview at (0|0), it hides my round corners. Any idea how I can fix this? I would like it to clip nicely.

    Here's the code that draws the round cornered rectangle. It's Monotouch but should be readably by any developer.

    (you can find the full code here: https://github.com/Krumelur/RoundedRectView)

    public override void Draw (RectangleF rect)
            {
                using (var oContext = UIGraphics.GetCurrentContext())
                {
                    oContext.SetLineWidth (this.StrokeWidth);
                    oContext.SetStrokeColor (this.oStrokeColor.CGColor);
                    oContext.SetFillColor (this.oRectColor.CGColor);
    
                    RectangleF oRect = this.Bounds;
    
                    float fRadius = this.CornerRadius;
                    float fWidth = oRect.Width;
                    float fHeight = oRect.Height;
    
                    // Make sure corner radius isn't larger than half the shorter side.
                    if (fRadius > fWidth / 2.0f)
                    {
                        fRadius = fWidth / 2.0f;
                    }
                    if (fRadius > fHeight / 2.0f)
                    {
                        fRadius = fHeight / 2.0f;    
                    }
    
                    float fMinX = oRect.GetMinX ();
                    float fMidX = oRect.GetMidX ();
                    float fMaxX = oRect.GetMaxX ();
                    float fMinY = oRect.GetMinY ();
                    float fMidY = oRect.GetMidY ();
                    float fMaxY = oRect.GetMaxY ();
    
                    // Move to left middle.
                    oContext.MoveTo (fMinX, fMidY);
    
                    // Arc to top middle.
                    oContext.AddArcToPoint (fMinX, fMinY, fMidX, fMinY, (this.RoundCorners & ROUND_CORNERS.TopLeft) == ROUND_CORNERS.TopLeft ? fRadius : 0);
                    // Arc to right middle.
                    oContext.AddArcToPoint (fMaxX, fMinY, fMaxX, fMidY, (this.RoundCorners & ROUND_CORNERS.TopRight) == ROUND_CORNERS.TopRight ? fRadius : 0);
                    // Arc to bottom middle.
                    oContext.AddArcToPoint (fMaxX, fMaxY, fMidX, fMaxY, (this.RoundCorners & ROUND_CORNERS.BottomRight) == ROUND_CORNERS.BottomRight ? fRadius : 0);
                    // Arc to left middle.
                    oContext.AddArcToPoint (fMinX, fMaxY, fMinX, fMidY, (this.RoundCorners & ROUND_CORNERS.BottomLeft) == ROUND_CORNERS.BottomLeft ? fRadius : 0);
    
                    // Draw the path.
                    oContext.ClosePath ();
                    oContext.DrawPath (CGPathDrawingMode.FillStroke);
                }
            }
    

    EDIT:

    Here's a piece of code that demonstrates how to solve it using CALayer.

    private void UpdateMask()
            {
                UIBezierPath oMaskPath = UIBezierPath.FromRoundedRect (this.Bounds, this.eRoundedCorners, new SizeF (this.fCornerRadius, this.fCornerRadius));
    
                CAShapeLayer oMaskLayer = new CAShapeLayer ();
                oMaskLayer.Frame = this.Bounds;
                oMaskLayer.Path = oMaskPath.CGPath;
                this.Layer.Mask = oMaskLayer;
            }
    
  • Krumelur
    Krumelur over 12 years
    That was a brilliant hint! I will update the code on github.com/Krumelur/RoundedRectView to use as CALayer mask. It is a one liner!
  • Krumelur
    Krumelur over 12 years
    Yeah, that's what I found and used after getting the CALayer hint. Thanks! My only issue with it is auto resizing. CALayer does not auto resize. So I override frame and bounds and update the mask but this cannot be animated and if you rotate the device, it looks a bit chunky.
  • Daniel Thorpe
    Daniel Thorpe almost 12 years
    The code to do this in Objective-C is shown here: stackoverflow.com/a/10515546/197626