2

Across my app I have several different subclasses of UIView: UIDatePicker, UIPicker, UIButton, UITableView, UITableViewCell, UITextView, etc. etc... For each of these I'd like to add a very simple drawRect custom implementation that I have working great.

Is there a simple way to get multiple subclasses of UIView to all have the same drawRect implementation without creating a subclass and repeating the same code across each UIPicker, UIButton, etc. etc... ?

I realize the solution to this is probably to write a delegate class for UIView's layer property and do the custom drawing in drawLayer, but I thought I would ask before I go re-working my code.

paulmrest
  • 414
  • 4
  • 14
  • 1
    FYI - you shouldn't be modifying the `drawRect:` method of any of the custom UI widgets. – rmaddy Sep 30 '15 at 22:33
  • I'm curious as to how you are using exactly the same drawing code for such disparate UI elements. – jscs Sep 30 '15 at 22:37
  • @rmaddy I am curious what you mean. Custom implementations of drawRect in UIView and its subclasses are one of the backbones of Cocoa/Objective-C if you want to add anything to how the class draws itself. https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIView_Class/#//apple_ref/occ/instm/UIView/drawRect: – paulmrest Sep 30 '15 at 22:43
  • Yes, implementing `drawRect:` for your *own* custom views if great. But Apple provided widgets are supposed to be black boxes with private view hierarchies. You shouldn't mess with how the provided widgets are drawn. – rmaddy Sep 30 '15 at 22:45
  • 1
    You could do this at the language level via swizzling. A category clobber in the ancestor class, in addition to being a terrible, bad, no-god, horrible practice, won't work because the intervening subclasses presumably have their own implementations. The only other language-based option would be the one you mention: your own subclass just above your custom classes and below the last framework class, but then again I'm not sure that's actually possible since some of the classes you mention are `UIControl`s, and others direct descendents of `UIView`. – jscs Sep 30 '15 at 22:49
  • @rmaddy So is your advice to write a drawLayer method in a CALayerDelegate class and set each UIView subclass's layer.delegate to that class? – paulmrest Sep 30 '15 at 23:07

1 Answers1

2

The answer seems to be no.

Furthermore, it appears that my idea for the workaround also doesn't work: I was thinking I could write a nice little class that implements the drawLayer method from CALayerDelegate and do the drawing in there, and then in each UIView or UIView subclass' init method do a self.layer.delegate = niceLittleCALayerDelegateClass.

My research, however, happened upon this: https://developer.apple.com/library/ios/documentation/GraphicsImaging/Reference/CALayer_class/#//apple_ref/occ/instp/CALayer/delegate which contains the damning sentence: In iOS, if the layer is associated with a UIView object, this property must be set to the view that owns the layer.

So, double nope.

Against rmaddy's advice I'm going to just write a subclass for each of the UIView subclasses I want to implement this drawing behavior in. We'll see how that goes.

Update:

I can't believe I didn't think of this before, but the "right" way to do this (that is to say without subclassing UIView's subclasses and adding a custom drawRect method to each subclass's subclass), from everything I can find, seems to be to either create a subview or a sublayer with a transparent background that does whatever custom drawing you want.

Obviously this is going to draw on top of the UIView you're actually using, so this would get exceedingly complicated if you're trying to draw things that interact with the default elements of the UIView, but for my purposes (just a simple frame drawn with a UIBezierPath) it seems to work great.

At present I'm not sure if the subview or the sublayer approach is more efficient. If anyone can shed light on that, I'd appreciate it.

paulmrest
  • 414
  • 4
  • 14