iOS drawRect with custom height - ios

I have placed a view on a viewController and I want to draw a rectangle in this view, but with a custom height. I determine the height based on a parameter in my viewController.
So for example. If my parameter is 50, I want the rectangle to have the height of 50% of the UIView.
2 questions:
how can I pass the height to the custom drawRect?
how do I make the rectangle be placed on the bottom of the UIView?
I have placed the view using Interface Builder and I have implemented the drawRect in a subclass of UIView and used this as Custom Class for my UIView.
So in the custom class I have:
- (void)drawRect:(CGRect)rect
{
// Drawing code
CGContextRef context = UIGraphicsGetCurrentContext();
UIColor * lightGrayColor = [UIColor colorWithRed:230.0/255.0 green:230.0/255.0 blue:230.0/255.0 alpha:1.0];
CGRect paperRect = self.bounds;
drawLinearGradient(context, paperRect, lightGrayColor.CGColor, [UIColor clearColor].CGColor);
}
void drawLinearGradient(CGContextRef context, CGRect rect, CGColorRef startColor, CGColorRef endColor)
{
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGFloat locations[] = { 0.0, 1.0 };
NSArray *colors = #[(__bridge id) startColor, (__bridge id) endColor];
CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, (__bridge CFArrayRef) colors, locations);
CGPoint startPoint = CGPointMake(CGRectGetMidX(rect), CGRectGetMinY(rect));
CGPoint endPoint = CGPointMake(CGRectGetMidX(rect), CGRectGetMaxY(rect));
CGContextSaveGState(context);
CGContextAddRect(context, rect);
CGContextClip(context);
CGContextDrawLinearGradient(context, gradient, startPoint, endPoint, 0);
CGContextRestoreGState(context);
CGGradientRelease(gradient);
CGColorSpaceRelease(colorSpace);
}
This draws a nice gradient rectangle in my view but it fills the complete UIView.
This is because of the self.bounds.
I have added a property *height to my custom class, but I don't know how to fill this from my viewController. So I want it to start at the bottom off the UIView, and make it as high as I have determined (a % of the real height).
Anybody knows how I can achieve this?

have you set your custom view class in identity inspector in interface builder?
you can set the height property from your viewController:
((MyViewClass *) self.myView).height = myCalculatedValue;
then implement drawRect:
- (void)drawRect:(CGRect)rect
{
// Drawing code
CGContextRef context = UIGraphicsGetCurrentContext();
UIColor * lightGrayColor = [UIColor colorWithRed:230.0/255.0 green:230.0/255.0 blue:230.0/255.0 alpha:1.0];
CGRect paperRect = self.bounds;
paperRect.origin.y = paperRect.size.height - self.height;
paperRect.size.height = self.height;
//assuming your height property is of CGFloat type
drawLinearGradient(context, paperRect, lightGrayColor.CGColor, [UIColor clearColor].CGColor);
}
this will draw your gradient from (height) points above the bottom to the bottom

I think you can get calculate 50% of screen height and then substract new view height from the full screen Height
youParameter = 50; // Lets assume this is your parametere
int heightOfView = ([UIScreen mainScreen].bounds.size.height * yourParameter) / 100;
// For placing the view to the bottom;
CGRect newFrame;
frame.origin.y = [UIScreen mainScreen].bounds.size.height - heightOfView;
frame.origin.x = 0;
frame.size.width = [UIScreen mainScreen].bounds.size.width; // assuming it will take full width
frame.size.height = heightOfView;
youViewToChange.frame = newFrame; // normally do this or
To pass the value to drawRect you can do:
[self drawRect:newFrame];

Related

create quarter transparent hole at right bottom on overlay UIView

