Question

Is there a way to add a category to a class whose header file you can't access?

For testing purposes, I want to add a category to UITableViewCellDeleteConfirmationControl, but the class is (as far as I can tell) part of a private framework.

How can I do that?


Elaboration (per mihirios's request):

I am trying to extend the Frank testing framework to simulate tapping the confirmation button (the big red "Delete" button) that appears when you try to delete a UITableViewCell. Frank adds a tap method to UIControl. For some reason, Frank's usual way of tapping a control does not work for the UITableViewCellDeleteConfirmationControl class (which subclasses UIControl).

I've create a workaround. I added a category to UITableViewCell, with the following method.

- (BOOL)confirmDeletion {
    if (![self showingDeleteConfirmation]) {
        return NO;
    }
    UITableView *tableView = (UITableView *)[self superview];
    id <UITableViewDataSource> dataSource = [tableView dataSource];
    NSIndexPath *indexPath = [tableView indexPathForCell:self];
    [dataSource tableView:tableView
       commitEditingStyle:UITableViewCellEditingStyleDelete
        forRowAtIndexPath:indexPath];
    return YES;
}

This finds the table's data source and invokes its tableView:commitEditingStyle:forRowAtIndexPath: method, which (according to the documentation for UITableView) is what the system does when the user taps the confirmation button.

This works, but I would prefer to make UITableViewCellDeleteConfirmationControl appear to be a tappable button by adding a tap method to it, overriding Frank's default one. The tap method would find the cell that contains the confirmation button, then invoke [cell confirmDeletion].

When I try to declare a category for UITableViewCellDeleteConfirmationControl, the compiler complains that it "can't resolve interface 'UITableViewCellDeleteConfirmationControl'."

When I try to use the header file that someone generated using class-dump, the linker complains that it can't find the symbol _OBJC_CLASS_$_UITableViewCellDeleteConfirmationControl.

Was it helpful?

Solution

For testing purposes, you can always get the class object using NSClassFromString and then use the class_replaceMethod runtime method to do whatever you need. See the Objective-C Runtime Reference for details.

OTHER TIPS

As far as i know you can not use a Category, but you could add the methods manually during runtime.

A Possible way to do this is, to create a new class, implement the methods you want to, and send this methods to UITableViewCellDeleteConfirmationControl using the appropriate objc-runtime functions. There are some things to take care of, like storing the original functions for later use in case of overloading, also in your 'category'-class you have to pay attention when you want to call super, as this will not work, you have to use objc-runtime function objc_msgSendSuper instead.

As Long as you don't need to call super this will do fine:

#import <objc/runtime.h>
#import <objc/message.h>

void implementInstanceMethods(Class src, Class dest) {
    unsigned int count;
    Method *methods = class_copyMethodList(src, &count);

    for (int i = 0; i < count; ++i) {
        IMP imp = method_getImplementation(methods[i]);
        SEL selector = method_getName(methods[i]);
        NSString *selectorName = NSStringFromSelector(selector);
        const char *types = method_getTypeEncoding(methods[i]);

    class_replaceMethod(dest, selector, imp, types);        
    }
    free(methods);
}

a good point to call the method is in main.m, for example:

@autoreleasepool {
        implementInstanceMethods([MyCategory class], NSClassFromString(@"UITableViewCellDeleteConfirmationControl"));
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([YourAppDelegate class]));
}

But i don't know why you not just move the confirmation handling in the controller-class.

As long as the compiler can (eventually) link to the class in question you can create a category for it. The more important question will be how to design the category since it seems you do not have access to the source for the original class.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top