i was trying out quartz 2d today and i got to learn basic drawing and other things, but now, somehow everything i do doesn't draw a thing on the iphone screen! i tried making a new project and starting from scratch but still no luck... first, i made a new view based application, and then created a new file (.h and .m) as a subclass of UIView. In the implementation, i just overrode the drawRect method, and nothing happens! heres the code:
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
CGFloat red[4] = {1.0f, 0.0f, 0.0f, 1.0f};
CGContextSetStrokeColor(context, red);
CGContextBeginPath(context);
CGRect rectangle = CGRectMake(0, 0, 400, 200);
CGContextAddRect(context, rectangle);
CGContextSetFillColor(context, red);
CGContextFillPath(context);
}
i have another project that i started before and have the exact same code in and that runs perfectly... so i think i changed some settings or something.. can anyone help?
EDIT: there is nothing i forgot, im sure of it, since i've been doing what i did in my last project, but somehow, it doesnt draw in this one...
I usually forget to set the class of the custom view in Interface Builder (Class popup at the top of the Identity page of the Inspector window). That would explain why drawRect: isn't called.
Related
I'm trying to get my code to programmatically zoom into a small area of the larger picture. I'll add the tap code later, but right now I just want to see it work.
The zoomToRect in this code does absolutely nothing and I simply don't understand why. When I build it, the image just sits there at the 0,0 origin.
I've tried using:
setContentOffset and scrollRectToVisible and both these work fine -the image moves to the specified coordinates. But neither of these is what I want, because I need to move and zoom the image, not just move it.
But zoomToRect utterly refuses to do anything. I've read about 50 pages of examples and tutorials on this now and not a damn thing works. I'm tearing my hair out not knowing why. Clearly I'm missing some really fundamental or important point.
UIImage *myFirstImage = [UIImage imageNamed:#"manga_page.jpg"];
UIImageView *myFirstImageView = [[UIImageView alloc] initWithImage:myFirstImage];
[myFirstImageView setFrame:CGRectMake(0, 0, myFirstImage.size.width, myFirstImage.size.height)];
UIScrollView *myFirstScrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];
[myFirstScrollView setContentSize:CGSizeMake(myFirstImage.size.width, myFirstImage.size.height)];
[myFirstScrollView addSubview:myFirstImageView];
[self.view addSubview:myFirstScrollView];
[myFirstScrollView zoomToRect:CGRectMake(300, 300, 300, 300) animated:YES];
The problem was that I hadn't defined the delegate for zooming - see viewForZoomingInScrollView (bottom of page) here:
https://developer.apple.com/library/ios/documentation/uikit/reference/uiscrollviewdelegate_protocol/Reference/UIScrollViewDelegate.html#//apple_ref/occ/intfm/UIScrollViewDelegate/viewForZoomingInScrollView
I don't have a code fragment in Objective C as I switched to the Xamarin framework. However the C# solution is as follows:
scrollView.ViewForZoomingInScrollView = delegate (UIScrollView sv) { return imageView; };
Where imageView is the UIImageView containing the image to be scrolled around.
Set the maximumZoomScale and minimumZoomScale properties for the UIScrollView.
I'm sure I'm missing something basic here. I'm trying out the CALayers 'hello world' code from:
http://www.raywenderlich.com/2502/introduction-to-calayers-tutorial
Doing the very first example. New single view project in xcode 4.2. No change to the nib/storyboard. Import QuartzCore. Add the following code to ViewDidLoad in the ViewController.m:
- (void)viewDidLoad
{
[super viewDidLoad];
self.view.layer.backgroundColor = [UIColor blueColor].CGColor;
self.view.layer.cornerRadius = 30.0;
self.view.layer.frame = CGRectMake(20, 20, 20, 20);
}
I run this (ipad 2 or ipad simulator) and get a full screen blue rectangle with rounded corners. What I hoped to get was a 20x20 blue rectangle offset by 20/20.
I'm clearly getting control over the views layer (as shown by the color and rounded corners). However, adjusting the frame seems to have no impact. I've NSLog'ed the frame before/after setting it, and it has changed. Is the frame of the root layer locked to the uiview frame?
I don't have a strong reason to change the views layers frame, I'm just trying to reason through what is going on. Hopefully this is an easy question...
Thanks!
Paul
Actually, the previous answer (you can't set uiview.layer.frame as it always fills the uiview) is close, but not quite complete. After reading the answer, I registered for the original site and to comment that the tutorial had issues. In doing so, I found that there were already comments that I hadn't seen in my first pass that addressed this. Using those, I started doing some testing.
The bottom line, if you move the self.view.layer.frame setting code from viewDidLoad to viewWillAppear, it works fine. That means that you can change the frame of the root layer of a view. However, if you do it in viewDidLoad it will be undone later.
However, the previous answer is still pretty close. I NSLog'ed the frame of the root layer and the frame of the view. Changing the root layer frame changes the view frame. So, the answer that the view.layer.frame always fills the view.frame is correct. However, setting the layer frame resets the view frame to match. (I'm guessing that uiview.frame property simply returns uiview.layer.frame...)
So, at some point in time between 2010 and today, something in the environment changed. Specifically, after viewDidLoad and before viewWillAppear the uiview/layer frame appears to be reset to the nib specified value. This overrides any changes in viewDidLoad. Changes made in viewWillAppear appear to stick.
Robin's answer got me on the right track, but I wanted to spell out the full answer.
The tutorial is wrong. Setting the frame of the view's main layer has no effect. The main layer is 'special' and will always fill the view's bounds. What you need to do is create a sublayer of the main layer like this:
- (void)viewDidLoad
{
[super viewDidLoad];
CALayer *newLayer = [[CALayer alloc] init];
newLayer.backgroundColor = [UIColor orangeColor].CGColor;
newLayer.cornerRadius = 20.0;
newLayer.frame = CGRectMake(100.0f, 100.0f, 200.0f, 200.0f);
[self.view.layer addSublayer:newLayer];
[newLayer release]; // Assuming you're not using ARC
}
Also, in your code a layer with width 20pt and height 20pt is too small to have rounded corners of 30pt anyway.
I'm writing an iPhone app and I'm new to the iOS sdk. Heres what I'm trying to accomplish. I want an image to flash on top of my screen (On top meaning over everything that's already in my view). I could do this fine, but I want it to be semi-transparent so I'm assuming I need to use the quartz libraries.
I implemented these two functions in a UIView class. This code that works ok but it creates a black background around the image. I want just the image with no box background around it. Is there a CGContext function I can use to alter or remove the background? Should I not be calling drawRect? Am going completely in the wrong direction? Let me know if I anyone needs more information. Thanks.
// Implements UIView
-(void)drawInContextCGContextRef)context{
CGRect imageRect;
imageRect.origin = CGPointMake(8.0, 8.0);
imageRect.size = CGSizeMake(64.0, 64.0);
CGContextSetAlpha(context, 0.5);
CGContextSetBlendMode(context, kCGBlendModeNormal);
CGContextDrawImage(context, imageRect, image);
}
-(void)drawRectCGRect)rect
{
[self drawInContext:UIGraphicsGetCurrentContext()];
}
Here's my code that calls it from the view controller.
[quartzUIView drawRect:CGRectMake(50, 50, 150, 250)];
[self.view addSubview:quartzUIView];
I think you're over-complicating it... No need to go low level.
You can use an UIImageView, just set its opacity to something you like (0.5f for instance) and be sure to set its opaque property to NO.
I have in front of me two Quartz iPhone apps. In each of them, calls to setNeedsDisplay cause a view to redraw itself. But there is an important difference. In one case (the "Quartz Fun" app from the Mark/Lamarche book "Beginning iPhone development"), the view starts out blank each time. In the other case (the app I am working on), the view starts with whatever was there before, and new graphics are added on top of it.
I can't figure out why this is. Can anyone clue me in?
Thanks.
EDIT #2: I still don't understand what is going on here. But I have figured out that I can clear my view by calling
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextClearRect(context, self.frame);
EDIT #3: showing shortened code:
As a suggested by a commenter, I have edited my app so that the issue occurs with very little code. [The form of the issue is a bit different now, as explained below.]
App delegate:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
DiceView *dv = [[DiceView alloc]initWithFrame: window.frame];
[window addSubview:dv];
[dv release];
[window makeKeyAndVisible];
return YES;
}
DiceView:
- (void)drawRect:(CGRect)rect {
static int nDrawrectCalls = 0;
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(context, 1.0);
CGContextSetStrokeColorWithColor(context, (nDrawrectCalls%5==0?[UIColor redColor]:[UIColor greenColor]).CGColor);
CGContextMoveToPoint(context, 10, 30+10*nDrawrectCalls);
CGContextAddLineToPoint(context, 300, 30+10*nDrawrectCalls);
CGContextStrokePath(context);
nDrawrectCalls++;
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
[self setNeedsDisplay];
}
Everything else is just default stub methods.
Now for the difference. It now appears to start drawing with whatever was on the screen two touches prior. In other words, after touch #2, I see the initial line, plus the line from touch #2 -- but not the line from touch #1. After touch #3, I see the lines from touches #1 and #3. After touch #4, I see the initial line and the lines from touches #2 and #4. And so on.
UIView has a clearsContextBeforeDrawing boolean property that switches between the different behaviors you describe. The default is YES which means the view empties the context before calling drawRect:
Check if that property is set somewhere in the example.
Apparently a view doesn't clear it's context correctly, when no backgroundColor is set on the view. Please set backgroundColor to something other than nil.
I think this is a bug and have filed rdar://8165730.
EDIT
It's not a bug. You have to set the opaque property to NO or the backgroundColor. The behavior is described in the UIView Documentation.
Property clearsContextBeforeDrawing:
The default value of this property is
YES. When set to YES, the current
graphics context buffer in the
drawRect: method is automatically
cleared to transparent black before
drawRect: is invoked. If the view’s
opaque property is also set to YES,
the backgroundColor property of the
view must not be nil or drawing errors
may occur.
If the value of this property is NO,
it is the view’s responsibility to
completely fill its content. Drawing
performance can be improved if this
property is NO—for example, when
scrolling.
Property opaque:
YES if it is opaque; otherwise, NO. If
opaque, the drawing operation assumes
that the view fills its bounds and can
draw more efficiently. The results are
unpredictable if opaque and the view
doesn’t fill its bounds. Set this
property to NO if the view is fully or
partially transparent. The default
value is YES.
In my iPhone project, I've got a UIView where I implement the drawRect method:
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
Inside the method I do a whole bunch of drawing of lines, images and texts using this context. The problem is that when I re-use this view, the context does not get reset. Is there a method I can call to reset the context somehow?
You might want to check out CGContextSaveGState(context) and CGContextRestoreGState(context). They will let you push and pop the current state of the context.
If your view is getting 'mangled', check your view's contentMode setting.
You can also use CGContextClearRect(context, CGRectMake(0, 0, width, height));