Hi i want to create a quarter transparent hole at right bottom on overlay UIView.
i am able to solve it using below code. But it does not look right as i am creating a rectangle outside the bond of view.
What i have tried:
#implementation PartialTransparentView
- (id)initWithBottomRightCornerRadiusForView:(UIView *)view withRadius:(CGFloat)radius
{
[self commonInitWithRect:CGRectMake(view.frame.size.width - radius, view.frame.size.height - radius, radius*2, radius*2)];
self = [super initWithFrame:CGRectMake(0, 0, 5000, 5000)];//**it does not look right to me**
if (self) {
// Initialization code
self.opaque = NO;
}
return self;
}
-(void)commonInitWithRect:(CGRect)rect{
backgroundColor = [UIColor colorWithWhite:1 alpha:0.75];
rectToBeSurrounded = rect;
}
- (void)drawRect:(CGRect)rect {
[backgroundColor setFill];
UIRectFill(rect);
CGFloat x = rectToBeSurrounded.origin.x;
CGFloat y = rectToBeSurrounded.origin.y;
CGFloat width = rectToBeSurrounded.size.width;
CGFloat height = rectToBeSurrounded.size.height;
//create outer square
CGFloat outerX = (x - width/2);
CGFloat outerY = y - height/2;
CGFloat outerWidth = 2*width;
CGFloat outerHeight = outerWidth;
//create outer square
CGRect outerRect = CGRectMake(outerX, outerY, outerWidth, outerHeight);
CGRect holeRectIntersection = CGRectIntersection( outerRect, rect );
CGContextRef context = UIGraphicsGetCurrentContext();
if( CGRectIntersectsRect( holeRectIntersection, rect ) )
{
CGContextAddEllipseInRect(context, holeRectIntersection);
CGContextClip(context);
CGContextClearRect(context, holeRectIntersection);
CGContextSetFillColorWithColor( context, [UIColor clearColor].CGColor );
CGContextFillRect( context, holeRectIntersection);
}
}
Now i am using above code as :
PartialTransparentView *transparentView = [[PartialTransparentView alloc] initWithBottomRightCornerRadiusForView:self.view withRadius:50];
[self.view addSubview:transparentView];
Result as expected:
i know my solution will break if i have to acheive same thing but on top left of view.
what i am looking for just provide center (x, y) and radius for circle and get desired results.
Thanks
Basd on Mr.T
UIView *transparentView = [[UIView alloc] initWithFrame:self.view.frame];
[transparentView setBackgroundColor:[UIColor colorWithWhite:1 alpha:0.75]];
[self.view addSubview:transparentView];
circleView *acircleView = [[circleView alloc] initWithFrame:CGRectMake(50, 50, 60, 60)];
[acircleView setBackgroundColor:[UIColor grayColor]];
[transparentView addSubview:acircleView];
and circleView.m
- (void)drawRect:(CGRect)rect {
// Drawing code
//// Oval Drawing
UIBezierPath* ovalPath = [UIBezierPath bezierPathWithOvalInRect: CGRectMake(50, 50, 60, 60)];
[UIColor.grayColor setFill];
[ovalPath fill];
}
output:
My suggestions is to add the transparent view as a separate view on your view controller. This can be either done on storyboard,so that you can set the background color and the alpha value to give the transparent effect!!!
Now create another view to make the circle and add it to transparent view, , and move this view on the transparent view according to your need!!!
Create the circle using bezier path:
circleView.m
- (void)drawRect:(CGRect)frame {
//// Oval Drawing
UIBezierPath* ovalPath = [UIBezierPath bezierPathWithOvalInRect: CGRectMake(CGRectGetMinX(frame), CGRectGetMinY(frame), 60, 60)];
[UIColor.grayColor setFill];
[ovalPath fill];
}
For testing purpose, I have created a circle view on my IB and created an outlet property in my view controller.
HEre is the screenshot.
Now to move the circle, I can simply change the frame of the circle view, wherever I need.
For example, If I want to move it to top left, I simply do:
-(void)moveCircleViewwithX:(float) x withY:(float) y{
_cView.frame=CGRectMake(x, y, _cView.frame.size.width, _cView.frame.size.height);
}
The result will be:
Update
Put the following the drawRect method of transparentView:
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGRect transparentPart = self.seeRect; //this is the rect of the circle view
CGContextAddEllipseInRect(ctx,transparentPart); //make the circle shape
CGContextClip(ctx);
CGContextClearRect(ctx, transparentPart);
and in your view controller:
when you want to apply the mask i.e the transparent for both circle and for the transparent layer:
-(void)applyMask{
[_cView setCircleColor:[UIColor clearColor]]; //circle view bezier path color
[_cView setNeedsDisplay];
[_tView setSeeRect:_cView.frame]; //set the transparency cut on transparency view
[_tView setNeedsDisplay];
}
Once you do this, you will get the transparency view!!!
You can move the circle by simply calling
[self moveCircleViewwithX:-30 withY:10]; //top left corner
and you can apply the transparency mask by simply calling:
[self applyMask];
So, the final result after you call the applyMask method will be:

