drawRect: being called, only shows the frame not the drawing - objective-c

I have a UIControl subclass where I have drawRect implemented. I am trying to draw a rectangle inside of the drawRect method using a UIBezierPath. All the appropriate methods are being called, nothing is null and the app runs with no issues. The problem is, nothing but the frame is drawn. Meaning, I can see the area of the frame, because it is black, but I cannot see anything that was drawn onto the frame.
Here is what I am doing to instantiate the class:
ZHButton *button =
[[ZHButton alloc] initWithFrame:CGRectMake(100, 200, 100, 50)];
button.buttonType = NSSelectorFromString(LEFT_ROUNDED_CORNER_BUTTON);
[self.view addSubview:button];
Here is the drawRect: method, which calls different methods using a selector:
- (void)drawRect:(CGRect)rect {
UIBezierPath *button = [self performSelector:self.buttonType withObject:nil];
[[UIColor orangeColor] setFill];
[button fill];
}
- (UIBezierPath *)drawButtonWithLeftCornersRounded {
return [UIBezierPath bezierPathWithRoundedRect:self.frame
byRoundingCorners:UIRectCornerTopLeft |
UIRectCornerBottomLeft
cornerRadii:buttonRadii];
}
Everything appears to be working fine, but the UIBezierPath does not get drawn. Also, not sure if using a selector is the best way to do this. Anyone know what is going on with this?

