0

I'm trying to subclass a TableView, and a method isn't getting called.

in the TouchTableView.h I have

#import <UIKit/UIKit.h>
#import <AVFoundation/AVFoundation.h>
#import "SimpleTableCell.h"



@protocol myTableViewDelegate;

@interface TouchTableView : UITableView <UITableViewDelegate, UITableViewDataSource, AVAudioRecorderDelegate, AVAudioPlayerDelegate, UITextViewDelegate, UITextFieldDelegate, UIAlertViewDelegate>

@property (nonatomic, assign) id<myTableViewDelegate> myDelegate;
@property (strong, nonatomic) NSMutableArray *sortedFiles;
@property (strong, nonatomic) NSString *simpleTableIdentifier;
@property (strong, nonatomic) SimpleTableCell *cell;
@property BOOL inverted;

-(void)refreshTable;

@end


@protocol myTableViewDelegate <NSObject>

- (void)selectedFile:(TouchTableView *)tableView withURL: (NSURL *) tableViewURL IndexPath:(NSIndexPath *)indexPath;
-(void)didDelete:(TouchTableView *)tableView IndexPath:(NSIndexPath *)indexPath;
-(void)setSortedFile:(TouchTableView *)tableView;

@end

and in the TouchTableView.m I have the method

-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {


        NSArray *folders = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
        NSString *documentsFolder = [folders objectAtIndex:0];
        NSString *dataPath = [documentsFolder stringByAppendingPathComponent:@"/Audio"];
        NSString *fileName = [NSString stringWithFormat:@"%@/%@",dataPath,[[sortedFiles objectAtIndex:indexPath.row] objectForKey: @"path"]];

        NSURL *tableViewUrl = [NSURL fileURLWithPath:fileName isDirectory:
               NO];


    if ([self.myDelegate respondsToSelector:@selector(selectedFile:withURL:IndexPath:)]) {
        [self.myDelegate selectedFile:self withURL:tableViewUrl IndexPath:indexPath];
    }

}

It appears that the self.delegate doesn't respond to the selector. It skips over and doesn't go to the method. What am I doing wrong?

MScottWaller
  • 3,321
  • 2
  • 24
  • 47

4 Answers4

0

Is the TableView delegate being assigned to self? By default it is nil so won't respond to the selector.

- (instancetype)initWithCoder:(NSCoder *)aDecoder {
    if (!(self = [super initWithCoder:aDecoder])) {
        return self;
    }

    self.delegate = self;
    return self;
}
Gavin Bunney
  • 1,240
  • 1
  • 12
  • 12
0

I think you should be using:

if ([tableview.delegate respondsToSelector:@selector(selectedFile:withURL:IndexPath:)]) {
        [tableview.delegate selectedFile:self withURL:tableViewUrl IndexPath:indexPath];
    }

Your updated delegate is a property in the table view and not the view controller.

You have also redeclared the delegate property in your subclass which could be an issue as the table view already has a property called delegate. Perhaps you should call make yours separate and call yours: myDelegate?

Rory McKinnel
  • 7,936
  • 2
  • 17
  • 28
  • I've updated my code, although it won't let me say tableView.myDelegate, only tableView.delegate, which doesn't have the method. self.myDelegate works however. Even so, it's still passing over the method. – MScottWaller Mar 03 '15 at 02:12
  • Try `((TouchTableView *)tableView).myDelegate` since xcode code checked needs to know its your extended UITableView. Also make sure you assigned myDelegate to whatever is implementing the delegate calls otherwise it will be nil. – Rory McKinnel Mar 03 '15 at 11:24
  • Also using the table as its own delegate seems a bit odd to me. It makes your delegate redundant as its just redirecting method calls back on itself. You only need myDelegate if you were going to delegate that functionality outside of your subclass. Otherwise just add those methods to your subclass and call them via [self ]; – Rory McKinnel Mar 03 '15 at 11:37
0

It's impossible to add new method to UITableViewDelegate, because you don't have authorization to modify the UIKit. However it's possible to intercept the delegate because it's all about messages passing in Objective-C. The first thing I would like to tell you is to declare your own delegate as weak reference instead of assign.

Whats the difference between 'weak' and 'assign' in delegate property declaration

Now I'm going to tell you how to intercept the delegate and make it look like no difference from the UITableViewDelegate.

1) Declare the delegate with your custom protocol in the header file.

@property (nonatomic, weak) id<UITableViewDelegate, myTableViewDelegate > delegate;

2) Add the following methods to create an object which will be used in intercepting the delegate.

@interface MessageInterceptor : NSObject

@property (nonatomic, assign) id receiver;
@property (nonatomic, assign) id middleMan;

@end

@implementation MessageInterceptor
@synthesize receiver;
@synthesize middleMan;

- (id)forwardingTargetForSelector:(SEL)aSelector {
    if ([middleMan respondsToSelector:aSelector]) { return middleMan; }
    if ([receiver respondsToSelector:aSelector]) { return receiver; }
    return [super forwardingTargetForSelector:aSelector];
}

- (BOOL)respondsToSelector:(SEL)aSelector {
    if ([middleMan respondsToSelector:aSelector]) { return YES; }
    if ([receiver respondsToSelector:aSelector]) { return YES; }
    return [super respondsToSelector:aSelector];
}

@end

3) Add a global object for it

MessageInterceptor *delegate_interceptor;

4) Initialize the interceptor

delegate_interceptor = [[MessageInterceptor alloc] init];
[delegate_interceptor setMiddleMan:self];
[super setDelegate:(id)delegate_interceptor];

5) Override the setter and getter for delegate

- (void)setDelegate:(id)newDelegate
{
    [super setDelegate:nil];
    [delegate_interceptor setReceiver:newDelegate];
    [super setDelegate:(id)delegate_interceptor];
}

- (id)delegate
{
    return delegate_interceptor.receiver;
}

6) Override the delegate you want

-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    // Call the default method again to make sure it follow the original behavior
    if ([self.delegate respondsToSelector:@selector(tableView:didSelectRowAtIndexPath:)]) {
        [self.delegate tableView:tableview didSelectRowAtIndexPath:indexPath];
    }

    NSArray *folders = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsFolder = [folders objectAtIndex:0];
    NSString *dataPath = [documentsFolder stringByAppendingPathComponent:@"/Audio"];
    NSString *fileName = [NSString stringWithFormat:@"%@/%@",dataPath,[[sortedFiles objectAtIndex:indexPath.row] objectForKey: @"path"]];

    NSURL *tableViewUrl = [NSURL fileURLWithPath:fileName isDirectory:
           NO];
    // Call the custom method which is declared in your protocol
    if ([self.delegate respondsToSelector:@selector(selectedFile:withURL:IndexPath:)]) {
        [self.delegate selectedFile:self withURL:tableViewUrl IndexPath:indexPath];
    }
}

The last step, Don't forget to adopt the protocol at the file you create the tableview.

@interface TheFileUsingTableView ()
<UITableViewDelegate, myTableViewDelegate>
@end
Community
  • 1
  • 1
Chris
  • 428
  • 2
  • 10
0

I added this to my viewDidLoad. Just had to set the delegate to self. Now it responds to the selector.

self.touchTableView = [[TouchTableView alloc] init];

[self.tableView setDelegate:self.touchTableView];
[self.tableView setDataSource:self.touchTableView];

self.touchTableView.myDelegate = self;
MScottWaller
  • 3,321
  • 2
  • 24
  • 47