I've made methods like follow and excute code.
I am confusing about origin of quartz framework after looking follow result image.
Please look at follow code and image.
[in ViewController.m]
Excute a follow method when press UIButton.
- (void)test {
[customView drawCustom];
[customView setNeedsDisplay];
}
[in CustomView.m]
- (void)drawRect:(CGRect)rect {
cgc_Context = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(cgc_Context, 5.0);
CGContextSetStrokeColorWithColor(cgc_Context, [UIColor redColor].CGColor);
CGContextMoveToPoint(cgc_Context, 0, 0);
CGContextAddLineToPoint(cgc_Context, 200, 50);
CGContextStrokePath(cgc_Context);
}
- (void)drawCustom {
CGContextSetLineWidth(cgc_Context, 5.0);
CGContextSetStrokeColorWithColor(cgc_Context, [UIColor yellowColor].CGColor);
CGContextMoveToPoint(cgc_Context, 0, 0);
CGContextAddLineToPoint(cgc_Context, 200, 50);
CGContextStrokePath(cgc_Context);
}
[Result screen]
Red Line : result of drawRect
Yellow Line : result of drawCustom
Why does occur different origin point?
You must read the apple's documentation about Quartz 2D Coordinate Systems
Related
I followed another StackOverflow post that explains how i could override the draw method of a PDFAnnotation so i could draw a picture instead of a traditional PDFAnnotation.
But sadly i was not able to achieve that and the annotation that is drawn on top of my pdf is still a regular one.
This is the code that i used :
#implementation PDFImageAnnotation { UIImage * _picture;
CGRect _bounds;};
-(instancetype)initWithPicture:(nonnull UIImage *)picture bounds:(CGRect) bounds{
self = [super initWithBounds:bounds
forType:PDFAnnotationSubtypeWidget
withProperties:nil];
if(self){
_picture = picture;
_bounds = bounds;
}
return self;
}
- (void)drawWithBox:(PDFDisplayBox) box
inContext:(CGContextRef)context {
[super drawWithBox:box inContext:context];
[_picture drawInRect:_bounds];
CGContextRestoreGState(context);
UIGraphicsPushContext(context);
};
#end
Does someone know how i could override the draw method so i could draw a custom Annotation ?
Thank You !
ps: i also tried to followed the tutorial on the apple dev site.
UPDATE :
Now i'm able to draw pictures using CGContextDrawImage but i'm not able to flip coordinates back in place. when i do that mi pictures are not drawn and it seems that they are put outside of the page but i'm not sure.
This is my new code :
- (void)drawWithBox:(PDFDisplayBox) box
inContext:(CGContextRef)context {
[super drawWithBox:box inContext:context];
UIGraphicsPushContext(context);
CGContextSaveGState(context);
CGContextTranslateCTM(context, 0.0, _pdfView.bounds.size.height);
CGContextScaleCTM(context, 1.0, -1.0);
CGContextDrawImage(context, _bounds, _picture.CGImage);
CGContextRestoreGState(context);
UIGraphicsPopContext();
}
I also tried to follow the tutorial on the Apple dev site.
Which one?
Custom Graphics
Adding Custom Graphics to a PDF
Because both include UIGraphicsPushContext(context) & CGContextSaveGState(context) calls, but your code doesn't. Do not blindly copy & paste examples, try to understand them. Read what these two calls do.
Fixed code:
- (void)drawWithBox:(PDFDisplayBox) box
inContext:(CGContextRef)context {
[super drawWithBox:box inContext:context];
UIGraphicsPushContext(context);
CGContextSaveGState(context);
[_picture drawInRect:_bounds];
CGContextRestoreGState(context);
UIGraphicsPopContext();
}
The image was drawn with CGRectMake(20, 20, 100, 100). It's upside down, because PDFPage coordinates are flipped (0, 0 = bottom/left). Leaving it as an exercise for OP.
Rotation
Your rotation code is wrong:
CGContextTranslateCTM(context, 0.0, _pdfView.bounds.size.height);
CGContextScaleCTM(context, 1.0, -1.0);
CGContextDrawImage(context, _bounds, _picture.CGImage);
It's based on _pdfView bounds, but it should be based on the image bounds (_bounds). Here's the correct one:
- (void)drawWithBox:(PDFDisplayBox) box
inContext:(CGContextRef)context {
[super drawWithBox:box inContext:context];
UIGraphicsPushContext(context);
CGContextSaveGState(context);
CGContextTranslateCTM(context, _bounds.origin.x, _bounds.origin.y + _bounds.size.height);
CGContextScaleCTM(context, 1.0, -1.0);
[_picture drawInRect:CGRectMake(0, 0, _bounds.size.width, _bounds.size.height)];
CGContextRestoreGState(context);
UIGraphicsPopContext();
}
This code appears to do nothing. Build successful, no errors. No rectangle drawn on screen.
- (void)viewDidLoad
{
[super viewDidLoad];
UIColor *reliantMagenta = [UIColor colorWithRed:208.0f / 255.0f green:27.0f / 255.0f blue:124.0f / 255.0f alpha:1];
CALayer *reliantCanvasLayer = [CALayer layer];
reliantCanvasLayer.frame = CGRectMake(0, 0, 640, 960);
[[[self view] layer] addSublayer:reliantCanvasLayer];
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGRect leftRect = CGRectMake(0, 0, 200, 300);
CGContextSaveGState(ctx);
CGContextSetFillColorWithColor(ctx, reliantMagenta.CGColor);
CGContextFillRect(ctx, leftRect);
CGContextRestoreGState(ctx);
}
I am just learning Quartz and really thrashing with it. If you want to explain the relationship between UIViews, CALayers, CGLayers, and context that would be a big help too, but not required, just having trouble understanding what is going on.
If you're starting out with Quartz, then you should start with the Quartz 2D Programming Guide, which walks through all of this. Your key mistake here is that there is no context available in viewDidLoad. Drawing of this kind is generally done in drawRect:. Your call to UIGraphicsGetCurrentContext() returns NULL at this point.
After reading through the Programming Guide, you may have more questions, but that's where you should start to learn about custom drawing.
I am subclassing UIButton.
But I need to know the state that the button is in to draw the color of button for up or down:
- (void)drawRect:(CGRect)rect{
if (state==UIControlStateNormal) {
//draw rect RED
}
if (state==UIControlEventTouchDown)
//draw rect BLUE
}
}
Did you even try looking at the docs?
Goto UIButton Reference
Check the properties
Nothing obvious
See if UIButton has a superclass
Goto UIControl Reference - UIButton's superclass
Check the properties
Oh look there is a state property
Update
The accepted answer is slightly incorrect and could lead to some annoyingly difficult bug to track down.
The header for UIControl declares state as
#property(nonatomic,readonly) UIControlState state; // could be more than one state (e.g. disabled|selected). synthesized from other flags.
Now looking up to see how UIControlState is defined we see
enum {
UIControlStateNormal = 0,
UIControlStateHighlighted = 1 << 0, // used when UIControl isHighlighted is set
UIControlStateDisabled = 1 << 1,
UIControlStateSelected = 1 << 2, // flag usable by app (see below)
UIControlStateApplication = 0x00FF0000, // additional flags available for application use
UIControlStateReserved = 0xFF000000 // flags reserved for internal framework use
};
typedef NSUInteger UIControlState;
Therefore as you are dealing with a bit mask you should check for the state appropriately e.g.
if (self.state & UIControlStateNormal) { ... }
Update 2
You could do this by drawing into an image and then setting the image as the background e.g.
- (void)clicked:(UIButton *)button;
{
UIGraphicsBeginImageContext(button.frame.size);
// Draw gradient
UIImage *gradient = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
[button setBackgroundImage:image forState:UIControlStateNormal];
}
Since state is a property, you can use self.state to access it.
Update:
See the next answer's update.
To redraw rect, I did this...
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
myState = 1;
[self setNeedsDisplay];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
myState = 0;
[self setNeedsDisplay];
}
- (void)drawRect:(CGRect)rect
{
// Drawing code
NSLog(#"drawRect state: %d", self.state);
NSLog(#"rect %#", NSStringFromCGRect(self.frame));
c = UIGraphicsGetCurrentContext();
if (myState==0){
///////// plot border /////////////
CGContextBeginPath(c);
CGContextSetLineWidth(c, 2.0);
CGContextMoveToPoint(c, 1, 1);
CGContextAddLineToPoint(c, 1, 20);
CGContextAddLineToPoint(c, 299, 20);
CGContextAddLineToPoint(c, 299, 1);
CGContextSetRGBFillColor(c, 0.0, 0.0, 1.0, 1.0);
CGContextFillPath(c);
CGContextClosePath(c);
CGContextStrokePath(c);
}else{
CGContextBeginPath(c);
CGContextSetLineWidth(c, 2.0);
CGContextMoveToPoint(c, 1, 20);
CGContextAddLineToPoint(c, 1, 40);
CGContextAddLineToPoint(c, 299, 40);
CGContextAddLineToPoint(c, 299, 20);
CGContextSetRGBFillColor(c, 1.0, 0.0, 1.0, 1.0);
CGContextFillPath(c);
CGContextClosePath(c);
CGContextStrokePath(c);
}
}
I am new to iOS/Mac programming. I am trying to create an iOS application that performs custom bitmap drawing, and blits it to screen.
I have looked at some examples that use CGImage, but I haven't been able to able to follow them to create an iOS application that performs custom drawing on the application window.
I am now looking for examples/tutorials related to this, written specifically for iOS. Please post any you know of.
Create a subclass of UIView and implement the drawRect: method. Drawing to a Graphics Context in iOS
Drawing a red rectangle:
...with Quartz
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetRGBFillColor(context, 1.0,0.0,0.0,1.0);
CGRect rectangle = CGRectMake(10, 10, 50, 50);
CGContextFillRect(context, rectangle);
}
...with UIKit
- (void)drawRect:(CGRect)rect {
[[UIColor redColor] set];
CGRect rectangle = CGRectMake(10, 10, 50, 50);
UIRectFill(rectangle);
}
Cocoa gives error:
Thu Jun 10 19:13:56 myComputer.local myApp[####] <Error>: doClip: empty path.
But I don't have this function anywhere in my code (can't find by searching in frameworks / project)... Seems a lot of people complain about this because it goes into the console logs, but couldn't find any reason given as to what causes it on a progmatic level.
Any thoughts as to what the problem is?
I would just check to see if the path is empty using: CGContextIsPathEmpty(CGContextRef ctx)
Example:
if(!CGContextIsPathEmpty(c))
CGContextClip(c);
Poking around with class-dump and nm, I found that it's a function in CoreGraphics (Quartz 2D). It's not declared in the headers, so it's a private function.
Break on doClip in the debugger, then move down the stack and see whether any of your code is drawing at that time. If so, you're probably trying to clip to an empty path. If a third-party framework you're using is involved, you should file a bug with its authors.
If you're not calling it (and you shouldn't), and a third-party framework is not implicated, it's probably a bug in one of the Apple frameworks. You should report it to Apple.
I've seen this error before when drawing in a context and trying to clip it. Are you doing any context drawing like this?
CGContextClip(ctx);
CGContextTranslateCTM(ctx, 0, image.size.height);
CGContextScaleCTM(ctx, 1.0, -1.0);
CGContextDrawImage(ctx, CGRectMake(0, 0, self.frame.size.width, self.frame.size.height), image.CGImage);
I had the same issue when I implemented the following methods to set the navigation bar image override specific for landscape orientation. To get rid of the clip error I commented out the CGContextClip portion of the code as shown below:
#implementation UINavigationBar(CustomBackground)
+ (UIImage *) bgImagePortrait
{
static UIImage *image = nil;
if (image == nil) {
image = [[UIImage imageNamed:#"iPhoneHeader_portrait"] retain];
}
return image;
}
+ (UIImage *) bgImageLandscape
{
static UIImage *image = nil;
if (image == nil) {
image = [[UIImage imageNamed:#"iPhoneHeader_landscape"] retain];
}
return image;
}
- (void) drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx
{
if ([self isMemberOfClass:[UINavigationBar class]] == NO) {
return;
}
UIImage *image = (self.frame.size.width > 320) ?
[UINavigationBar bgImageLandscape] : [UINavigationBar bgImagePortrait];
//CGContextClip(ctx); // Causes '<Error>: doClip: empty path.' error when changing views.
CGContextTranslateCTM(ctx, 0, image.size.height);
CGContextScaleCTM(ctx, 1.0, -1.0);
CGContextDrawImage(ctx, CGRectMake(0, 0, self.frame.size.width, self.frame.size.height), image.CGImage);
}
#end