42

I have subclassed UIImageView and tried to override drawRect so I could draw on top of the image using Quartz 2D. I know this is a dumb newbie question, but I'm not seeing what I did wrong. Here's the interface:

#import <UIKit/UIKit.h>

@interface UIImageViewCustom : UIImageView {

}
- (void)drawRect:(CGRect)rect;
@end

And the implementation:

#import "UIImageViewCustom.h"

@implementation UIImageViewCustom

- (id)initWithFrame:(CGRect)frame {
    if (self = [super initWithFrame:frame]) {
    }
    return self;
}

- (void)drawRect:(CGRect)rect {
    // do stuff
}

- (void)dealloc {
    [super dealloc];
}
@end

I set a breakpoint on drawRect and it never hits, leading me to think it never gets called at all. Isn't it supposed to be called when the view first loads? Have I incorrectly overridden it?

Peter Hosey
  • 95,783
  • 15
  • 211
  • 370
Ben Collins
  • 20,538
  • 18
  • 127
  • 187
  • please see the updated answer, sorry for bothering you again. – Madhup Singh Yadav Apr 10 '10 at 05:09
  • okies. going through the documentation I observed it would work if you make a subclass of UIView instead of UIIMageView, now you can add the properties of UIImageView in your subclass, I did it once for drawing the background of my tableview cells , for more details you can check at.http://stackoverflow.com/questions/1784827/draw-a-custom-cell-for-tableview-uitableview-with-changed-colors-and-separa – Madhup Singh Yadav Apr 10 '10 at 05:33

4 Answers4

90

It'll only get called if the view is visible, and dirty. Maybe the problem is in the code that creates the view, or in your Nib, if that's how you're creating it?

You'll also sometimes see breakpoints failing to get set properly if you're trying to debug a "Release" build.


I somehow missed the first time that you're subclassing UIImageView. From the docs:

Special Considerations

The UIImageView class is optimized to draw its images to the display. UIImageView will not call drawRect: in a subclass. If your subclass needs custom drawing code, it is recommended you use UIView as the base class.

So there you have it. Given how easy it is to draw an image into your view using [UIImage drawInRect:], or by using CALayer, there's probably no good reason to subclass UIImageView anyway.

Mark Bessey
  • 19,598
  • 4
  • 47
  • 69
  • shouldn't drawRect get called when the view is first rendered, though? – Ben Collins Apr 10 '10 at 04:50
  • Actually, now that you mention it, I remember reading that. Don't know why I couldn't find it again when I was looking for it. Thanks. – Ben Collins Apr 10 '10 at 14:45
  • 1
    `UIImageView` is a great thing to be able to subclass if you want to work with IB. Sigh... thanks for the answer, it helps. – Dan Rosenstark Oct 15 '11 at 23:33
  • 1
    But on second thought, you can still subclass it. You just cannot mess with the drawing :) – Dan Rosenstark Oct 16 '11 at 01:07
  • 3
    Going on six years later, and the Swift 3.0.1/Xcode 8.2.1 template for a subclassing of UIImageView still has boilerplate commented out draw(_ rect: CGRect) function, wherein the comment says if you need to customize drawing just uncomment and add your custom drawing code. So, thank you, stackoverflow community! – tobinjim Jan 15 '17 at 08:01
  • Yes, the template system in Xcode is sadly under-developed. They could do so much more with it... – Mark Bessey Apr 23 '18 at 22:51
1

Try to add

Edit:

- (id)initWithFrame:(CGRect)frame{
    if (self = [super initWithFrame:frame]) {
        [self setClearsContextBeforeDrawing:YES];//add this line also
    }
    return self;
}


- (void)setNeedsDisplay{
    [self setNeedsDisplayInRect:self.frame];
}

into your code.

hope this helps.

Thanks,

madhup

Madhup Singh Yadav
  • 8,110
  • 7
  • 51
  • 84
0

Depending on your specific case, it could make sense to use a UIButton with a background image instead of a UIImageView. You could even set userInteractionEnabled to NO and the result would be indistinguishable from a UIImageView.

In this case your UIButton subclass would simply include:

- (void)drawRect:(CGRect)rect
{
  if (kSomethingSpecial) {
    [self setBackgroundImage:[UIImage imageNamed:@"RedBackground.png"] 
                    forState:UIControlStateNormal];
  }
}

Disclaimer - I would only recommend this if you have plans to use the image as a button at least some of the time. I can't wholeheartedly endorse using a UIButton as a workaround for this drawRect behaviour in UIImageView, as I'm sure Apple has its reasons for writing their API this way, like Mark referenced.

Alex Cio
  • 6,014
  • 5
  • 44
  • 74
Kyle Clegg
  • 38,547
  • 26
  • 130
  • 141
0

Not directly answering your question, but may solve your problem:

Why do this with a subclass of UIImageView? Subclassing can always be problematic, especially for classes that aren't designed to be subclassed--and I bet UIImageView isn't. If you just want to draw stuff on top of an image, then create a view with a transparent background, draw whatever you want, and place it directly over the image.

jasoncrawford
  • 2,713
  • 2
  • 21
  • 20
  • 1
    OK, fine, I looked it up in the documentation and apparently subclassing it is fine. I still suspect this isn't the best way to accomplish the goal. – jasoncrawford Apr 11 '10 at 22:08