How is a rounded rect view with transparency done on iphone? - iphone

A lot of apps pop up a transparent view with rounded corners and an activityIndicator when running a time consuming operation.
How is this rounding done and is it possible to do it just using Interface Builder (as there are lots of places I'd like to use something like this)? Or, should I use an imageview with a rounded rect or stretchable image? Do I need to draw the background myself?
So far, I have managed to get a basic view with similar transparency by setting the alphaValue in Interface Builder however it doesn't have rounded corners, and also the transparency seems to apply to all subviews (I don't want the text and activityindicator to be transparent, however even though I set the alphaValue on those in IB it seems to get ignored).

As of iPhone SDK 3.0, you can simply use the layer's cornerRadius property. E.g.:
view.layer.cornerRadius = 10.0;
Along the same lines, you can change the view's border color and width:
view.layer.borderColor = [[UIColor grayColor] CGColor];
view.layer.borderWidth = 1;

view.layer.cornerRadius = radius;
The hard way (that used to be required in the first iPhone SDK) is to create your own UIView subclass with drawRect: method:
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetRGBFillColor(context, 0,0,0,0.75);
CGContextMoveToPoint(context, rect.origin.x, rect.origin.y + radius);
CGContextAddLineToPoint(context, rect.origin.x, rect.origin.y + rect.size.height - radius);
CGContextAddArc(context, rect.origin.x + radius, rect.origin.y + rect.size.height - radius,
radius, M_PI, M_PI / 2, 1); //STS fixed
CGContextAddLineToPoint(context, rect.origin.x + rect.size.width - radius,
rect.origin.y + rect.size.height);
CGContextAddArc(context, rect.origin.x + rect.size.width - radius,
rect.origin.y + rect.size.height - radius, radius, M_PI / 2, 0.0f, 1);
CGContextAddLineToPoint(context, rect.origin.x + rect.size.width, rect.origin.y + radius);
CGContextAddArc(context, rect.origin.x + rect.size.width - radius, rect.origin.y + radius,
radius, 0.0f, -M_PI / 2, 1);
CGContextAddLineToPoint(context, rect.origin.x + radius, rect.origin.y);
CGContextAddArc(context, rect.origin.x + radius, rect.origin.y + radius, radius,
-M_PI / 2, M_PI, 1);
CGContextFillPath(context);
Note: rect in this code should be taken from [self bounds] (or whatever location you want it in), it won't make sense with rect passed to drawRect: method.

I abstracted out #lostInTransit's response into this function:
static void ContextAddRoundedRect(CGContextRef c, CGRect rect, CGFloat radius) {
CGFloat minX = CGRectGetMinX(rect);
CGFloat maxX = CGRectGetMaxX(rect);
CGFloat minY = CGRectGetMinY(rect);
CGFloat maxY = CGRectGetMaxY(rect);
CGContextMoveToPoint(c, minX + radius, minY);
CGContextAddArcToPoint(c, maxX, minY, maxX, minY + radius, radius);
CGContextAddArcToPoint(c, maxX, maxY, maxX - radius, maxY, radius);
CGContextAddArcToPoint(c, minX, maxY, minX, maxY - radius, radius);
CGContextAddArcToPoint(c, minX, minY, minX + radius, minY, radius);
}
which places the path onto the context for you to do with as you may
slightly different CoreGraphics calls and i didn't close the path, in case you want to add that
CGContextFillPath(c);

In your view do this in the drawRect method
float radius = 5.0f;
CGRect rect = self.bounds;
CGContextRef context = UIGraphicsGetCurrentContext();
rect = CGRectInset(rect, 1.0f, 1.0f);
CGContextBeginPath(context);
CGContextSetGrayFillColor(context, 0.5, 0.7);
CGContextMoveToPoint(context, CGRectGetMinX(rect) + radius, CGRectGetMinY(rect));
CGContextAddArc(context, CGRectGetMaxX(rect) - radius, CGRectGetMinY(rect) + radius, radius, 3 * M_PI / 2, 0, 0);
CGContextAddArc(context, CGRectGetMaxX(rect) - radius, CGRectGetMaxY(rect) - radius, radius, 0, M_PI / 2, 0);
CGContextAddArc(context, CGRectGetMinX(rect) + radius, CGRectGetMaxY(rect) - radius, radius, M_PI / 2, M_PI, 0);
CGContextAddArc(context, CGRectGetMinX(rect) + radius, CGRectGetMinY(rect) + radius, radius, M_PI, 3 * M_PI / 2, 0);
CGContextClosePath(context);
CGContextFillPath(context);
This will make your rounded rectangle for your view. You can find a complete example in the HeadsUpUI sample provided with the SDK. HTH

