I'm trying to understand Quartz and getting the context you have to draw on. If I have a function where I create a context, but then I call another function to some other drawing to the same context, do I need to pass the context from the first method to the next? Or can I just use UIGraphicsGetCurrentContext() for any CG methods that require a context since I'm still drawing into the same context?
The docs for UIGraphicsGetCurrentContext() say:
The current graphics context is nil by default. Prior to calling its
drawRect: method, view objects push a valid context onto the stack,
making it current. If you are not using a UIView object to do your
drawing, however, you must push a valid context onto the stack
manually using the UIGraphicsPushContext(_:) function.
So after calling UIGraphicsPushContext() with the context you've created, your other methods can access that context with UIGraphicsGetCurrentContext(). If you're calling UIGraphicsGetCurrentContext() outside of drawRect: and haven't set a context explicitly with UIGraphicsPushContext(), the current graphics context is undefined—and certainly not safe to use.
Related
So i was reading Mike Ash's article on Swift speed where i encountered that if we have an empty function then a method call will not be made by Swift
func test() {}
Then a call to test() basically would not be called by the compiler.
Now if i remember correctly then Apple recommends against leaving an empty drawRect: method because it might hinder performance.
Now my question is if an empty function is never called by the compiler then why against the empty drawRect:
I was just curious. Thanks
The problem with drawRect is not the function call. You can't just call drawRect, there is a lot of setup needed to set up the correct context for drawRect. And the code doing that setup can check that there is no drawRect method and avoid the whole setup, but it cannot detect that drawRect does nothing.
I want to dynamically change the current CGContextRef according to different user actions? Is this possible or is its modification only possible within drawRect: of a view instance? What happens when I call UIGraphicsGetCurrentContext() outside drawRect: and are there any limitations in doing so, is this recommended? Any possible implications I need to consider?
According to the docs the graphics context is only set just before this function is called. This means that if this function is not called it won't be set and if you don't make the system call it again (never do this yourself for that reason) it won't be there either.
Use one of these functions to force the view back into drawRect:
setNeedsDisplay:
setNeedsDisplayInRect:
It doesn't mean you can only do stuff inside drawRect however. This context is sort of globally available at that moment and you can call clean separate functions or even classes for drawing things. Passing the reference to those functions is a clean way to do it.
I'm having trouble finding the correct place to do my shader setup for an OpenGLES application using GLKView and GLKViewController.
It seems like viewDidLoad is a natural place to do this, but shader creation fails when I try to do this here. My setup is something like this:
//shader helper method
int setupShaders(const char* vShader, const char* fShader); //returns a program handle
//inside GLKViewController subclass
static int program;
-(void)viewDidLoad{
[super viewDidLoad];
program = setupShaders(vsh, fsh); //program will be zero indicating setup failure
}
I know the setup code works because it succeeds if I call it inside -(void)glkView:(GLKView *)view drawInRect:(CGRect)rect.
So I'm assuming OpenGL isn't fully initialized when -(void)viewDidLoad is called, or something has to be done to set the correct OpenGL context for the setup I'm trying to do, I just can't find any documentation on where or how to do setup correctly.
You are right that the earliest place you can easily set up your shaders is in the drawRect method. This is because there must be a valid GL context current. Per the GLKView documentation:
Before calling its drawRect: method, the view makes its EAGLContext object the current OpenGL ES context and binds its framebuffer object to the OpenGL ES context as the target for rendering commands.
So, the easiest thing to do is hang onto some information, like the program handle, and only initialize if it is non-zero.
if (program == 0)
program = setupShaders(vsh, fsh);
If you don't like this approach, you can consider initializing your GLKView with a context that you provide, or overriding bindDrawable. Or you could not use GLKView and do things manually...
There needs to be a current EAGLContext before you can call any OpenGL ES API, and that includes setup work like compiling shaders. GLKView makes its context current before invoking your drawRect: (or glkView:drawInRect:) method, but you're welcome to make it the current context at any time.
The view's context is current as of viewDidAppear: because that method is called after the first time the view draws itself. I'm not sure it's guaranteed to be current at that point, though -- there's no documented API contract that a GLKView's context will remain current after the end of drawing. So it's better to call [EAGLContext setCurrentContext:myContext] yourself whenever you want to do setup work for your OpenGL ES scene (even if you choose to do it in viewDidAppear:).
See the OpenGL ES Game template when you create a new Xcode project for an example of GLKView/GLKViewController setup.
So it turns out it works perfectly if you initialize from inside -(void)viewDidAppear. Jacob's solution works fine as well, but it seems slightly cleaner to me to use a callback rather than adding a conditional to the draw method.
I'm new to CG drawing, and I'm confused on where the CG code goes.
What is stopping the idea of putting the draw functions in the UIViewController vs the UIView? How should I determine which parts of the CG code should go where? I see that some of the tutorials have code in viewDidLoad from the view controller, but others say it goes in the view. What determines what goes where?
(Yes this is kind of an MVC question, but Im still having trouble differentiating.)
The correct place for custom drawing code is (almost always) in the drawRect method of a subclass of UIView. The usual way to go is to make a custom subclass of UIView and make that the root view of your view controller. In the view controller's loadView method, for example, you can assign self.view = [[MyCustomView alloc] init]; (autorelease that view if you're in non-ARC code!) Then your custom drawing code should go in the drawRect method of MyCustomView.
Core Graphics drawing code can go where ever a valid context is. This means it can go in your own functions if you create your own context.
The reason you generally place Core Graphics drawing code in a UIView subclass is you normally may want to encapsulate the code in a reusable form. But if you were going to create an image from the Core Graphics code you could easily start a new image context, draw, then save the contents of the context into a UIImage. This type of drawing can go anywhere, even a UIViewController. Core Graphics drawing can even be used to generate PDFs. Its simply a simple geometric drawing framework. As long as you have a valid context be it the one created before drawRect: is called in a UIView or a context you created on command.
I found this in the Quartz 2D Programming Guide:
To draw to the screen in an iOS application, you set up a UIView object and implement its drawRect: method to perform drawing. The view’s drawRect: method is called when the view is visible onscreen and its contents need updating. Before calling your custom drawRect: method, the view object automatically configures its drawing environment so that your code can start drawing immediately. As part of this configuration, the UIView object creates a graphics context (a CGContextRef opaque type) for the current drawing environment. You obtain this graphics context in your drawRect: method by calling the UIKit function UIGraphicsGetCurrentContext.
Since I am having problems with invalid Context (because it's 0x00 when I go back to re-draw), I was wondering if I could get the current context in the beginning of -drawRect and somehow pass it to the methods I call from within -drawRect?
You can definitely pass CGContextRef to methods called from drawRect: as long as these methods do not save the reference for use outside the duration of the drawRect: call, your code should be fine. However, the context reference that you pass around would be equivalent to the context retrieved through UIGraphicsGetCurrentContext, so I doubt that there is much to gain by adding an extra parameter.
UIGraphicsGetCurrentContext can only be called from drawRect: method (or methods called from it) otherwise it will return nil.
You can use UIGraphicsGetCurrentContext from any method called from -drawRect. It's worth noting that you should not call -drawRect directly when you need to update your view; call -setNeedsDisplay instead.
If you want to use the UIKit drawing system with your own off-screen context, you can use UIGraphicsPushContext to set the current context.
In my experience, passing CGContextRef produces a memory leak that's pretty "fast."