如何使一个对象成为自己的委托,同时仍允许其他人成为其委托者?

时间:2013-10-17 00:48:39

标签: ios objective-c delegates

我想创建一个继承自UITableView的新类MYTableView。 MYTableView将具有存储其所有数据的NSArray属性。这意味着我不再需要担心实现numberOfRowscellForRow,因为MYTableView将是它自己的数据源并提供必要的数据。另外,我希望MYTableView在didSelectRow方法中有一些自定义逻辑,所以我将其设置为自己的委托。

现在问题是......我有一个TVC(一个UITableViewController)对象,它有一个MYTableView实例。我想覆盖didSelectRow方法中的逻辑。也就是说,除了TVC的逻辑之外,我还希望MYTableView的逻辑发生。

解决方案1: tableView.delegate = TVC
显然这不起作用。如果我尝试将tableView的委托设置回TVC,那么是的,TVC的didSelectRow方法被调用,但是MYTableView的逻辑从未被调用,因为我们劫持了委托。

解决方案2: tableView.delegate2 = TVC
我可以创建第二个代理(参见下面的代码)并将TVC设置为delegate2。然后我会从tableView.delegate的didSelectRow方法中调用delegate2的didSelectRow方法。

问题:

  • delegatedelegate2变得丑陋。此外,如果用户将委托设置为其他内容,则事情会中断。
  • 我必须转发TVC可能想要实施的每一件事。

问题:

因为这不是最佳解决方案。你能想到更好的解决方案吗?

如果我无法控制继承的类(例如,如果我继承自UITableView),我的选择是什么?

如果我确实可以控制继承的类(例如我使用的是我拥有源代码的假设UITableView),那么实现它以实现这种功能的最佳方法是什么?

注意:

  • didSelectRow目前在这些示例中只有一行代码。显然,如果它只是一行代码,那么这不是一个大问题,我会找到解决方法。真正的代码是许多代码行。

完整代码如下:(对于解决方案2)

// ---------------------------------------------------
// VC.m
// ---------------------------------------------------

@implementation VC

- (MYTableView *)myTableView {
    return (MYTableView *)self.tableView;
}

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    self.navigationItem.leftBarButtonItem = self.editButtonItem;

    UIBarButtonItem *addButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(insertNewObject:)];
    self.navigationItem.rightBarButtonItem = addButton;
    self.myTableView.delegate2 = self;
}

- (void)insertNewObject:(id)sender
{
    static NSInteger i = 0;
    if (i % 3 == 0) {
        [self.myTableView insertObject:[NSNull null] inDataArrayAtIndex:0];
    } else {
        [self.myTableView insertObject:[NSDate date] inDataArrayAtIndex:0];
    }
    i++;
}

#pragma mark - MYTableViewDelegate

- (void)myTableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    NSLog(@"Selected row: %@", indexPath);
}

@end


// ---------------------------------------------------
// MYTableView.h
// ---------------------------------------------------

@class MYTableView;

@protocol MYTableViewDelegate <UITableViewDelegate>
@optional
- (void)myTableView:(MYTableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath;
@end

@interface MYTableView : UITableView <UITableViewDataSource, UITableViewDelegate>
@property (nonatomic, weak) id<MYTableViewDelegate> delegate2;
- (void)insertObject:(NSObject *)object inDataArrayAtIndex:(NSUInteger)index;
@end

// ---------------------------------------------------
// MYTableView.m
// ---------------------------------------------------

@interface MYTableView ()
@property (nonatomic, strong) NSMutableArray *dataArray;
@end

@implementation MYTableView

- (void)commonInit {
    self.dataArray = [NSMutableArray array];
    self.dataSource = self;
    self.delegate = self;
}

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

- (id)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
        [self commonInit];
    }
    return self;
}

- (void)insertObject:(NSObject *)object inDataArrayAtIndex:(NSUInteger)index {
    [self.dataArray insertObject:object atIndex:index];
    NSIndexPath *indexPath = [NSIndexPath indexPathForRow:index inSection:0];
    [self insertRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
}

#pragma mark - UITableViewDataSource

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

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    NSString * const identifier = @"String Cell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
    if (!cell) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier];
    }

    cell.textLabel.text = [self.dataArray[indexPath.row] description];
    return cell;
}

#pragma mark - UITableViewDelegate

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    NSObject *object = self.dataArray[indexPath.row];
    if ([NSNull null] == object) {
        [self deselectRowAtIndexPath:indexPath animated:YES];
    }

    if ([self.delegate2 respondsToSelector:@selector(myTableView:didSelectRowAtIndexPath:)]) {
        [self.delegate2 myTableView:self didSelectRowAtIndexPath:indexPath];
    }
}


@end

2 个答案:

答案 0 :(得分:0)

您可以使用两个委托,但覆盖setDelegate方法,使事物至少看起来更干净。我在UITextView的子类中做了类似的事情:https://github.com/mbachrach/GCPTextView

答案 1 :(得分:0)

如果您创建了一个单独的类,它只是根据您的数据源实现UITableViewDataSource协议,那该怎么办?这可以是一个NSObject,或者我想你的MyTableView,如果你真的想这样做的话。这是一个非常高度可重复使用的课程,将使您的生活更加轻松。

对于双委托问题,如果您通过MyTableView中的UITableViewDelegate处理行的选择但设置了基于块的系统来选择视图控制器,该怎么办呢?

void(^RowSelected)(void)(NSObject *selectedObject, NSIndexPath *indexPath)