Search code examples
iphoneobjective-cipadrounded-corners

Just two rounded corners?


In my iPad app, I want a second view to appear within the main when the user clicks a button. The new view will be smaller than the first, and darken the background when it is displayed. I want the top two corners of the new view to appear rounded, but using cornerRadius sets all of them rounded. How can I make just two corners rounded?


Solution

  • You have to do this in drawRect:. I actually modified the classic addRoundedRectToPath: so that it takes a bitmap and rounds the corners you request:

    static void addRoundedRectToPath(CGContextRef context, CGRect rect, float radius, UIImageRoundedCorner cornerMask)
    {
        CGContextMoveToPoint(context, rect.origin.x, rect.origin.y + radius);
        CGContextAddLineToPoint(context, rect.origin.x, rect.origin.y + rect.size.height - radius);
        if (cornerMask & UIImageRoundedCornerTopLeft) {
            CGContextAddArc(context, rect.origin.x + radius, rect.origin.y + rect.size.height - radius, 
                            radius, M_PI, M_PI / 2, 1);
        }
        else {
            CGContextAddLineToPoint(context, rect.origin.x, rect.origin.y + rect.size.height);
            CGContextAddLineToPoint(context, rect.origin.x + radius, rect.origin.y + rect.size.height);
        }
    
        CGContextAddLineToPoint(context, rect.origin.x + rect.size.width - radius, 
                              rect.origin.y + rect.size.height);
    
        if (cornerMask & UIImageRoundedCornerTopRight) {
            CGContextAddArc(context, rect.origin.x + rect.size.width - radius, 
                            rect.origin.y + rect.size.height - radius, radius, M_PI / 2, 0.0f, 1);
        }
        else {
            CGContextAddLineToPoint(context, rect.origin.x + rect.size.width, rect.origin.y + rect.size.height);
            CGContextAddLineToPoint(context, rect.origin.x + rect.size.width, rect.origin.y + rect.size.height - radius);
        }
    
        CGContextAddLineToPoint(context, rect.origin.x + rect.size.width, rect.origin.y + radius);
    
        if (cornerMask & UIImageRoundedCornerBottomRight) {
            CGContextAddArc(context, rect.origin.x + rect.size.width - radius, rect.origin.y + radius, 
                            radius, 0.0f, -M_PI / 2, 1);
        }
        else {
            CGContextAddLineToPoint(context, rect.origin.x + rect.size.width, rect.origin.y);
            CGContextAddLineToPoint(context, rect.origin.x + rect.size.width - radius, rect.origin.y);
        }
    
        CGContextAddLineToPoint(context, rect.origin.x + radius, rect.origin.y);
    
        if (cornerMask & UIImageRoundedCornerBottomLeft) {
            CGContextAddArc(context, rect.origin.x + radius, rect.origin.y + radius, radius, 
                            -M_PI / 2, M_PI, 1);
        }
        else {
            CGContextAddLineToPoint(context, rect.origin.x, rect.origin.y);
            CGContextAddLineToPoint(context, rect.origin.x, rect.origin.y + radius);
        }
    
        CGContextClosePath(context);
    }
    

    This takes a bitmask (I called it UIImageRoundedCorner because I was doing this for images, but you can call it whatever) and then builds up a path based on the corners you want rounded. Then you apply that path to the view in drawRect:

    CGContextBeginPath(context);
    addRoundedRectToPath(context, rect, radius, yourMask);
    CGContextClosePath(context);
    CGContextClip(context);
    

    As I said, I was doing this for UIImages, so my code isn't exactly set up for use in drawRect:, but it should be pretty easy to adapt it. You're basically just building up a path and then clipping the context to it.

    Edit: To explain the bitmask part, it's just an enum:

    typedef enum {
        UIImageRoundedCornerTopLeft = 1,
        UIImageRoundedCornerTopRight = 1 << 1,
        UIImageRoundedCornerBottomRight = 1 << 2,
        UIImageRoundedCornerBottomLeft = 1 << 3
    } UIImageRoundedCorner;
    

    In this way you can OR things together to form a bitmask that identifies corners, since each member of the enum represents a different power of two in the bitmask.

    Much Later Edit: I've discovered an easier way to do this using UIBezierPath's bezierPathWithRoundedRect:byRoundingCorners:cornerRadii:. Just use the bezier path object's CGPath in your context.