It seems you're not calling the drawButtonWithLeftCornersRounded method. I believe this line is wrong:
[self performSelector:self.buttonType withObject:nil];
But it should be:
[self performSelector:#selector(drawButtonWithLeftCornersRounded) withObject:nil];
Hope this helps!

Solved it. In drawButtonWithLeftCornersRounded bezierPathWithRoundedRect should not be self.frame. This will cause the x and y coordinates to be at the x, y location within the frame, not the view. I changed it the parameter to this:
CGRectMake(0, 0, self.bounds.size.width, self.bounds.size.height);

Related

How to add an NSView to NSWindow in a Cocoa app?

Since the template of an OS X app in Xcode seems to be similar to an empty app template, the following is used to add a view and a button (trying not to use Interface builder for now):
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
NSView *view = [[NSView alloc] initWithFrame:NSMakeRect(100, 100, 100, 100)];
view.layer.backgroundColor = [[NSColor yellowColor] CGColor];
[self.window.contentView addSubview:view];
NSRect frame = NSMakeRect(10, 40, 90, 40);
NSButton* pushButton = [[NSButton alloc] initWithFrame: frame];
pushButton.bezelStyle = NSRoundedBezelStyle;
[self.window.contentView addSubview:pushButton];
NSLog(#"subviews are %#", [self.window.contentView subviews]);
}
Similar code on iOS should have produced a yellow box and a button, but the code above only produce a button, but the view won't show. Is there something wrong with the code above, and how to make it show the view with a yellow background?
Use setWantsLayer: method of NSView class.
NSView *view = [[NSView alloc] initWithFrame:NSMakeRect(100, 100, 100, 100)];
[view setWantsLayer:YES];
view.layer.backgroundColor = [[NSColor yellowColor] CGColor];
[self.window.contentView addSubview:view];
NSRect frame = NSMakeRect(10, 40, 90, 40);
NSButton* pushButton = [[NSButton alloc] initWithFrame: frame];
pushButton.bezelStyle = NSRoundedBezelStyle;
[self.window.contentView addSubview:pushButton];
NSLog(#"subviews are %#", [self.window.contentView subviews]);
To expand on the suggestion by Kevin Ballard, the classic way to do this is to subclass NSView and override the -drawRect: method. NSRectFill is a very convenient function for filling a rectangle without having to create a bezier path:
- (void)drawRect:(NSRect)rect
{
[[NSColor yellowColor] set];
NSRectFill(rect);
}
NSViews in Cocoa are, by default, not layer-backed. I suspect that if you type
NSLog(#"%#", view.layer);
you will see that it is nil.
In iOS, all views have layers. But on OS X, views don't have layers. In addition, there's 2 "modes" of layer-backed views on OS X. There's what's called a "layer-backed views" and a "layer-hosting view". A layer-backed view uses a CoreAnimation layer to cache drawn data, but you are not allowed to interact with the layer in any way. A layer-hosting view uses a CALayer that you explicitly provide, and you may mess with that layer all you want. However, with a layer-hosting view you may not add any subviews, or use the built-in NSView drawing mechanism. A layer-hosting view must only be used as the root of a CoreAnimation layer hierarchy.
Given all this, you should probably avoid using CoreAnimation at all for your view.
It's possible that an NSBox will do what you want. You can certainly set a fill color there, turn off the border, and set the style to custom. I'm just not 100% certain it will draw as a simple filled rectangle of color. Alternatively you can define your own NSView subclass that draws a color in -drawRect:.

Trying to draw in UIView

Hi I am trying to draw some .png images stored in sandbox and the output the text in UIView. My code is:
-(void)setItemDetails:(ItemShow *)itmShow
{
if(theItem!=itmShow)
{
[theItem release];
theItem=itmShow;
[theItem retain];
}
UIImage *rImage=[UIImage imageNamed:#"years"];
[[UIColor blackColor] set];
[rImage drawInRect:CGRectMake(55.0, 22.0, 17.0, 17.0)];
[[UIColor brownColor] set];
[theItem.itemYear drawAtPoint:CGPointMake(7.0,19.0)
forWidth:100
withFont:[UIFont systemFontOfSize:17.0]
minFontSize:17.0
actualFontSize:NULL
lineBreakMode:UILineBreakModeTailTruncation
baselineAdjustment:UIBaselineAdjustmentAlignBaselines];
}
That after I call this method in viewDidLoad. Nothing happens. I can't see any images and text on canvas of UIView. What's wrong here?
That's right, it is exactly what's supposed to happen (nothing), because viewDidLoad is not the right place from which you do drawing in iOS. You need to implement a different method:
- (void)drawRect:(CGRect)rect {
CGContextRef myContext = UIGraphicsGetCurrentContext();
// Do your drawing in myContext
}
This method of your UIView implementation gets called to do the drawing. Trying to draw on the screen from about anywhere else is not going to produce the desired results.

being called but not drawing

I have some code in the drawRect: method that draws a series of circles from an array. I can verify that the points stay in memory long enough and get to the point where they should be drawn. However, it seems as if the fill method of NSBezierPath is not drawing the circles.
Here is my drawRect:
- (void)drawRect:(NSRect)dirtyRect
{
NSPoint aPoint;
NSRect aRect = NSMakeRect(0, 0, 6, 6);
// Erase the rectangle
[[NSColor clearColor] set];
NSRectFill([self bounds]);
// Draw the dots
[[NSColor colorWithCalibratedRed:(77.0/255.0) green:(11.0/255.0) blue:(11.0/255.0) alpha:1.0] set];
for(NSValue *aValuePoint in arrayOfPoints) {
aPoint = [aValuePoint pointValue];
aRect.origin.y = aPoint.y - aRect.size.height/2.0;
aRect.origin.x = aPoint.x - aRect.size.width/2.0;
// The code does get to this point, it does not draw however...
[[NSBezierPath bezierPathWithOvalInRect:aRect] fill];
}
}
I have another method drawDotAtPoint: where I add the points to an array
- (void)drawDotAtPoint:(NSPoint)aPoint
{
NSLog(#"drawDotAtPoint:(%f, %f)", aPoint.x, aPoint.y);
[arrayOfPoints addObject:[NSValue valueWithPoint:aPoint]];
// I've also tried using [self superview] instead of just self
[self setNeedsDisplay: YES];
}
And while I am able to verify that these methods are called at the correct times (or so it seems) no drawing after the initial drawRect: will take place
Sidenote:
My goal is to actually draw a point and have a ring appear around it, enlarge and fade out. Much similar to the GPS current location indicator on iOS, in the Maps app. Any insight on that would also be appreciated.
Check your points and make sure that you don't have an inverted rectangle and that your X/Y coords are what you expect. Make sure that your anchor point and coordinates are correctly relative.
You may be helped along a bit by using a tool called Quartz Debug that comes with apple's developer tools. It is in the /Developer/Applications/Graphics Tools/ folder. After starting this tool, go to the "tools" menu and turn on "Flash Screen Updates". This will give you an idea of when things are being redrawn or not.
Hope that helps some.
I've copy-pasted your view code. I stuck this in an app delegate for testing. BadView is the custom view here (no reflection on you -- "Bad" because it's misbehaving :) ) The windows are both created in IB, and the WebView is likewise placed in the parent window in IB.
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
// badWindow is an IBOutlet to the overlay window, parentWindow an IBOutlet
// to the window with the webview, myWebView an IBOutlet to the webview
BadView * myBadView = [[BadView alloc] initWithFrame:[[badWindow contentView] frame]];
// Create some random points for testing
[myBadView createArrayOfPoints];
[badWindow setStyleMask:NSBorderlessWindowMask];
[badWindow setOpaque:NO];
[badWindow setAlphaValue:0.9]; // Will still act opaque without this
[badWindow setHasShadow:NO];
[badWindow setContentView:myBadView];
[parentWindow addChildWindow:badWindow ordered:NSWindowAbove];
[[myWebView mainFrame] loadRequest:[NSURLRequest requestWithURL:
[NSURL URLWithString:#"http://www.apple.com/"]]];
[myBadView release];
}

CALayer - clear internal rendering context in favor of custom drawing code

When subclassing an CALayer and implementing the drawInContext method, I would assume that any drawing I do within there is all that will show up, but instead if I set (for example) borderWidth/borderColor then CALayer will draw a border on it's own above all my custom drawing code.
This is a CALayer subclass:
#implementation MyCustomCALayer
- (id)init
{
self = [super init];
if(self)
{
[self setNeedsDisplayOnBoundsChange:YES];
}
return self;
}
- (void)drawInContext:(CGContextRef)context
{
CGRect rect = CGContextGetClipBoundingBox(context);
CGContextSetFillColorWithColor(context, [UIColor redColor].CGColor);
CGContextFillRect(context, rect);
}
#end
Created in a UIView something like:
- (void)ensureLayer
{
if(myLayer)
return;
myLayer = [[[MyCustomCALayer alloc] init] autorelease];
myLayer.borderColor = [UIColor greenColor].CGColor;
myLayer.borderWidth = 1;
myLayer.frame = CGRectMake(0, 0, self.frame.size.width, self.frame.size.height);
}
- (void)layoutSublayersOfLayer:(CALayer *)layer
{
[super layoutSublayersOfLayer:layer];
[self ensureLayer];
if(![[layer.sublayers objectAtIndex:0] isEqual:myLayer])
[layer insertSublayer:myLayer atIndex:0];
}
What happens, is the MyCustomCALayer fills a rectangle with red, this is what I would expect to see and nothing else, since i've implemented the drawInContext method, but instead I see a red rectangle with a green border on top, always on top, i've tried just about every combination I can think of to get rid of the green border being drawn and cannot figure it out.
My reasoning is I would like to use the borderWidth and borderColor and other properties of the CALayer instead of creating my own properties, because the code that I need to draw contains a border, a fill, etc... but the rendering I need to do is not a simple shape. So the only way i've found around this is to set the borderWidth to 0 and add my own property to my subclass, like myBorderWidth, which is ridiculous.
This is done with the latest iOS SDK, but i'd imagine it's the same for Mac.
Hope this makes sense, any thoughts?
Thanks!
You’re out of luck; CoreAnimation doesn’t support overriding its implementation of rendering for the basic layer properties. Please do file a bug.

drawRect is not being called

I need to draw an image in a rectangle so for that am using drawRect method.i observed that it is not being called.The output i can see only the blank screen.Am using MAC OS and i need to test the App in Iphone OS 3+.Please can anyone solve my problem.Here is my code:
-(IBAction)calling
{
int a=0;
a=a+1;
[self.view setNeedsDisplay]; //InRect:CGRectMake(78, 43, 200, 138)];
}
- (void)drawRect:(CGRect)rect {
UIImage *myImage = [UIImage imageNamed:#"btbp.jpg"];
CGRect imageRect = CGRectMake(10, 10, 300, 400);
[myImage drawInRect:imageRect];
[self.view setNeedsDisplay];
[myImage release];
}
i donno where i was wrong:(
Is your drawRect method on an actual view? Or is it on the view controller, which is what it seems like? drawRect is something you write/override on the view itself.
You should not call release on the myImage object, since you didn't alloc it yourself.
If this isn't making 100% sense, I'd recommend you start with the iOS Cocoa Views documentation to understand how this all fits together.