0

I have a custom subclass of NSPopUpButtonCell so that I can overwrite its drawBezelWithFrame:inView: method.

Currently, I have to create a fresh instance using initTextCell:pullsDown: and then copy all its properties by hand. That's rather tedious and error-prone as I may be missing some properties.

I wonder if I can use initWithCoder: for this task instead. I imagine I should be able to file the data from the existing NSPopUpButtonCell instance into an NSCoder object, e.g. into NSKeyedArchiver, and then file that data back into my NSPopUpButtonCell subclass. But I can't figure out how to accomplish that.

Thomas Tempelmann
  • 11,045
  • 8
  • 74
  • 149
  • How do you create the popup button, programmatically or XIB? – Willeke May 24 '16 at 23:50
  • @Willeke From a Nib, and I alteady learned that I could solve that particular one more easily. Still, I like to learn if there is a way to clone any object in a subclass of it. – Thomas Tempelmann May 26 '16 at 07:45

2 Answers2

1

If all your subclass does is override methods, i.e. it does not add additional fields, then you can swizzle the class of your original NSPopupButtonCell, or a copy of it, so it becomes and instance of your subclass.

Apple implements KVO using this technique, in that case using a dynamically generated subclass. If your case the subclass is static, making it a lot easier, the outline of the code is:

object_setClass(<an NSPopupButtonCell instance>,
                [<your subclass> class]);

If you can safely change the class on the original instance then this involves no object creation or copying at all.

Remember you can only do this if the subclass changes behaviour only.

For another explanation see this answer.

HTH

Community
  • 1
  • 1
CRD
  • 52,522
  • 5
  • 70
  • 86
0

Looks like I just used the wrong functions before. The solition is pretty straight-forward.

This code duplicates the properties of a NSPopUpButtonCell into a subclass of it, so that I can overwrite its draw... methods:

@interface MyPopup : NSPopUpButton
@end

@implementation MyPopup
- (instancetype)initWithCoder:(NSCoder *)coder {
    self = [super initWithCoder:coder];
    if (self) {
        // Set up the archiver
        NSMutableData *data = [NSMutableData data];
        NSKeyedArchiver *arch = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
        // Archive the cell's properties
        [self.cell encodeWithCoder:arch];
        [arch finishEncoding];
        // Set up the unarchiver
        NSKeyedUnarchiver *ua = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
        // Create a subclass of the cell's class, using the same properties
        MyCell *newCell = [[MyCell alloc] initWithCoder:ua];
        // Assign the new subclass object as the NSButton's cell.
        self.cell = newCell;
    }
    return self;
}

Another, cleaner, way to use a custom NSButtonCell subclass is shown here: Using full width of NSPopupButton for text, no arrows

Community
  • 1
  • 1
Thomas Tempelmann
  • 11,045
  • 8
  • 74
  • 149