MBProgressHUD....
http://www.cocoadev.com/index.pl?MBProgressHUD

Related

I need help to draw a pointer on a map callout

I have no experience in Quartz drawing.
I need to draw a rectangle on callout view. i found some sample code to draw it.
My current code for that is
- (void)drawRect:(CGRect)rect {
CGFloat stroke = 1.0;
CGFloat radius = 7.0;
CGMutablePathRef path = CGPathCreateMutable();
UIColor *color;
CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
CGContextRef context = UIGraphicsGetCurrentContext();
CGFloat parentX = [self relativeParentXPosition];
//Determine Size
rect = self.bounds;
rect.size.width -= stroke + 14;
rect.size.height -= stroke + 14 + CalloutMapAnnotationViewHeightAboveParent;
rect.origin.x += stroke / 2.0 + 7;
rect.origin.y += stroke / 2.0 + 7;
//Create Path For Callout Bubble
CGPathMoveToPoint(path, NULL, rect.origin.x, rect.origin.y + radius);
CGPathAddLineToPoint(path, NULL, rect.origin.x, rect.origin.y + rect.size.height - radius);
CGPathAddArc(path, NULL, rect.origin.x + radius, rect.origin.y + rect.size.height - radius, radius, 0.0f, M_PI / 2, 1);
CGPathAddLineToPoint(path, NULL, parentX - 15, rect.origin.y + rect.size.height);
CGPathAddLineToPoint(path, NULL, parentX, rect.origin.y + rect.size.height + 15);
CGPathAddLineToPoint(path, NULL, parentX + 15, rect.origin.y + rect.size.height);
CGPathAddLineToPoint(path, NULL, rect.origin.x + rect.size.width - radius, rect.origin.y + rect.size.height);
CGPathAddArc(path, NULL, rect.origin.x + rect.size.width - radius, rect.origin.y + rect.size.height - radius, radius, M_PI / 2, 0.0f, 1);
CGPathAddLineToPoint(path, NULL, rect.origin.x + rect.size.width, rect.origin.y + radius);
CGPathAddArc(path, NULL, rect.origin.x + rect.size.width - radius, rect.origin.y + radius, radius, 0.0f, -M_PI / 2, 1);
CGPathAddLineToPoint(path, NULL, rect.origin.x + radius, rect.origin.y);
CGPathAddArc(path, NULL, rect.origin.x + radius, rect.origin.y + radius, radius, -M_PI / 2, M_PI, 1);
CGPathCloseSubpath(path);
//Fill Callout Bubble & Add Shadow
color = [[UIColor blackColor] colorWithAlphaComponent:.6];
[color setFill];
CGContextAddPath(context, path);
CGContextSaveGState(context);
CGContextSetShadowWithColor(context, CGSizeMake (0, self.yShadowOffset), 6, [UIColor colorWithWhite:0 alpha:.5].CGColor);
CGContextFillPath(context);
CGContextRestoreGState(context);
//Stroke Callout Bubble
color = [[UIColor darkGrayColor] colorWithAlphaComponent:.9];
[color setStroke];
CGContextSetLineWidth(context, stroke);
CGContextSetLineCap(context, kCGLineCapSquare);
CGContextAddPath(context, path);
CGContextStrokePath(context);
//Determine Size for Gloss
CGRect glossRect = self.bounds;
glossRect.size.width = rect.size.width - stroke;
glossRect.size.height = (rect.size.height - stroke) / 2;
glossRect.origin.x = rect.origin.x + stroke / 2;
glossRect.origin.y += rect.origin.y + stroke / 2;
CGFloat glossTopRadius = radius - stroke / 2;
CGFloat glossBottomRadius = radius / 1.5;
//Create Path For Gloss
CGMutablePathRef glossPath = CGPathCreateMutable();
CGPathMoveToPoint(glossPath, NULL, glossRect.origin.x, glossRect.origin.y + glossTopRadius);
CGPathAddLineToPoint(glossPath, NULL, glossRect.origin.x, glossRect.origin.y + glossRect.size.height - glossBottomRadius);
CGPathAddArc(glossPath, NULL, glossRect.origin.x + glossBottomRadius, glossRect.origin.y + glossRect.size.height - glossBottomRadius, glossBottomRadius, M_PI, M_PI / 2, 1);
CGPathAddLineToPoint(glossPath, NULL, glossRect.origin.x + glossRect.size.width - glossBottomRadius, glossRect.origin.y + glossRect.size.height);
CGPathAddArc(glossPath, NULL, glossRect.origin.x + glossRect.size.width - glossBottomRadius, glossRect.origin.y + glossRect.size.height - glossBottomRadius, glossBottomRadius, M_PI / 2, 0.0f, 1);
CGPathAddLineToPoint(glossPath, NULL, glossRect.origin.x + glossRect.size.width, glossRect.origin.y + glossTopRadius);
CGPathAddArc(glossPath, NULL, glossRect.origin.x + glossRect.size.width - glossTopRadius, glossRect.origin.y + glossTopRadius, glossTopRadius, 0.0f, -M_PI / 2, 1);
CGPathAddLineToPoint(glossPath, NULL, glossRect.origin.x + glossTopRadius, glossRect.origin.y);
CGPathAddArc(glossPath, NULL, glossRect.origin.x + glossTopRadius, glossRect.origin.y + glossTopRadius, glossTopRadius, -M_PI / 2, M_PI, 1);
CGPathCloseSubpath(glossPath);
//Fill Gloss Path
CGContextAddPath(context, glossPath);
CGContextClip(context);
CGFloat colors[] =
{
1, 1, 1, .3,
1, 1, 1, .1,
};
CGFloat locations[] = { 0, 1.0 };
CGGradientRef gradient = CGGradientCreateWithColorComponents(space, colors, locations, 2);
CGPoint startPoint = glossRect.origin;
CGPoint endPoint = CGPointMake(glossRect.origin.x, glossRect.origin.y + glossRect.size.height);
CGContextDrawLinearGradient(context, gradient, startPoint, endPoint, 0);
//Gradient Stroke Gloss Path
CGContextAddPath(context, glossPath);
CGContextSetLineWidth(context, 2);
CGContextReplacePathWithStrokedPath(context);
CGContextClip(context);
CGFloat colors2[] =
{
1, 1, 1, .3,
1, 1, 1, .1,
1, 1, 1, .0,
};
CGFloat locations2[] = { 0, .1, 1.0 };
CGGradientRef gradient2 = CGGradientCreateWithColorComponents(space, colors2, locations2, 3);
CGPoint startPoint2 = glossRect.origin;
CGPoint endPoint2 = CGPointMake(glossRect.origin.x, glossRect.origin.y + glossRect.size.height);
CGContextDrawLinearGradient(context, gradient2, startPoint2, endPoint2, 0);
//Cleanup
CGPathRelease(path);
CGPathRelease(glossPath);
CGColorSpaceRelease(space);
CGGradientRelease(gradient);
CGGradientRelease(gradient2);
}
this is the current callout i am getting.But i need to draw that triangle shape at the edge at the top of the rectangle.
by studding it these 2 two lines i need to change
CGPathAddLineToPoint(path, NULL, parentX - 15, rect.origin.y + rect.size.height);
CGPathAddLineToPoint(path, NULL, parentX, rect.origin.y + rect.size.height + 15);
but i am poor in quartz graphics.
my required callout is like below image
triangle need to up of the callout view.
people who have knowledge on quartz graphics please help me.
Well, if you're looking for the callout to be on the top, try this. Replace the path for the callout bubble with the following:
//Determine Size
rect = self.bounds;
rect.size.width -= stroke + 14;
rect.size.height -= stroke + 14 + CalloutMapAnnotationViewHeightAboveParent;
rect.origin.x += stroke / 2.0 + 7;
rect.origin.y += stroke / 2.0 + 7 + 14;
//Create Path For Callout Bubble
CGPathMoveToPoint(path, NULL, rect.origin.x, rect.origin.y + radius);
CGPathAddLineToPoint(path, NULL, rect.origin.x, rect.origin.y + rect.size.height - radius); //Left line
CGPathAddArc(path, NULL, rect.origin.x + radius, rect.origin.y + rect.size.height - radius, radius, 0.0f, M_PI / 2, 1); //Bottom left arc
// CGPathAddLineToPoint(path, NULL, parentX - 15, rect.origin.y + rect.size.height); //bottom to callout line
// CGPathAddLineToPoint(path, NULL, parentX, rect.origin.y + rect.size.height + 15); //callout left
// CGPathAddLineToPoint(path, NULL, parentX + 15, rect.origin.y + rect.size.height); //callout right
CGPathAddLineToPoint(path, NULL, rect.origin.x + rect.size.width - radius, rect.origin.y + rect.size.height); //bottom to right line
CGPathAddArc(path, NULL, rect.origin.x + rect.size.width - radius, rect.origin.y + rect.size.height - radius, radius, M_PI / 2, 0.0f, 1); //bottom right
CGPathAddLineToPoint(path, NULL, rect.origin.x + rect.size.width, rect.origin.y + radius); //right line
CGPathAddArc(path, NULL, rect.origin.x + rect.size.width - radius, rect.origin.y + radius, radius, 0.0f, -M_PI / 2, 1); //top right
CGPathAddLineToPoint(path, NULL, parentX + 15, rect.origin.y); //top right to right callout
CGPathAddLineToPoint(path, NULL, parentX, rect.origin.y - 15); //callout right
CGPathAddLineToPoint(path, NULL, parentX - 15, rect.origin.y); //callout left
CGPathAddLineToPoint(path, NULL, rect.origin.x + radius, rect.origin.y); //top left callout to top left
CGPathAddArc(path, NULL, rect.origin.x + radius, rect.origin.y + radius, radius, -M_PI / 2, M_PI, 1); //top left
CGPathCloseSubpath(path); //Path closed, no line draw
I believe this should work (haven't tested it). But the gloss path is dependent on that callout rect being used so I think everything else should work fine.
As a side note, Core Graphics involves drawing paths kind of like drawing on a piece of paper. A CGPathMoveToPoint involves putting the pencil down. Everything after that is drawing either lines or arcs sequentially. All you need to do is read the point that the line/curve is being drawn to. Note that arcs are translated into curves, but to read them you need to know where pi, pi/2 and pi/4 is on a unit circle. That tells you where the arc is being drawn. But in general, you can assume (at least in this case) that the arc is corresponding to the actual corner the previous line was drawn to. I added comments indicating what path elements correspond to what part of the box. I also commented out the bottom callout rather than delete it. You can uncomment it to draw the bottom callout again but you'll need to remember to comment out the top and shift the rect back up (I shifted it down by 14). Also consider avoiding using 'magic numbers', like the size of the callout (15) and instead put something like this in your file:
#define CalloutSize 15.0f
Doing this and inserting CalloutSize into the code rather than 15 will allow you to easily change it's size without having to search for each instance it's being used.

Why is my CoreGraphics shape not showing a border?

This is the code I have:
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextClearRect(context, rect);
CGContextSetFillColorWithColor(context, [BUTTON_COLOR CGColor]);
CGContextSetStrokeColorWithColor(context, [[UIColor blackColor] CGColor]);
CGContextSetLineWidth(context, 1);
int radius = CORNER_RADIUS;
CGContextMoveToPoint(context, 0, self.frame.size.height / 2);
CGContextAddLineToPoint(context, POINT_WIDTH, self.frame.size.height);
CGContextAddLineToPoint(context, rect.origin.x + rect.size.width - radius,
rect.origin.y + rect.size.height);
CGContextAddArc(context, rect.origin.x + rect.size.width - radius,
rect.origin.y + rect.size.height - radius, radius, M_PI / 2, 0.0f, 1);
CGContextAddLineToPoint(context, rect.origin.x + rect.size.width, rect.origin.y + radius);
CGContextAddArc(context, rect.origin.x + rect.size.width - radius, rect.origin.y + radius,
radius, 0.0f, -M_PI / 2, 1);
CGContextAddLineToPoint(context, POINT_WIDTH, 0);
CGContextAddLineToPoint(context, 0, self.frame.size.height / 2);
CGContextClosePath(context);
CGContextDrawPath(context, kCGPathFillStroke);
Why is it not drawing a border around the shape?
EDIT: Now it draws the shape, thanks to changing the last line, but it draws an extra line around it, like this:
http://cl.ly/0u0e051a060m161u0n08
Use CGContextDrawPath(context, kCGPathFillStroke) instead of CGContextFillPath() and CGContextStrokePath().
The thing is, after the first call to CGContextFillPath() or CGContextStrokePath() the context's path is cleared, so the second call does not have a path to work with.

UIPopover in ipad

I would like to create something like this:
The copy, star, reply, details. Is this just a UIPopover with a segmented button on it?
Here's another example of what I want:
Anyone got something like this?
The class you are looking for is UIMenuController
This SO page has a lot of useful information.
No. Ive been trying to recreate that view with a UIPopoverViewcontroller, holding a UIViewController with a UISegment.. and Im not getting that same functionality. The main issue is the UIPopoverVC is giving me a 3d border around my viewcontroller/view.
It does appear that its a custom view. Which, on that note, would not be too difficult to create by the way, and that view can host your SegmentedViewController, just not in a UIPopoverVC.
And.. for your viewing enjoyment... here is the code that creates the CGPath for a "Balloon" view like the Popover creates. this code goes in your drawRect.
float radius=5; //corner radius
CGContextSaveGState(context);
CGContextBeginPath(context);
CGContextSetRGBFillColor(context, backColor.red,backColor.green,backColor.blue,backColor.alpha);
CGContextMoveToPoint(context, CGRectGetMinX(rect) + radius, CGRectGetMinY(rect));
CGContextAddArc(context, CGRectGetMaxX(rect) - radius, CGRectGetMinY(rect) + radius, radius, 3 * M_PI / 2, 0, 0);
CGContextAddArc(context, CGRectGetMaxX(rect) - radius, CGRectGetMaxY(rect) - radius, radius, 0, M_PI / 2, 0);
CGContextAddArc(context, CGRectGetMinX(rect) + radius, CGRectGetMaxY(rect) - radius, radius, M_PI / 2, M_PI, 0);
CGContextAddArc(context, CGRectGetMinX(rect) + radius, CGRectGetMinY(rect) + radius, radius, M_PI, 3 * M_PI / 2, 0);
CGContextMoveToPoint(context, rect.size.width/2, rect.size.height);
CGContextAddLineToPoint(context, rect.size.width/2, rect.size.height+10);
CGContextAddLineToPoint(context, rect.size.width/2+10, rect.size.height);
CGContextClosePath(context);
CGContextFillPath(context);
CGContextRestoreGState(context);

Corners from CGContextAddArc are too dark

i have little problem with my code. I want to add a footer to a uitableview-section, but that's not my problem.
My problem is that i want to have rounded corners on my footer-view, but not on all corners, just the two on the bottom. But the corners are just too dark and i don't know why...
Heres my code:
- (void)drawRect:(CGRect)rect {
float radius = 10.0f;
CGContextRef context = UIGraphicsGetCurrentContext();
CGFloat strokeColorArray[4] = {0.35f,0.35f,0.35f,1.0f};
CGContextSetStrokeColor(context, strokeColorArray);
CGContextSetRGBFillColor(context, 1.0, 1.0, 1.0, 1);
CGContextSetLineWidth(context, 1.0);
CGContextBeginPath(context);
CGContextMoveToPoint(context, CGRectGetMinX(rect), CGRectGetMinY(rect));
CGContextAddLineToPoint(context, CGRectGetMaxX(rect), CGRectGetMinY(rect));
CGContextAddArc(context, CGRectGetMaxX(rect) - radius, CGRectGetMaxY(rect) - radius, radius, 0, M_PI / 2, 0);
CGContextAddArc(context, CGRectGetMinX(rect) + radius, CGRectGetMaxY(rect) - radius, radius, M_PI / 2, M_PI, 0);
CGContextClosePath(context);
CGContextFillPath(context);
CGContextMoveToPoint(context, CGRectGetMaxX(rect), CGRectGetMinY(rect));
CGContextAddArc(context, CGRectGetMaxX(rect) - radius, CGRectGetMaxY(rect) - radius, radius, 0, M_PI / 2, 0);
CGContextAddArc(context, CGRectGetMinX(rect) + radius, CGRectGetMaxY(rect) - radius, radius, M_PI / 2, M_PI, 0);
CGContextAddLineToPoint(context, CGRectGetMinX(rect), CGRectGetMinY(rect));
CGContextDrawPath(context, kCGPathStroke);
}
Here's a picture of my problem:
http://img195.imageshack.us/img195/9834/bildkw.png
Can you see the darker corners? How can i fix that?
Thank you
Sebastian
There's a couple things you can try. One is to change the radius of the corners. Another is to add some code to make sure that you draw lines on the inside of the rectangle and instead of centering the lines on the edges. To do that, you would have to inset the rect by half of the line width, or 0.5 pixels in this case.

How can I clear the corners outside of a rounded rect using the iPhone SDK?

I am attempting to implement a custom view. This view should display an image surrounded by a gray, rounded rect border. I can get the image to display fine, as well as the border, however, since the border has rounded corners, I need a way to clear those corners such that they correctly display whatever is behind the view. How can I accomplish this?
It seems like I might be able to use CGContextClearRect, but then wouldn't I have to call this multiple times, reconstructing the area outside of my rounded corner? That sounds overly complicated.
Is there a better way to create this view?
Here is my current code:
- (void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
// Draw the image. This will completely fill the current rect.
[image drawInRect:self.bounds];
// Ensure we draw completely within our bounds instead of straddling it.
CGRect rrect = self.bounds;
rrect.size.height = rrect.size.height - 1.0;
rrect.size.width = rrect.size.width - 1.0;
rrect.origin.x = rrect.origin.x + (1.0 / 2);
rrect.origin.y = rrect.origin.y + (1.0 / 2);
CGFloat radius = 5.0;
CGFloat minx = CGRectGetMinX(rrect);
CGFloat midx = CGRectGetMidX(rrect);
CGFloat maxx = CGRectGetMaxX(rrect);
CGFloat miny = CGRectGetMinY(rrect);
CGFloat midy = CGRectGetMidY(rrect);
CGFloat maxy = CGRectGetMaxY(rrect);
// Draw the rounded rect border.
CGContextSetRGBStrokeColor(context, 0.6, 0.6, 0.6, 1.0);
CGContextSetRGBFillColor(context, 1.0, 1.0, 1.0, 0.0);
CGContextSetLineWidth(context, 1.0);
CGContextMoveToPoint(context, minx, midy);
CGContextAddArcToPoint(context, minx, miny, midx, miny, radius);
CGContextAddArcToPoint(context, maxx, miny, maxx, midy, radius);
CGContextAddArcToPoint(context, maxx, maxy, midx, maxy, radius);
CGContextAddArcToPoint(context, minx, maxy, minx, midy, radius);
CGContextClosePath(context);
CGContextDrawPath(context, kCGPathFillStroke);
}
Add the rounded-rect path to the clipping path before drawing the image.
Last week I was trying to do something similar.
With your question, the answer and other Q&A I created an example with a solution.
https://github.com/GabrielMassana/TransparentRoudRectButton