Alex Alex - 1 month ago 11
iOS Question

Access UIViewController methods from within subview

I subclassed UITableViewCell and initialized it in a UIView. From the TableView, I need to call a method in the parent ViewController (to perform segues, etc.).

Now, since the ViewController cannot be directly passed to its subviews (in respect of MVC principles), I created a delegation pattern, adding a protocol to the UITableView, and implementing the protocol as delegate in the UIVIewController.

The problem is that since the TableView is instantiated from the UIView, which is a subview of the Viewcontroller, I cannot assign the ViewController as delegate in the cellForRowAtIndexPath method since "self" would point to the View not the ViewController.

If I move all the table methods to the ViewController and instantiate the table from there, everything work as I wish. But that would not allow me to reuse the code in other ViewControllers.

CustomViewController.h

#import "CustomTableViewCell.h"
@interface CustomViewController : UIViewController<CustomTableViewCellDelegate>


CustomViewController.m

- (void)viewDidLoad {
[super viewDidLoad];
// Create a sub-view where the custom table will appear
_customUIView = [[CustomUIView alloc] initWithFrame:CGRectMake(0, 60, self.view.frame.size.width, self.view.frame.size.height-60)];

[self.view addSubview:_customUIView];
[_customUIView populateTheView];
}


CustomUIView.m

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *cellIdentifier = [NSString stringWithFormat:@"cell%ld",(long)indexPath.row];

CustomTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];

if (cell == nil) {
cell = [[CustomTableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellIdentifier];
cell.delegate = self; // << THIS WILL NOT WORK AS IT WOULD POINT TO THE View, RATHER THAN THE ViewController
}
}


CustomTableViewCell.h

@protocol CustomTableViewCellDelegate <NSObject>
-(void)performSegue;
@end
@interface CustomTableViewCell : UITableViewCell
@property(weak, nonatomic) id<CustomTableViewCellDelegate>delegate;
@end


CustomTableViewCell.m

if ([self.delegate respondsToSelector:@selector(performSegue)]) {
[self.delegate performSegue];
}

Answer

The best solution I could find is using notifications. In your viewController set up the observer in the viewWillAppear method. You will also need to remove it in the viewWillDisappear method or the app will crash:

// In the parent view controller
- (void)viewWillAppear:(BOOL)animated {
    // Add observers to perform an action    
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(imageTappedMessageReceived:) name:@"imageTapped" object:nil];
}

-(void) imageTappedMessageReceived:(NSNotification *)notification {
    NSLog(@"The image was tapped");
}

- (void)viewWillDisappear:(BOOL)animated {
    NSLog(@"View controller will disappear");
    [[NSNotificationCenter defaultCenter] removeObserver:self name:@"imageTapped" object:nil];
}

In your subview issue the notification when you need to (button pushed or other logic your have):

// In the subview
[[NSNotificationCenter defaultCenter] postNotificationName:@"imageTapped" object:self userInfo:tempDictionary];

The "imageTappedMessageReceived" method in the viewController will be fired every time the "imageTapped" notification is called in the subview.

Comments