How to add an UIActivityIndicator into a drawRect

I want to know how could I add an activity indicator (in the middle of a HUD) to this drawrect method (i'm trying to create a hud with the activity on it)
- (void)drawRect:(CGRect)rect {
// Sets the rectangle to be 96 X 96.
const CGFloat boxWidth = 120.0f;
const CGFloat boxHeight = 120.0f;
// This method is used to calculate the position of the rectangle.
CGRect boxRect = CGRectMake( roundf(self.bounds.size.width - boxWidth) / 2.0f, roundf(self.bounds.size.height - boxHeight) / 2.0f, boxWidth,boxHeight);
// This draws the rectangle with rounded corners.
UIBezierPath *roundedRect = [UIBezierPath bezierPathWithRoundedRect:boxRect cornerRadius:10.0f];
[[UIColor colorWithWhite:0.0f alpha:0.75] setFill];
[roundedRect fill];
[[UIColor whiteColor] set]; // Sets the color of the font to white.
UIFont *font = [UIFont boldSystemFontOfSize:16.0f]; // Sets the size of the font to 16.
CGSize textSize = [self.text sizeWithFont:font];
// Calculates where to draw the text.
CGPoint textPoint = CGPointMake( self.center.x - roundf(textSize.width / 2.0f), self.center.y - roundf(textSize.height / 2.0f) + boxHeight / 4.0f);
// Draws the text on the rectangle.
[self.text drawAtPoint:textPoint withFont:font];
}
Thanks!
You don't. Activity indicator is a type of view, so you'd add it as a subview of your HUD and it will draw itself.

Can't draw in UITableViewCell's drawRect

I'm having trouble drawing in the drawRect method of my custom UITableViewCell. Here is the code I am using
- (void)drawRect:(CGRect)rect
{
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGPoint origin = _faceView.frame.origin;
CGFloat width = _faceView.frame.size.width;
CGFloat height = _faceView.frame.size.height;
CGFloat border = 2.0f;
CGPoint startPt = CGPointMake(origin.x + width/2, self.frame.size.height);
CGContextBeginPath(ctx);
CGContextMoveToPoint(ctx, startPt.x, startPt.y);
CGPoint basePt = CGPointMake(startPt.x, origin.y - height - border);
CGContextAddLineToPoint(ctx, basePt.x, basePt.y);
CGRect circleRect = CGRectMake(origin.x - border, origin.y - border, width + 2 * border, height + 2 * border);
CGContextAddEllipseInRect(ctx, circleRect);
UIColor *color = [UIColor redColor];
CGContextSetFillColorWithColor(ctx, color.CGColor);
CGContextSetStrokeColorWithColor(ctx, color.CGColor);
CGContextSetLineWidth(ctx, 1.0f);
CGContextDrawPath(ctx, kCGPathFillStroke);
}
I've debugged to make sure that all of the numeric values make sense and it appears that they do. Can't really find out why nothing is being drawn on screen.
For what its worth, this is a cell defined in a nib as well. And i'm building with the iOS 7 sdk.
Any ideas?
tahnks
Try set backgroungcolor property to transparent color
self.backgroundColor = [UIColor clearColor]
You probably shouldn't do this in UITableViewCell's own drawRect. Instead, create a custom UIView and add it as a subview.
See also this answer.
You have to set
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
.....
[cell setNeedsDisplay];
return cell;
}
Try set backgroundColor of contentView property to clear color
self.contentView = [UIColor clearColor];

Infinitely scrollable UIView for a graph

