For those times when a UISlider just isn't fun enough I was thinking I'd work up a custom UIControl to do something similar to a Google O Meter from the google chart API on iOS.
Here's an example of what I'm talking about visually. My goal would be to enable touch manipulation of the needle and easy customization of the range labels and coloring gradients. The Google Charts API is a great inspiration:
Before I re-invent the wheel is anyone aware of an existing effort I could leverage good sample code with something like this or project I could contribute too?
Thanks for any pointers.
I have implemented a GaugeView as a child of UIView and added the code to draw the gauge in the drawRect method. Nothing fancy but may be helpful to start
- (void)drawRect:(CGRect)rect {
[super drawRect:rect];
CGRect parentViewBounds = self.bounds;
CGFloat x = CGRectGetWidth(parentViewBounds)/2;
CGFloat y = CGRectGetHeight(parentViewBounds);
CGFloat radius = y;
CGFloat percent_angle = _progress * M_PI;
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextBeginPath(ctx);
// Add path
CGFloat m2_x = radius * (1.0 - 0.9*cos(percent_angle));
CGFloat m2_y = radius * (1.0 - 0.9*sin(percent_angle));
CGContextBeginPath(ctx);
CGContextAddArc(ctx, x, y, radius, M_PI, 0, 0);
CGContextAddLineToPoint(ctx, x + 0.1*radius, y);
CGContextAddArc(ctx, x, y, 0.1*radius, 0, M_PI, 1);
CGContextClosePath(ctx);
CGContextSetFillColorWithColor(ctx,[UIColor whiteColor].CGColor);
CGContextFillPath(ctx);
CGContextBeginPath(ctx);
CGContextMoveToPoint(ctx, m2_x, m2_y);
CGContextAddArc(ctx, x, y, 0.9*radius, M_PI + percent_angle, M_PI, 1);
CGContextAddLineToPoint(ctx, 0.8*radius, y);
CGContextSetStrokeColorWithColor(ctx,self.fillColor.CGColor);
CGContextAddArc(ctx, x, y, 0.2*radius, M_PI, M_PI + percent_angle, 0);
CGContextClosePath(ctx);
CGContextStrokePath(ctx);
CGContextBeginPath(ctx);
CGContextMoveToPoint(ctx, m2_x, m2_y);
CGContextSetFillColorWithColor(ctx,self.fillColor.CGColor);
CGContextAddArc(ctx, x, y, 0.9*radius, M_PI + percent_angle, M_PI, 1);
CGContextAddLineToPoint(ctx, 0.8*radius, y);
CGContextAddArc(ctx, x, y, 0.2*radius, M_PI, M_PI + percent_angle, 0);
CGContextClosePath(ctx);
CGContextFillPath(ctx);
/* CGContextBeginPath(ctx);
CGContextAddArc(ctx, x, y, radius, M_PI, 0, 0);
CGContextAddLineToPoint(ctx, x + 0.1*radius, y);
CGContextAddArc(ctx, x, y, 0.1*radius, 0, M_PI, 1);
CGContextClosePath(ctx);
CGContextSetStrokeColorWithColor(ctx,[self.strokeColor CGColor]);
CGContextStrokePath(ctx);*/
for(int i = 0; i < 100; i++)
{
CGFloat angle =M_PI * 0.01 * i;
CGFloat l1_x = radius * (1.0 - 0.98*cos(angle));
CGFloat l1_y = radius * (1.0 - 0.98*sin(angle));
if(i%10 == 0)
{
l1_x = radius * (1.0 - 0.9*cos(angle));
l1_y = radius * (1.0 - 0.9*sin(angle));
}
else if(i%5 == 0)
{
l1_x = radius * (1.0 - 0.95*cos(angle));
l1_y = radius * (1.0 - 0.95*sin(angle));
}
CGFloat l2_x = radius * (1.0 - cos(angle));
CGFloat l2_y = radius * (1.0 - sin(angle));
CGContextMoveToPoint(ctx, l1_x, l1_y);
CGContextSetLineWidth(ctx, 1.0);
CGContextAddLineToPoint(ctx, l2_x, l2_y);
CGContextSetStrokeColorWithColor(ctx,[self.strokeColor CGColor]);
CGContextStrokePath(ctx);
}
CGFloat n1_x = radius * (1.0 - 0.1*cos(percent_angle));
CGFloat n1_y = radius * (1.0 - 0.1*sin(percent_angle));
CGFloat n2_x = radius * (1.0 - cos(percent_angle));
CGFloat n2_y = radius * (1.0 - sin(percent_angle));
CGContextMoveToPoint(ctx, n1_x, n1_y);
CGContextSetLineWidth(ctx, 4.0);
CGContextAddLineToPoint(ctx, n2_x, n2_y);
CGContextSetStrokeColorWithColor(ctx,[self.needleColor CGColor]);
CGContextStrokePath(ctx);
}
Related
I have made a subclass of UIView and I am trying to draw part of a circle in my drawRect method.
I have tried using bezierPathWithArcCenter and filling it but that only result in a pie shape (image 3) and that's not what i'm after. I want to draw what you see in image 1 and 2.
Maybe I can clip a full circle somehow? The area around the circle needs to be transparent.
TompaLompas answer pointed me in the right direction (with the arc drawing part). However the complete solution and answer is like this:
#define DEGREES_TO_RADIANS(degrees) ((M_PI * degrees)/ 180)
- (void)drawRect:(CGRect)rect
{
[super drawRect:rect];
CGContextRef ctx = UIGraphicsGetCurrentContext();
int radius = self.frame.size.width / 2;
CGPoint center = CGPointMake(self.frame.size.width / 2, self.frame.size.height / 2);
//Image 2
CGContextSetFillColorWithColor(ctx, [UIColor redColor].CGColor);
CGContextAddArc(ctx, center.x, center.y, radius, DEGREES_TO_RADIANS(225), DEGREES_TO_RADIANS(315), NO);
CGContextDrawPath(ctx, kCGPathFill);
}
try overriding drawRect with this:
- (void)drawRect:(CGRect)rect
{
[super drawRect:rect];
CGContextRef context = UIGraphicsGetCurrentContext();
float radius = 50.0f;
float x_left = rect.origin.x;
float x_left_center = x_left + radius;
float y_top = rect.origin.y;
float y_top_center = y_top + radius;
/* Begin path */
CGFloat white[4] = {0.0f, 204.0f/255.0f, 1.0f, 0.8f};
CGContextSetFillColor(context, white);
CGContextSetLineWidth(context, 1.0);
CGContextBeginPath(context);
CGContextMoveToPoint(context, x_left, y_top_center);
CGContextAddArcToPoint(context, x_left, y_top, x_left_center, y_top, radius);
CGContextAddLineToPoint(context,x_left, y_top + radius);
CGContextFillPath(context);
}
It will draw a rotated image number 2
I want to make UIView which is originally rectangular to Hexagon shape to be used in my application.
Please help me something on this.
Thanks in advance.
Subclass UView and override drawRect to draw a hexagon like so:
- (void)drawRect:(CGRect)rect
{
float polySize = 60; // change this
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSaveGState(context);
CGAffineTransform t0 = CGContextGetCTM(context);
t0 = CGAffineTransformInvert(t0);
CGContextConcatCTM(context, t0);
//Begin drawing setup
CGContextBeginPath(context);
CGContextSetRGBStrokeColor(context, 0, 0, 0, 1);
CGContextSetLineWidth(context, 2.0);
CGPoint center;
//Start drawing polygon
center = CGPointMake(160, 90.0);
CGContextMoveToPoint(context, center.x, center.y + polySize);
for(int i = 1; i < 6; ++i)
{
CGFloat x = polySize * sinf(i * 2.0 * M_PI / 6);
CGFloat y = polySize * cosf(i * 2.0 * M_PI / 6);
CGContextAddLineToPoint(context, center.x + x, center.y + y);
}
//Finish Drawing
CGContextClosePath(context);
CGContextDrawPath(context, kCGPathStroke);
CGContextRestoreGState(context);
}
You can create a subclass of UIView and in it's -(void)drawRect: method draw a hexagon. Or Use UIImageView with image of hexagon
I want to draw a circle in drawRect through a context like a pie chart thorugh UITouch (took from a tutorial)? I have given the code as follows, how can I rotate? Any help please?
#define PI 3.14159265358979323846
#define snapshot_start 360
#define snapshot_finish 360
static inline float radians(double degrees) { return degrees * PI / 180; }
- (void)drawRect:(CGRect)rect
{
// Drawing code
CGRect parentViewBounds = self.bounds;
CGFloat x = CGRectGetWidth(parentViewBounds)/2;
CGFloat y = CGRectGetHeight(parentViewBounds)*0.55;
// Get the graphics context and clear it
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextClearRect(ctx, rect);
// define stroke color
CGContextSetRGBStrokeColor(ctx, 1, 1, 1, 1.0);
// define line width
CGContextSetLineWidth(ctx, 4.0);
// need some values to draw pie charts
double snapshotCapacity =20;
double rawCapacity = 100;
double systemCapacity = 1;
int offset = 5;
double pie1_start = 315.0;
double pie1_finish = snapshotCapacity *360.0/rawCapacity;
double system_finish = systemCapacity*360.0/rawCapacity;
CGContextSetFillColor(ctx, CGColorGetComponents( [[UIColor greenColor] CGColor]));
CGContextMoveToPoint(ctx, x+2*offset, y);
CGContextAddArc(ctx, x+2*offset, y, 100, radians(snapshot_start), radians(snapshot_start+snapshot_finish), 0);
CGContextClosePath(ctx);
CGContextFillPath(ctx);
// system capacity
CGContextSetFillColor(ctx, CGColorGetComponents( [[UIColor colorWithRed:15 green:165/255 blue:0 alpha:1 ] CGColor]));
CGContextMoveToPoint(ctx, x+offset,y);
CGContextAddArc(ctx, x+offset, y, 100, radians(snapshot_start+snapshot_finish+offset), radians(snapshot_start+snapshot_finish+system_finish), 0);
CGContextClosePath(ctx);
CGContextFillPath(ctx);
/* data capacity */
CGContextSetFillColor(ctx, CGColorGetComponents( [[UIColor colorWithRed:99/255 green:184/255 blue:255/255 alpha:1 ] CGColor]));
CGContextMoveToPoint(ctx, x, y);
CGContextAddArc(ctx, x, y, 100, radians(snapshot_start+snapshot_finish+system_finish+offset), radians(snapshot_start), 0);
CGContextClosePath(ctx);
CGContextFillPath(ctx);
}
Whats wrong with this code?
-(void) drawRect:(CGRect) rect {
CGContextRef c = UIGraphicsGetCurrentContext();
if (c != nil) {
CGContextSetFillColorWithColor(c, self.cornerColor.CGColor);
[self drawRoundedCornersInRect:self.bounds inContext:c];
CGContextFillPath(c);
}
}
-(void) drawCornerInContext:(CGContextRef)c cornerX:(int) x cornerY:(int) y
arcEndX:(int) endX arcEndY:(int) endY {
CGContextMoveToPoint(c, x, endY);
CGContextAddArcToPoint(c, x, y, endX, y, radius);
CGContextAddLineToPoint(c, x, y);
CGContextAddLineToPoint(c, x, endY);
}
-(void) drawRoundedCornersInRect:(CGRect) rect inContext:(CGContextRef) c {
int x_left = rect.origin.x;
int x_left_center = rect.origin.x + radius;
int x_right_center = rect.origin.x + rect.size.width - radius;
int x_right = rect.origin.x + rect.size.width;
int y_top = rect.origin.y;
int y_top_center = rect.origin.y + radius;
int y_bottom_center = rect.origin.y + rect.size.height - radius;
int y_bottom = rect.origin.y + rect.size.height;
if (roundUpperLeft) {
[self drawCornerInContext:c cornerX: x_left cornerY: y_top
arcEndX: x_left_center arcEndY: y_top_center];
}
if (roundUpperRight) {
[self drawCornerInContext:c cornerX: x_right cornerY: y_top
arcEndX: x_right_center arcEndY: y_top_center];
}
if (roundLowerRight) {
[self drawCornerInContext:c cornerX: x_right cornerY: y_bottom
arcEndX: x_right_center arcEndY: y_bottom_center];
}
if (roundLowerLeft) {
[self drawCornerInContext:c cornerX: x_left cornerY: y_bottom
arcEndX: x_left_center arcEndY: y_bottom_center];
}
}
No errors, No warnings ... but the round corners don't work at all. I found this code here.
You need to begin a path, and close it when you're done. Your 2nd call to CGContextAddLineToPoint messes things up I think. Here's a snippet that works. Study it and enhance it to support your multiple cases (it seems that you want to be able to round only some corners, and not necessarily all of them...)
void addRoundedRect(CGContextRef ctx, CGRect rect, float cornerRadius) {
if (cornerRadius <= 2.0) {
CGContextAddRect(ctx, rect);
} else {
float x_left = rect.origin.x;
float x_left_center = x_left + cornerRadius;
float x_right_center = x_left + rect.size.width - cornerRadius;
float x_right = x_left + rect.size.width;
float y_top = rect.origin.y;
float y_top_center = y_top + cornerRadius;
float y_bottom_center = y_top + rect.size.height - cornerRadius;
float y_bottom = y_top + rect.size.height;
/* Begin path */
CGContextBeginPath(ctx);
CGContextMoveToPoint(ctx, x_left, y_top_center);
/* First corner */
CGContextAddArcToPoint(ctx, x_left, y_top, x_left_center, y_top, cornerRadius);
CGContextAddLineToPoint(ctx, x_right_center, y_top);
/* Second corner */
CGContextAddArcToPoint(ctx, x_right, y_top, x_right, y_top_center, cornerRadius);
CGContextAddLineToPoint(ctx, x_right, y_bottom_center);
/* Third corner */
CGContextAddArcToPoint(ctx, x_right, y_bottom, x_right_center, y_bottom, cornerRadius);
CGContextAddLineToPoint(ctx, x_left_center, y_bottom);
/* Fourth corner */
CGContextAddArcToPoint(ctx, x_left, y_bottom, x_left, y_bottom_center, cornerRadius);
CGContextAddLineToPoint(ctx, x_left, y_top_center);
/* Done */
CGContextClosePath(ctx);
}
}
I use this: [view.layer setCornerRadius:7];. It sets the corner radius using the UIView's CALayer backer.
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