0

I define my own my own drawRect method and it is called on 4.2.1 (iOS) 5.0 (iOS) and 4.3.2 (Simulator) sucesfully. But it never called on 3.1.3 (iPhone 2g).

What reason could be for this?

P.S. Since i start write the question i think about my 3.1.3 device is jailbroken. Maybe it is root cause of this strange behaviour.

Upd: To reproduce issue i use next code:

@implementation UIView (MyOwnCategory)
- (void)drawRect:(CGRect)rect
{
    const char * function = __FUNCTION__;
    [NSException raise: @"hi!" format: @"%s", function];
}
@end

exception never happened on 3.1.3 even when i call [super drawRect: rect] explicitly

Maxim Kholyavkin
  • 4,463
  • 2
  • 37
  • 82
  • Is your view a subclass or is drawRect a category method? – jrtc27 Jan 11 '12 at 22:33
  • Plug in your iPhone 2G. Set a breakpoint in Xcode. Does the line of code get hit? Maybe you could show some code while you're at it? – Shaggy Frog Jan 11 '12 at 22:33
  • 6
    Why are you implementing `-drawRect:` on a category of `UIView`? That sounds like a really bad idea. – Lily Ballard Jan 11 '12 at 22:34
  • @KevinBallard I want to replace empty implementation to draw bounds of any views to see it on my debug application. – Maxim Kholyavkin Jan 11 '12 at 23:19
  • 1
    @Speakus: Implementing `-drawRect:` on views that otherwise didn't have one has some behavioral and performance implications. In any case, a better approach might be to [swizzle](http://cocoadev.com/index.pl?MethodSwizzling) `-[UIView initWithFrame:]` to call the original implementation, then pull out the layer and set the `borderColor`/`borderWidth` properties. – Lily Ballard Jan 11 '12 at 23:29

1 Answers1

3

I wanted to write about Method Swizzling for a few weeks now, and @Kevin Ballard's comment finally made me do it (thank you for the inspiration, Kevin).

So here's a solution for your problem using method swizzling which should also work on iOS 3.x:

UIView+Border.h:

#import <Foundation/Foundation.h>
@interface UIView(Border)
@end

UIView+Border.m:

#import "UIView+Border.h"
#import <QuartzCore/QuartzCore.h>
#import <objc/runtime.h>

@implementation UIView(Border)

- (id)swizzled_initWithFrame:(CGRect)frame
{
    // This is the confusing part (article explains this line).
    id result = [self swizzled_initWithFrame:frame];

    // Safe guard: do we have an UIView (or something that has a layer)?
    if ([result respondsToSelector:@selector(layer)]) {
        // Get layer for this view.
        CALayer *layer = [result layer];
        // Set border on layer.
        layer.borderWidth = 2;
        layer.borderColor = [[UIColor redColor] CGColor];
    }

    // Return the modified view.
    return result;
}

- (id)swizzled_initWithCoder:(NSCoder *)aDecoder
{
    // This is the confusing part (article explains this line).
    id result = [self swizzled_initWithCoder:aDecoder];

    // Safe guard: do we have an UIView (or something that has a layer)?
    if ([result respondsToSelector:@selector(layer)]) {
        // Get layer for this view.
        CALayer *layer = [result layer];
        // Set border on layer.
        layer.borderWidth = 2;
        layer.borderColor = [[UIColor blueColor] CGColor];
    }

    // Return the modified view.
    return result;
}

+ (void)load
{
    // The "+ load" method is called once, very early in the application life-cycle.
    // It's called even before the "main" function is called. Beware: there's no
    // autorelease pool at this point, so avoid Objective-C calls.
    Method original, swizzle;

    // Get the "- (id)initWithFrame:" method.
    original = class_getInstanceMethod(self, @selector(initWithFrame:));
    // Get the "- (id)swizzled_initWithFrame:" method.
    swizzle = class_getInstanceMethod(self, @selector(swizzled_initWithFrame:));
    // Swap their implementations.
    method_exchangeImplementations(original, swizzle);

    // Get the "- (id)initWithCoder:" method.
    original = class_getInstanceMethod(self, @selector(initWithCoder:));
    // Get the "- (id)swizzled_initWithCoder:" method.
    swizzle = class_getInstanceMethod(self, @selector(swizzled_initWithCoder:));
    // Swap their implementations.
    method_exchangeImplementations(original, swizzle);
}

@end
DarkDust
  • 90,870
  • 19
  • 190
  • 224
  • Thanks. This code solve one of my wish (to see views). Although it does not answer to my initial question about never called drawRect on 3.1.3 – Maxim Kholyavkin Jan 12 '12 at 22:25
  • 1
    You're right, your question wasn't answered. My guess is: according to the [drawRect: reference](http://developer.apple.com/library/ios/documentation/uikit/reference/UIView_Class/UIView/UIView.html#//apple_ref/occ/instm/UIView/drawRect:), the default implementation does nothing and you don't need to call `[super drawRect:bounds];` in a direct `UIView` subclass. Therefor I'd say there is simply no guarantee that the method is called in the plain `UIView` class (there is no need to). – DarkDust Jan 12 '12 at 23:14