I'm relatively new to graphics. This block of code simply draws graph nodes and edges in a UIView. This code works fine for a small number of nodes, but takes a 10+ seconds to load when the number of nodes and edges exceeds 200 in a UIView that is equaly to the frame of the iPad. Am I drawing inefficiently? My goal is to create an 'infinite' scrollable/zoomable graph view where nodes and edges are loaded only when they become visible. I believe that UIView dimensions are limited, so inserting an extremely large UIView into a UIScrollView will not work.
Currently, I am creating a UIView that is slightly larger than the dimensions of the iPad and placing that within a UIScrollView. All nodes and edges are being rendered, even if they are not visible. This is most likely inefficient.
How can I minimize time drawing? How can I create an infinite scrollable/zoomable view?
Thanks
A sample of the drawing:
UIView Code:
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSaveGState(context);
//Set background
CGContextSetFillColorWithColor(context, [UIColor whiteColor].CGColor);
CGContextFillRect(context, self.bounds);
//Translate the coordinate system relative to the minimum and maximum points in the graph
CGContextTranslateCTM(context, -graph.minX + MAP_BORDER_WIDTH, -graph.minY + MAP_BORDER_HEIGHT);
if (graph) {
//Draw all edges in graph
for (FNETEdge *edge in graph.edges) {
[self drawEdge:edge
inRect:rect];
}
//Draw all nodes in graph
for (FNETNode *node in graph.nodes) {
[self drawNode:node
inRect:rect];
}
}
CGContextRestoreGState(context);
}
- (void)drawNode:(FNETNode*)node inRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSaveGState(context);
//Get the size of the text
NSMutableAttributedString *text = node.getAttributedText;
CGSize maxTextSize = [text boundingRectWithSize:CGSizeMake(0, 0) options:NSStringDrawingUsesLineFragmentOrigin context:nil].size;
//Find the width and height of the node
CGFloat width = NODE_PADDING + node.icon.size.width + NODE_SPACING + maxTextSize.width + NODE_PADDING;
CGFloat height = node.icon.size.height > maxTextSize.height ? (NODE_PADDING + node.icon.size.height + NODE_PADDING) : (NODE_PADDING + maxTextSize.height + NODE_PADDING);
//Top left corner of the node
CGPoint topLeftPoint = CGPointMake(node.center.x - width/2, node.center.y - height/2);
//Inner and outer rectangles for node
CGRect outerNodeRect = CGRectMake(topLeftPoint.x, topLeftPoint.y, width, height);
CGRect innerNodeRect = CGRectMake(topLeftPoint.x + NODE_PADDING, topLeftPoint.y + NODE_PADDING, width - (2 * NODE_PADDING), height - (2 * NODE_PADDING));
//Find where we will draw the icon and the text
CGRect iconRect = CGRectMake(innerNodeRect.origin.x, topLeftPoint.y + (height - node.icon.size.height) / 2, node.icon.size.width, node.icon.size.height);
CGRect textRect = CGRectMake(innerNodeRect.origin.x + node.icon.size.width + NODE_SPACING, innerNodeRect.origin.y, maxTextSize.width, maxTextSize.height);
//Fill background
[[node getDeviceColor] setFill];
UIBezierPath *roundedRect = [UIBezierPath bezierPathWithRoundedRect:outerNodeRect cornerRadius:8];
[roundedRect fillWithBlendMode: kCGBlendModeNormal alpha:1.0f];
//Draw icon
[node.icon drawInRect:iconRect];
//Draw text
CGContextSetFillColorWithColor(context, [UIColor blackColor].CGColor);
[text drawWithRect:textRect options:NSStringDrawingUsesLineFragmentOrigin context:nil];
CGContextRestoreGState(context);
}
- (void)drawEdge:(FNETEdge*)edge inRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSaveGState(context);
CGContextSetLineCap(context, kCGLineCapSquare);
CGContextSetStrokeColorWithColor(context, [edge getSpeedColor].CGColor);
CGContextSetLineWidth(context, EDGE_LINE_WIDTH);
//Iterate through all points and connect the dots!
for (int i = 0; i < edge.points.count -1; i++) {
CGPoint startPoint = ((NSValue*)[edge.points objectAtIndex:i]).CGPointValue;
CGPoint endPoint = ((NSValue*)[edge.points objectAtIndex:i+1]).CGPointValue;
CGContextMoveToPoint(context, startPoint.x, startPoint.y);
CGContextAddLineToPoint(context, endPoint.x, endPoint.y);
}
CGContextStrokePath(context);
CGContextRestoreGState(context);
}

