Im working on a project with out Interface Builder,
i want to draw a line and a test square, but is not working
here the code
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextClearRect(context, rect);
//Set the stroke (pen) color
CGContextSetStrokeColorWithColor(context, [UIColor blueColor].CGColor);
//Set the width of the pen mark
CGContextSetLineWidth(context, 5.0);
//Start at this point
//CGContextMoveToPoint(context, 10.0, 30.0);
CGContextMoveToPoint(context, 20, 20);
//Give instructions to the CGContext
//(move "pen" around the screen)
CGContextAddLineToPoint(context, 310.0, 30.0);
CGContextStrokePath(context);
// Draw a red solid square
CGContextSetRGBFillColor(context, 255, 0, 0, 1);
CGContextFillRect(context, CGRectMake(10, 10, 50, 50));
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 480)];
self.view.backgroundColor = [UIColor grayColor];
UILabel *labelA = [[[UILabel alloc] initWithFrame:CGRectMake(100, 100, 100, 20)]autorelease];
labelA.text = #"mk ss9";
[self.view addSubview:labelA];
}
the label shows fine, but the line and square are not showing, what is missing??
thanks a lot!
Ok got it!
the problem is that I was attempting to draw in my ViewController, and that is not the way,
So I created a new class, with subclass UIView, and it works fine now!
Related
I have an image of a clock hand. I am trying to created rotated versions of this image so that Ic an play it using animation (using UIImageView's animatedImages approach).
In the code below, I am seeing that if I apply a CGContextTranslateCTM to the image graphics context, the image is not drawn unless I increase the size I give to the UIGraphicsBeginImageContextWithOptions to make it bigger than image size.
Why is this needed? I would think that all translation does is to move the definition of how the drawAtPoint: interprets where 0,0 is?
**Edited Code showing more details*
-(void) animatedClock
{
UIGraphicsBeginImageContextWithOptions(CGSizeMake(20, 100), NO, 0);
UIBezierPath *clockHand = [[UIBezierPath alloc] init];
[[UIColor yellowColor] setFill];
[clockHand moveToPoint:CGPointMake(0, 20)];
[clockHand addLineToPoint:CGPointMake(10, 0)];
[clockHand addLineToPoint:CGPointMake(20, 20)];
[clockHand fill];
[clockHand removeAllPoints];
[clockHand moveToPoint:CGPointMake(5, 100)];
[clockHand addLineToPoint:CGPointMake(5, 20)];
[clockHand addLineToPoint:CGPointMake(15, 20)];
[clockHand addLineToPoint:CGPointMake(15, 100)];
[clockHand closePath];
[clockHand fill];
UIImage *baseImage = UIGraphicsGetImageFromCurrentImageContext();
NSLog(#"Image size is %#", NSStringFromCGSize(baseImage.size));
UIGraphicsEndImageContext();
//The screenshot will show baseimage, an arrow that is pointed upwards and at the top left of the screen
UIImageView *imageView = [[UIImageView alloc] initWithImage:baseImage];
UIGraphicsEndImageContext();
[self.view addSubview:imageView];
//This code is just to see where 100, 100 is on screen
UIView *redView = [[UIView alloc] initWithFrame:CGRectMake(100,100, 5, 5)];
redView.backgroundColor = [UIColor redColor];
[self.view addSubview:redView];
for (NSUInteger index = 0; index <= 8; index++)
{
//UIGraphicsBeginImageContextWithOptions(baseImage.size, NO, 0); //If I keep this line then nothing is shown
UIGraphicsBeginImageContextWithOptions(CGSizeMake(500, 600), NO, 0); // If I keep this line I can see some of teh rotations
CGContextRef con = UIGraphicsGetCurrentContext();
CGContextTranslateCTM(con, 100, 100); //I want the clock hands to be centered around 100, 100
CGContextRotateCTM(con, 45*index*M_PI/180);
[baseImage drawAtPoint:CGPointMake(0, 0)];
UIImageView *imageView = [[UIImageView alloc] initWithImage:UIGraphicsGetImageFromCurrentImageContext()];
UIGraphicsEndImageContext();
[self.view addSubview:imageView];
imageView.opaque = YES;
imageView.backgroundColor = [UIColor clearColor];
[self.view addSubview:imageView];
}
}
I am seeing that if I apply a CGContextTranslateCTM to the image graphics context, the image is not drawn
Of course. You draw at 0,0. But first you apply a translate CTM of 100,100. So you actually draw at 100,100. But the context is only 20 points wide. So you are drawing completely outside the context. There is no context outside the context, so nothing is drawn.
Based on #matt response, I did some experimentation. Running this code and the comments on what to expect may help some other beginners understand what is happening and how the drawing and translation interact.
#define CROSS_SIZE 10
-(void) conetxtTranslationDemos
{
UIGraphicsBeginImageContextWithOptions(CGSizeMake(CROSS_SIZE, CROSS_SIZE), NO, 0);
//Creates an image that looks like multiply symbol. Image is CROSS_SIZE by CROSS_SIZE wide
UIBezierPath *cross = [[UIBezierPath alloc] init];
[[UIColor redColor] set];
[cross moveToPoint:CGPointMake(0, CROSS_SIZE)];
[cross addLineToPoint:CGPointMake(CROSS_SIZE, 0)];
//[cross removeAllPoints];
[cross moveToPoint:CGPointMake(0, 0)];
[cross addLineToPoint:CGPointMake(CROSS_SIZE, CROSS_SIZE)];
[cross stroke];
UIImage *crossImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
NSLog(#"Image size is %#", NSStringFromCGSize(crossImage.size));
//If you keep this you will see a cross image in top left of screen
// UIImageView *imageView = [[UIImageView alloc] initWithImage:crossImage];
// [self.view addSubview:imageView];
UIImageView *imageView;
UIGraphicsBeginImageContextWithOptions(CGSizeMake(100, 100), NO, 0);
CGContextRef con = UIGraphicsGetCurrentContext();
//Results in all drawings being 40 points displaced downwards from top left of screen
CGContextTranslateCTM(con, 0, 40);
//Results in a multiply which has its top left at 20, 40 (40 due to above translation)
CGContextTranslateCTM(con, 20, 0);
[crossImage drawAtPoint:CGPointMake(0, 0)];
imageView = [[UIImageView alloc] initWithImage:UIGraphicsGetImageFromCurrentImageContext()];
[self.view addSubview:imageView];
//Results in a multiply which has its top left at 0, 40 (40 due to original translation)
CGContextTranslateCTM(con, -20, 0);
[crossImage drawAtPoint:CGPointMake(0, 0)];
imageView = [[UIImageView alloc] initWithImage:UIGraphicsGetImageFromCurrentImageContext()];
[self.view addSubview:imageView];
//Results in a multiply which has its top left at 40, 40+20/sqrt(2) (40 due to original translation)
CGContextTranslateCTM(con, 40, 20/sqrt(2));
[crossImage drawAtPoint:CGPointMake(0, 0)];
imageView = [[UIImageView alloc] initWithImage:UIGraphicsGetImageFromCurrentImageContext()];
[self.view addSubview:imageView];
//Undo above translation
CGContextTranslateCTM(con, -40, -20/sqrt(2));
//Results in a add symbol instead of multiply. This is because a point at (10, 10) is now (10, 10) in rotated axis, which is 0, 2*10/sqrt(2) in normal axis. That is why you see you will bottom enplane of the add co-incide with top left of cross drawn above at (40, 40+20/sqrt(2))
CGContextTranslateCTM(con, 40, 0);
CGContextRotateCTM(con, 45*M_PI/180);
[crossImage drawAtPoint:CGPointMake(0, 0)];
imageView = [[UIImageView alloc] initWithImage:UIGraphicsGetImageFromCurrentImageContext()];
[self.view addSubview:imageView];
}
I have a UIView that has 3 custom subviews whose class is called (for now) LVStemp and that represent nodes in a tree. An LVStemp's frame is larger than the node itself because it will also include some drawing that lies outside the node (not coded yet). LVStemp has a property called nodeFrame that represents the frame of the node itself, in the main view's coordinates.
I'm trying to draw the nodes and fill them with a gradient. But the gradient is clipping in a way that is not what I intend. The result looks like this:
The top node is filled correctly but the bottom two are not.
Setting up, in the UIView's superview:
- (void)viewDidLoad
{
[super viewDidLoad];
UIView *myView = [[UIView alloc] initWithFrame:CGRectMake(0, 100, 350, 350)];
[self.view addSubview:myView];
LVStemp *node1 = [[LVStemp alloc] init];
node1.frame = CGRectMake(75, 0, 50, 50);
node1.nodeFrame = node1.frame;
node1.backgroundColor = [UIColor clearColor];
[myView addSubview:node1];
LVStemp *node2 = [[LVStemp alloc] init];
node2.frame = CGRectMake(0, 25, 100, 175);
node2.nodeFrame = CGRectMake(0, 150, 50, 50);
node2.backgroundColor = [UIColor clearColor];
[myView addSubview:node2];
LVStemp *node3 = [[LVStemp alloc] init];
node3.frame = CGRectMake(100, 25, 100, 175);
node3.nodeFrame = CGRectMake(150, 150, 50, 50);
node3.backgroundColor = [UIColor clearColor];
[myView addSubview:node3];
}
My code in LVStemp.h:
#interface LVStemp : UIView
#property (nonatomic) CGRect nodeFrame;
#end
My code in LVStemp.m:
#implementation LVStemp
- (void)drawRect:(CGRect)rect
{
// Build nodeBounds (= nodeFrame converted to self's coordinate system)
CGRect nodeBounds = [self convertRect:self.nodeFrame fromView:self.superview];
// Get CGContextRef
CGContextRef context = UIGraphicsGetCurrentContext();
// Draw node with gradient
[self drawEllipseInRect:nodeBounds withGradient:context];
}
- (void)drawEllipseInRect:(CGRect)rect withGradient:(CGContextRef)context
{
UIGraphicsPushContext(context);
CGFloat colors[] = {
0.1, 0.8, 1.0, 1.0,
0.1, 0.4, 0.9, 1.0
};
CGColorSpaceRef baseSpace = CGColorSpaceCreateDeviceRGB();
CGGradientRef gradient = CGGradientCreateWithColorComponents(baseSpace, colors, NULL, 2);
CGColorSpaceRelease(baseSpace), baseSpace = NULL;
CGContextSaveGState(context);
CGContextAddEllipseInRect(context, rect);
CGContextClip(context);
CGPoint startPoint = CGPointMake(CGRectGetMidX(rect), CGRectGetMinY(rect));
CGPoint endPoint = CGPointMake(CGRectGetMidX(rect), CGRectGetMaxX(rect));
CGContextDrawLinearGradient(context, gradient, startPoint, endPoint, 0);
CGGradientRelease(gradient), gradient = NULL;
CGContextRestoreGState(context);
CGContextAddEllipseInRect(context, rect);
CGContextDrawPath(context, kCGPathStroke);
UIGraphicsPopContext();
}
#end
(drawEllipseInRect:withGradient: is adapted from here.)
And here's what happens if I comment out the clipping part:
//CGContextAddEllipseInRect(context, rect);
//CGContextClip(context);
Any suggestions?
The context is clipped correct, but you are drawing the gradient at the wrong Position:
CGPoint endPoint = CGPointMake(CGRectGetMidX(rect), CGRectGetMaxX(rect));
Change that to use maxY:
CGPoint endPoint = CGPointMake(CGRectGetMidX(rect), CGRectGetMaxY(rect));
But as you might have noticed, the strokes are being clipped. That's because they are drawn centered on the given outline. You might consider calling it with some inset:
[self drawEllipseInRect:CGRectInset(nodeBounds, lineWidth/2, lineWidth/2) withGradient:context];
All,
i have this piece of code :
- (void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, [UIColor redColor].CGColor);
CGContextFillRect(context, CGRectMake(0, 440, 320, 30));
and i call it like this :
_rectangeView = [[PopUpRectangle alloc] initWithFrame:CGRectMake(0, 538, 320, 30)];
but its BLACK!
anyone advice why this is ?
Change your colouring rect like this...
CGContextFillRect(context, CGRectMake(0, 0, 320, 30));
Use 0, 0 as the origins not 0, 440.
That should work.
Inside drawRect you are dealing in the vector space of the view itself not its superview. So 0, 0 is the top left of your rectangleView.
Your _rectangeView height is just 30 points but the context fill rect bounds y value is 440.0; exceeds the bounds of the view. Try changing the y value to 0 and you will see the color.
everybody,
I want to make the circle mask on the camera.
i try this way:
- (void)drawRect:(CGRect)rect
{
// Drawing code
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextAddArc(context, self.frame.size.width/2, self.frame.size.height/2, 200, 0, M_PI *2, 0);
//Set the stroke color to black
[[UIColor blackColor]setStroke];
//Define line width and cap
CGContextSetLineWidth(context, 200);
CGContextSetLineCap(context, kCGLineCapButt);
//draw it!
CGContextDrawPath(context, kCGPathStroke);
}
just set the line with enough big,but i think it can set alpha.
thanks.
[[UIColor colorWithRed:0 green:0 blue:0 alpha:alpha/255.0] setStroke];
set the alpha percent you want
As you don't mentioned adjustable circle mask, I assume you need a static circle mask over on the camera view. If you need dynamically created circle, this is not the right answer.
Anyway it might help to know a simple way to do this:
prepare a png which is a circle actually, and the inside of that circle is transparent (make sure you handle the proper width and height or even create multiple circle pngs for different devices eg. iPhone5 and iPhone4)
use the imagePickerController's cameraOverlayView property to add the overlay image
UIView *tmp_camera_overlay = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 480)];
UIImageView *tmp_camera_bkgr =[[UIImageView alloc] initWithImage:[UIImage imageNamed:#"circle_screen_overlay.png"]];
tmp_camera_bkgr.frame = CGRectMake(0, 0, 320, 480);
[tmp_camera_overlay addSubview:tmp_camera_bkgr];
imagePickerController.cameraOverlayView = tmp_camera_overlay;
I'm drawing an inset rectangular border to my UIButton subclass inside the drawRect: method.
As you can see that shape is too close to my button titleLabel frame. How can I set the titleLabel's max width/margin to avoid this?
DrawRect: method
- (void)drawRect:(CGRect)rect {
[super drawRect:rect];
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetStrokeColorWithColor(context, [UIColor bluecolor].CGColor);
CGContextSetLineWidth(context, 2.0);
CGContextMoveToPoint(context, 4,4);
CGContextAddLineToPoint(context, 56, 4);
CGContextAddLineToPoint(context, 56, 56);
CGContextAddLineToPoint(context, 4, 56);
CGContextAddLineToPoint(context, 4,3);
// and now draw the Path!
CGContextStrokePath(context);
}
Use self.titleEdgeInsets=UIEdgeInsetsMake(0, 5, 0, 5); in drawRect method.
Try this code....
UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
UIEdgeInsets edgeInsets = button.titleEdgeInsets;
edgeInsets.left -= 5;
edgeInsets.right += 5;
button.titleEdgeInsets = edgeInsets;
From xCode, this can be set using the Inset property as shown in the screenshot below: