9

The typical UITableView usage pattern is to have the main UIViewController become a target datasource and delegate for the UITableView it is holding on to.

Are there any simple and easy to follow tutorials that would help me figure out how to move the code that pertains to the UITableViewDelegate and UITableViewDataSource methods into a separate class and hook that to my UIViewController instead? I would ideally like to have both the delegate and datasource living in the same class.

Right now, I am creating the UITableView via Interface Builder and connecting its outlet to my controller class.

Typical code:

@interface MyController : UIViewController <UITableViewDelegate, UITableViewDataSource>
{
    IBOutlet UITableview *myTableview;
}

I want to do something more like this:

@interface MyController : UIViewController 
{
    IBOutlet UITableview *myTableview;
}
@end

@interface MyTableSourceDelegate : NSObject<UITableViewDelegate, UITableViewDataSource>
{
}

@implementation MyTableSourceDelegate
    // implement all of the UITableViewDelegate and methods in this class 
@end
Alexi Groove
  • 6,646
  • 12
  • 47
  • 54
  • 1
    possible duplicate of [UITableView issue when using separate delegate/dataSource](http://stackoverflow.com/questions/254354/uitableview-issue-when-using-separate-delegate-datasource) – Jon Limjap Feb 03 '11 at 08:23
  • 3
    Possible duplicate of [UITableView issue when using separate delegate/dataSource](https://stackoverflow.com/questions/254354/uitableview-issue-when-using-separate-delegate-datasource) – Cœur Dec 16 '18 at 06:10

4 Answers4

3

I spend 2 hours to solve this problem:

It's working for me

//  GenreDataSource.h

#import Foundation/Foundation.h

    @interface GenreDataSource : NSObject <UITableViewDataSource> {
        NSArray *dataSource;
        CGSize cellSize;
    }

@property(nonatomic, assign) CGSize cellSize;

@end



//  GenreDataSource.m
#import "GenreDataSource.h"

@implementation GenreDataSource
@synthesize cellSize;

-(id)init{

    self = [super init];
    if ( self != nil ) {

        dataSource = [[NSArray alloc] initWithObjects:@"All",@"Folk",@"Disco",@"Blues",@"Rock",@"Dance",@"Hip-Hop",@"R&B",@"Soul",@"Lounge",@"Techno",@"Bubstep", nil];
    }
    return self;
}

#pragma mark - UITableViewDataSource

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
    return [dataSource count];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
    static NSString *CellIdentifier = @"CellPicker";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {

        cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero] autorelease];
        [cell setSelectionStyle:UITableViewCellSelectionStyleGray];

        //сконфигурируем структуру
        FontLabel *fLabel= [[FontLabel alloc] initWithFrame:CGRectMake(30, 
                                                                       5, 
                                                                       cellSize.width-30, 
                                                                       cellSize.height-5)
                                                   fontName:@"HelveticaNeueCondensedBlack" 
                                                  pointSize:18.0f];
        [fLabel setTextColor:[UIColor darkTextColor]];
        [fLabel setTag:101];
        [fLabel setBackgroundColor:[UIColor clearColor]];
        [cell.contentView addSubview:fLabel];
        [fLabel release];
    }

    FontLabel *fLabel = (FontLabel*)[cell viewWithTag:101];
    [fLabel setText:[dataSource objectAtIndex:indexPath.row]];

    return cell;
}

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
    return 1;
}

@end
shanethehat
  • 15,460
  • 11
  • 57
  • 87
WINSergey
  • 1,977
  • 27
  • 39
2

First thing is if you're using a UITableViewController subclass with interface builder you will want to disconnect the delegate and datasource outlets that are already hooked up by default. (Hint, look in the connections inspector). Check even if you have a tableView inside a viewController.

Second create your classes and make sure they conform to <UITableViewDelegate> and <UITableViewDataSource>. You're probably going to have to declare this contract in the .h file if you're using objc.

Third, In your view controller instantiate this class or two separate classes somewhere like viewDidLoad, and then assign self.tableView.delegate = myCustomDelegateInstance and self.tableView.dataSource = myCustomDataSourceInstance.

Now, any calls that come through the controller will be dispatched to your custom handlers. Pretty basic.

The only reason to really do this is if you 1) have a very bloated controller, or 2) you need to reuse the dataSource and delegate methods somewhere else and you want to avoid code repetition. Otherwise, it's probably better practice to leave it put.

SmileBot
  • 19,393
  • 7
  • 65
  • 62
0

You can create separe classes (with UITableViewDelegate , UITableViewDataSource) and add them in IB as external files and link the IBActions

CiNN
  • 9,752
  • 6
  • 44
  • 57
  • I have the separate class that implements UITableViewDelegate, UITableViewDataSource in MyController. How do you link these instances as IBActions? – Alexi Groove Jan 19 '10 at 23:14
0

In IB, you can drag a 'External Object' from Library->Cocoa Touch->Controllers into your xib window. You can then select that object, view the inspector, and set the class. It is now available to serve as a delegate, etc.

shawnwall
  • 4,549
  • 1
  • 27
  • 38
  • Does IB take care of the instantiation of this class? Do I need to do anything to use it? I tried this but my app seems to be crashing due to 'EXC_BAD_ACCESS'. – Alexi Groove Jan 20 '10 at 00:49
  • Yep, you're right. I get the same thing now that I can test it out. I've redone this by dragging an Object instead of an External Object. Next, I declared an IBOutlet typed to the Object item's type in the File's Owner controller. I then linked up the target new control's delegate to the new Object, the new Object's view to the new control, and the new IBOutlet to the new Object... and it runs! – shawnwall Jan 20 '10 at 01:44
  • FYI the reason for the IBOutlet is that the new control needs to be retained after instantiation. If you don't do this is gets destroyed immediately and essentially you are getting a null pointer exception. – shawnwall Jan 20 '10 at 01:45
  • This is a possibility, but if you're just using it to setup a delegate and datasource outlet to a separate object you're better off doing it in code where everyone can easily see what you're up to. Also, if you need to pass any data over you're going to be creating an outlet from this proxy to the main controller. You might as well just new up the object and explicitly declare the delegate and datasource assignment for everyone to see. So, in short, I think this is a poor solution although perfectly possible. – SmileBot Apr 21 '15 at 02:03