Drawing Text on UILabel with a gradient image

I have an UILabel subclass that draws some text in drawinRect method using a pattern image. This pattern image is a gradient image I am creating.
-(void)drawRect:(CGRect)rect
{
UIColor *color = [UIColor colorWithPatternImage:[self gradientImage]];
[color set];
[someText drawInRect:rec withFont:font lineBreakMode:UILineBreakModeWordWrap alignment:someAlignment];
}
The gradient image method is
-(UIImage *)gradientImage
{
NSData *colorData = [[NSUserDefaults standardUserDefaults] objectForKey:#"myColor"];
UIColor *co = [NSKeyedUnarchiver unarchiveObjectWithData:colorData];
colors = [NSArray arrayWithObjects:co,co,[UIColor whiteColor],co,co,co,co,nil];
NSArray *gradientColors=colors;
CGFloat width;
CGFloat height;
CGSize textSize = [someText sizeWithFont:font];
width=textSize.width; height=textSize.height;
UIGraphicsBeginImageContext(CGSizeMake(width, height));
// get context
CGContextRef context = UIGraphicsGetCurrentContext();
// push context to make it current (need to do this manually because we are not drawing in a UIView)
UIGraphicsPushContext(context);
//draw gradient
CGGradientRef gradient;
CGColorSpaceRef rgbColorspace;
//set uniform distribution of color locations
size_t num_locations = [gradientColors count];
CGFloat locations[num_locations];
for (int k=0; k<num_locations; k++)
{
locations[k] = k / (CGFloat)(num_locations - 1); //we need the locations to start at 0.0 and end at 1.0, equaly filling the domain
}
//create c array from color array
CGFloat components[num_locations * 4];
for (int i=0; i<num_locations; i++)
{
UIColor *color = [gradientColors objectAtIndex:i];
NSAssert(color.canProvideRGBComponents, #"Color components could not be extracted from StyleLabel gradient colors.");
components[4*i+0] = color.red;
components[4*i+1] = color.green;
components[4*i+2] = color.blue;
components[4*i+3] = color.alpha;
}
rgbColorspace = CGColorSpaceCreateDeviceRGB();
gradient = CGGradientCreateWithColorComponents(rgbColorspace, components, locations, num_locations);
CGRect currentBounds = self.bounds;
CGPoint topCenter = CGPointMake(CGRectGetMidX(currentBounds), 0.0f);
CGPoint midCenter = CGPointMake(CGRectGetMidX(currentBounds), CGRectGetMidY(currentBounds));
CGContextDrawLinearGradient(context, gradient, topCenter, midCenter, 0);
CGGradientRelease(gradient);
CGColorSpaceRelease(rgbColorspace);
// pop context
UIGraphicsPopContext();
// get a UIImage from the image context
UIImage *gradientImage = UIGraphicsGetImageFromCurrentImageContext();
// clean up drawing environment
UIGraphicsEndImageContext();
return gradientImage;
}
Everything is working fine. The text is drawn with nice glossy effect. Now my problem is if I change the x or y position of the text inside the UILabel and then call the
[someText drawinRect:rec]... to redraw, the gloss effect is generated differently. It seems that the pattern image is changing with change of text position..
The following is the effect for the frame rec = CGRectMake(0, 10, self.frame.size.width, self.frame.size.height);
The following is the effect for the frame rec = CGRectMake(0, 40, self.frame.size.width, self.frame.size.height);
I hope I have explained my question well. Could not find any exact answer elsewhere. Thanks in advance.
You might want to look at using an existing 3rd party solution like FXLabel.

Resources