For a while now I've had this dilemma on my mind. A cell in UITableView is essentially a view, thus the class for UITableViewCell should take care of view related things (i.e. presentation methods, layout and so on.) and have no business logic inside of it (usually taken care of the controller). But since we don't have a controller for each cell and only a controller for the whole table, I have trouble figuring out where to put my cell-wise logic. Putting it in the cell itself breaks MVC, but putting it in the table controller makes it hard to determine what cell the method is being called from (I prefer writing subclasses for my senders if the view is action based so I can add properties to help me determine what view this is).
For instance I have a cell, that cell has a UIButton inside of it, when the button is pushed a UIPopover appears. Now where do I put the popover presentation code (The presentation appears from one specific cell, therefore I must know which cell it's being called from.)
I'd like to know what other people do in this case and what are their best practices.
If you put the presentation of the popover inside the cell, then it's the best option. Why ?, because this is not logic, this is view related things and because the button who makes this action is inside your cell, then the code should be inside your cell (or you can send message(delegate) to your viewController to show that).
Then what is the logic ? The logic is for example: calculating, date operations, sending things to server. All these should be inside another object that we can call it module or manager.
The controller can exchange messages between all these objects (view - model), but the view and the module should be separated from each other.
Update:
You may want to take a look at Single Responsibility principle
Normally, it's to your View Controller to handle the "filling" logic for your cells. Cells are recipient that you fill each time.
It is even said in prepareForReuse: of UITableViewCell :
The table view's delegate in tableView:cellForRowAtIndexPath: should always reset all content when reusing a cell.
So indeed, your cells shouldn't hold any logic other than displaying.
If you need logic like button in your cell, you should set a delegate (you create one protocol) to your subclass of UITableViewCell and then hold in your UIViewController the cell logic.
If you cell is unique, I recommend you to define your cell as a static cell (no reuse identifier). And make a strong link to it.
You could subclass UITableView and UITableViewCell. Then, add delegate methods for the button. e.g. tableView:buttonWasPressedForCell: & buttonWasPressedForCell:. The tableView would conform to the cell's delegate and receive the message buttonWasPressedForCell:. Then, the tableView would send the message tableView:buttonWasPressedForCell: to it's delegate, in this case, your controller. This way you know which UITableView and which UITableViewCell the message was sent from.
Example:
ABCTableView.h
#protocol ABCTableViewDelegate <NSObject, UITableViewDelegate>
// You may not need this delegate method in a different UIViewController.
// So, lets set it to optional.
#optional
// Instead of passing the cell you could pass the index path.
- (void)tableView:(ABCTableView *)tableView buttonWasPressedForCell:(ABCTableViewCell *)cell;
#end
#interface ABCTableView : UITableView
// Declare the delegate as an IBOutlet to enable use with IB.
#property (weak, nonatomic) IBOutlet id<ABCTableViewDelegate> delegate;
#end
ABCTableView.m
#implementation ABCTableView
#dynamic delegate;
- (void)buttonWasPressedForCell:(ABCTableViewCell *)cell
{
// Check if the delegate responds to the selector since
// the method is optional.
if ([self.delegate respondsToSelector:#selector(tableView:buttonWasPressedForCell:)])
{
[self.delegate tableView:self buttonWasPressedForCell:cell];
}
}
#end
ABCTableViewCell.h
#protocol ABCTableViewCellDelegate;
#interface ABCTableViewCell : UITableViewCell
// Declare the delegate as an IBOutlet to enable use with IB.
#property (weak, nonatomic) IBOutlet id<ABCTableViewCellDelegate> delegate;
#end
#protocol ABCTableViewCellDelegate <NSObject>
// You may not need this delegate method in a different custom UITableView.
// So, lets set it to optional.
#optional
- (void)buttonWasPressedForCell:(ABCTableViewCell *)cell;
#end
ABCTableViewCell.m
#implementation ABCTableViewCell
- (IBAction)action:(id)sender
{
// Check if the delegate responds to the selector since
// the method is optional.
if ([self.delegate respondsToSelector:#selector(buttonWasPressedForCell:)])
{
[self.delegate buttonWasPressedForCell:self];
}
}
#end
Note:
When you dequeue the cell in tableView:cellForRowAtIndexPath: or add the cell using Interface Builder be sure to set the cell's delegate to the tableView.
E.g.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
ABCTableViewCell *cell = (ABCTableViewCell *)[tableView dequeueReusableCellWithIdentifier:#"Cell"];
cell.delegate = tableView;
return cell;
}
Usually for tasks like this I assign to cell my viewController as delegate (and define some protocol for it). Also, i keep weak reference to object from which I populate my cell, so on button's action I will forward to delegate (viewController) method like this:
- (void)actionOnCell:(UITableViewCell *)cell fromView:(UIView *)sender withItem:(id)sourceItem;
so in this way, I know where from show my popover, and what information (appropriate to sourceItem) show in it.
EDIT Also, if there multiple controls on cell to avoid duplication of pretty similar methods you can just add one parameter to function mentioned above, and define enum of all possible actions
Create an action handler and a data source for the cell. Have your data source conform to the data source protocol (View Model). Then there is no need for the cell to even know about the data model.
In the interface: TableViewCell
#property (nonatomic, weak) id <SomeTableViewCellActionHandler> actionHandler;
#protocol SomeTableViewCellActionHandler <NSObject>
- (void)cell:(SomeTableViewCell *)cell didReceiveStartButtonAction:(UIButton *)button;
- (void)cell:(SomeTableViewCell *)cell didReceivePauseButtonAction:(UIButton *)button;
- (void)cell:(SomeTableViewCell *)cell didReceiveClearButtonAction:(UIButton *)button;
#end
Implementation
- (void)prepareActionsForControls
{
[self.startButton addTarget:self action:#selector(handleStartButtonAction:) forControlEvents:UIControlEventTouchUpInside];
[self.pauseButton addTarget:self action:#selector(handlePauseButtonAction:) forControlEvents:UIControlEventTouchUpInside];
[self.clearButton addTarget:self action:#selector(handleClearButtonAction:) forControlEvents:UIControlEventTouchUpInside];
}
- (void)handleStartButtonAction:(id)sender
{
[self.actionHandler cell:self didReceiveStartButtonAction:sender];
}
- (void)handlePauseButtonAction:(id)sender
{
[self.actionHandler cell:self didReceivePauseButtonAction:sender];
}
- (void)handleClearButtonAction:(id)sender
{
[self.actionHandler cell:self didReceiveClearButtonAction:sender];
}
When you create your cell in the View Controller
create an action handler that conforms to the MyTableViewCellActionHandler protocol, pass the action handler the View Controller if it needs to do presentation.
cell.actionHandler = self.tableViewCellActionHandler;
You may also provide a datasource for your cell and pass in a View Model. (MVVM) This will allow you to keep only presentation code in the cell and keep all of your business logic where it belongs. Separation of concerns.
Related
I'm developing an iOS-App and therefore I use a UITableViewController. Within "cellForRowAtIndexPath" I use cells with reuse identifiers:
[[[UITableViewCell alloc] initWithStyle:UITableViewCellStyle1 reuseIdentifier:textFieldIdentifier];
The problem is that some cells have a dependecy on each other, e.g. if the user enters text in one cell another cell changes its value.
So what is the best way to safe a reference to the cell that has to be changed? The problem is, that if I safe the reference within "cellForRowAtIndexPath", during the callback for "textFieldDidChange" the reference might be broken, e.g. if the cell is not visible or another cell has the adress due to the reuse identifier?!
Don't try to save references to cached cells. Update whatever you need to display in the table's data source and then call reloadData. That way, the table takes care of refreshing visible cells and dealing with the cache...so you don't need to.
I would make an protocol for the cells
Example
#protocol MyProtocol <NSobject>
- (void) changeText:(NSString)theText;
#end
#interface TableViewCell1 : UITableViewCell
#property (nonatomic, weak) id<MyProtocol> delegate;
#end
#implementation TableViewCell1
//put this in the method where you get the value of the textfield
[self.delegate chageText:#"Hello"];
#end
#interface TableViewCell2 : UITableViewCell <MyProtocol>
#end
#implementation TableViewCell2
- (void) chageText:(NSString *)text {
self.textLabel.text = text;
}
#end
I have a small problem with cells in uitableview.
I create properties in one cell and i want to grab values from them and sent to my webservice:
#property (weak, nonatomic) IBOutlet UILabel *profileValueLabel;
#property (weak, nonatomic) IBOutlet UITextField *profileTextfield;
profileValueLabel is just text, it is ok but I need to take profileTextField value and send it with my service out.
Any help?
Your best bet would be to have your UITableViewCell subclass conform to the UITextFieldDelegate, and then create a protocol (delegate) for your table view cell subclass that fires when the UITextField didEndEditing method is called and pass the table view cell itself (perhaps along with the text data you want to send to your web service) into the delegate method so that in your view controller you can determine the index path based on the cell in the table. Then, in your view controller, you can send the data on each cell to the web service whenever you delegate method is fired. This is probably the cleanest approach, but it's best to encapsulate this logic in the table view class itself so as to keep your view controller thinner and make the communication between the two components more explicit.
I hope you have implemented the delegates of UITableView , onE Approach
NSString *tempString1 ;
NSString *tempString2
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell1 = [tableView cellForRowAtIndexPath:indexPath];
tempString1= cell1.textLabel.text; //<-- your label
*tempString2= cell1.textfeild.text ; // u can access the value ,
}
second part of question //
You need to embed tempString and tempString2 variable with the service request .
Your profileTextfield property needs to be in the header file for your UITableViewCell subclass. Then in your UITableViewDataSource controller you would use [myTableView cellForRowAtIndexPath:yourCellsIndexPath].profileTextfield.text once you've determined the index path for the cell whose textfield text you want to grab.
If you want to grab the profileTextfield property as soon as your user is done entering data, you will need to create a protocol in your UITableViewCell subclass and a corresponding delegate property. Then you make a protocol method like -(void)tableViewCell:(myUITableViewCellSubclass *)cell didEndEditingProfileTextFieldWithText:(NSString *)text; Then in your UITableViewCell subclass, set the profileTextField's delegate to your UITableViewCell subclass and use the textFieldDidEndEditing: or textFieldWillReturn: delegate method. In whichever method you use, call [self.delegate tableViewCell:self didEndEditingProfileTextFieldWithText:self.profileTextfield.text] protocol method. Your tableViewController should handle this method, and you can do whatever you need to there.
I don't know if this is considered to be an accepted Objective-C practice or not, so I'm open to other ideas. Here is the idea. I have a table that gets is cells from a custom UITableViewCell. Each of these cells presents an event that the user can attend. As such, I'd like for the user to be able to add them to their calendar directly from the table view.
To accomplish this, I put a button on each table cell - an "add to calendar button". What I am stuck on is how to wire an action from this button back to the UIViewController where the UITableView is a subview. The button is part of a UITableView class and doesn't have visibility to the UIViewController.
I've been trying to implement the delegate pattern suggested by Aaron below. I'm almost there, but something is still disconnected. Here is what I have:
New protocol EventDelegate.h
#protocol EventDelegate <NSObject>
- (void) addToCalendar : (NSString *) strDate;
#end
In MyTableCell.h, I have added this property:
#property (nonatomic, strong) id<EventDelegate> eventDelegate;
In MyTableCell.m, I have added this method:
- (IBAction)addToCalendar:(UIButton *)sender
{
NSLog(#"calling addToCalendar delegate %#", _dayAndTime.text);
[self.eventDelegate addToCalendar:_dayAndTime.text];
}
All is well to this point. When I click on the button that I added to the table cell, I get the output calling addToCalendar delegate Monday, January 13
Over in MyViewController.h, I changed it look like this:
#interface TrainingScheduleViewController : UIViewController <UITableViewDelegate, UITableViewDataSource, EventDelegate>
- (void) addToCalendar:(NSString *)strDate;
#end
And finally, in MyViewController.m I added the method body:
- (void) addToCalendar:(NSString *)strDate
{
NSLog(#"inside delegate");
NSLog(#"%#", strDate);
}
The part that I think might be the problem is where Aaron suggests adding this line of code:
[tableViewController setEventDelegate:self];
First, I'm not sure where to add this line. I put it in viewDidLoad. The compiler wouldn't let me type it verbatim, so the closest thing that I could find was this line:
[self.tableView setDelegate:self];
Maybe I need an additional outlet?
I have to be almost there, but I just don't see what I am still missing. Can anyone help me? Thanks!
Here is some example code that I pulled from my "cellForRowAtIndexPath" method. This is from a table that was built in IB and uses a prototype cell, but the idea is the same no matter how you implement. In this case, I use a tag to identify the button, then reference it when the cell is created:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell" forIndexPath:indexPath];
UIButtonRed *actionButton = (UIButtonRed *)[cell.contentView viewWithTag:4];
[actionButton addTarget:self action:#selector(initializeReorder:) forControlEvents:UIControlEventTouchDown];
return cell;
}
The initializeReorder: method automatically receives (id)sender as a parameter. You can cast that to a table cell and inspect it to get the rest of your info:
- (void)initializeReorder:(id)sender
{
UIButtonRed *actionButton = (UIButtonRed*)sender;
UITableViewCell *cell = (UITableViewCell*)actionButton.superview.superview;
NSIndexPath* cellPath = [self.tableView indexPathForCell:cell];
...
}
Create a protocol for this.
I would define a protocol like this:
#protocol MyEventDelegate <NSObject>
- (void)addToCalendar:(Event*)event;
#end
And add a delegate property to both your UITableViewController class and your MyTableCell class:
#property (nonatomic, strong) id<MyEventDelegate> eventDelegate;
MyViewController should conform to this protocol and implement addToCalendar:
#interface MyViewController : UIViewController <MyEventDelegate>
When your MyViewController object sets up the UITableViewController, pass in a reference to itself:
[tableViewController setEventDelegate:self];
and when your UITableViewController creates each cell, pass it on:
[cell setEventDelegate:self.eventDelegate];
Now, when the IBAction is called in your cell, the cell can call the delegate method on MyViewController like so:
[self.eventDelegate addToCalendar:event];
Xcode 4.6.1 iOS 6 using storyboards
My problem is this
I have a UITableView with dynamic prototype cells on a UIView in a UIViewController (that is itself embedded in a navigation controller) and I want to segue from one specific cell to another view
(Before anyone suggests I should just be using a UITableViewController , I do have other things on the UIView, so i'm set up this way for a reason.)
Now i'm not sure how to go about creating the segue
If I drag from the prototype UITableViewCell to create a segue , all the generated cells automatically call the the segue - when i need only one to do so. This is normal behaviour and I would get around this if i was using a UITableViewController by creating the segue by dragging from UITableViewController and calling [self performSegueWithIdentifier:.... From my didSelectRowAtIndexPathMethod so only the specific cell I want to perform this segue triggers it.
I don't have a UITableViewController in this case - just my UITableView on a UIView that is part of a UIViewController subclass
I've been playing around and I have just discovered that i cannot drag from the UITableView - doesn't let you do that, so that was a deadend.
My only choice that seemed left to me was to drag from the UIViewController
So i tried that and of course XCode throws up an error on the perform segue line telling me i have ... No visible interface for 'LocationTV' declares the selector performSegueWithIdentifier. LocationTv being my tableview subclass.
What is the correct way to attempt to call the new view in this situation
Thank
Simon
First of all segues can be use only between UIViewControllers. So in case you want to perform a segue between two views that are on the same view controller, that's impossible.
But if you want to perform a segue between two view controllers and the segue should be trigger by an action from one view (inside first view controller) well that's possible.
So in your case, if I understand the question, you want to perform a segue when the first cell of a UITableView that's inside of a custom UIView is tapped. The easiest approach would be to create a delegate on your custom UIView that will be implemented by your UIViewController that contains the custom UIView when the delegate method is called you should perform the segue, here is a short example:
YourCustomView.h
#protocol YourCustomViewDelegate <NSObject>
-(void)pleasePerformSegueRightNow;
#end
#interface YourCustomView : UIView {
UITableView *theTableView; //Maybe this is a IBOutlet
}
#property(weak, nonatomic) id<YourCustomViewDelegate>delegate;
YourCustomview.m
#implementation YourCustomview
# synthesise delegate;
//make sure that your table view delegate/data source are set properly
//other methods here maybe
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
if(indexPath.row == 0) { //or any other row if you want
if([self.delegate respondsToSelector:#selector(pleasePerformSegueRightNow)]) {
[self.delegate pleasePerformSegueRightNow];
}
}
}
YourTableViewController.h
#interface YourTableViewController : UIViewController <YourCustomViewDelegate> {
//instance variables, outlets and other stuff here
}
YourTableViewController.m
#implementation YourTableViewController
-(void)viewDidLoad {
[super viewDidLoad];
YourCustomView *customView = alloc init....
customView.delegate = self;
}
-(void)pleasePerformSegue {
[self performSegueWithIdentifier:#"YourSegueIdentifier"];
}
You can create any methods to your delegate or you can customise the behaviour, this is just a simple example of how you can do it.
My Solution
I ended up using a delegation pattern
I made a segue dragging from the my UIViewController - specifically dragging from the viewController icon (the orange circle with a white square in it - from the name bar thats under the view in the storyboard - although you could also drag from the sidebar ) to the view that i wanted to segue to.
I needed to trigger this segue from a table view cell on a table view.
TableView Bit
So i declared a protocol in my tableview header file - which is called LocationTV.h - as follows
#protocol LocationTVSegueProtocol <NSObject>
-(void) makeItSegue:(id)sender;
#end
Below that I declare a property to hold my delegate
#property (nonatomic, strong) id<LocationTVSegueProtocol> makeSegueDelegate;
To actually trigger the segue i called the makeItSegueMethod on my makeSequeDelegate in my didSelectRowAtIndexPath method
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
switch (indexPath.section) {
DLog(#"selected row %d",indexPath.row);
case dLocation:
{
if(indexPath.row == 2){
[_makeSegueDelegate makeItSegue:self];
} else if (indexPath.row == 7){
UIViewController Bit
and set up my UIViewController (named MultiTableHoldingVC) as implementing that protocol
#interface MultiTableHoldingView : UIViewController
<EnviroTVProtocol,LocationTVSegueProtocol> {
}
Below that i declared the protocol method in the list of my classes methods (although i'm not sure that is necessary as the compiler should know about the method as the decalration of implementing a protocol is essentially a promise to implement this method)
-(void) makeItSegue:(id)sender;
And then over in the implementation file of my UIViewController i wrote the method which essentially just calls preformSegueWithIdentifier
-(void) makeItSegue:(id)sender{
[self performSegueWithIdentifier:#"ChooseCountryNow"
sender:sender];
}
And to link it all together,as in the header file I had declared my instance of the tableView as follows
#property (strong, nonatomic) IBOutlet LocationTV *dsLocationTV;
I had to set that tables views delegate property to be self - which I did in my UIViewControllers -(void)ViewDidLoad method
_dsLocationTV.makeSegueDelegate = self;
It all seems a bit of a kludge calling a method to call a method and allprog suggestion is simpler (I cant for the life of me work out why it threw up errors for me) but this works just fine . Thanks to both allprog and danypata for their suggestions.
Hope this is helpful to someone out there
performSegueWithIdentifier: is a method of the UIViewController class. You cannot call it on a UITableView instance. Make your view controller implement the UITableViewDelegate protocol and set it as the delegate for the UITableView.
Another option is that you don't use segues. In the same delegate method do:
OtherViewController ov = [[OtherViewController alloc] init<<some initializer>>];
// Or in case of storyboard:
OtherViewController ov = [self.storyboard instantiateViewControllerWithIdentifier:#"ovidentifier"];
// push view controller
[self.navigationController pushViewController:ov animated:YES];
If the delegate object is different from the view controller, then the easiest solution is to add a weak property to the delegate's class that keeps a reference to the viewController, like this:
#property (weak) UIViewController *viewController;
and set it up in the viewDidLoad of the viewController
- (void) viewDidLoad {
self.tableView1.viewController = self;
}
Make sure that the tableView1 property is declared like this:
#property (IBACTION) (weak) SpecialTableView *tableView1;
Sometimes using the storyboard is more painful than writing the code yourself.
I am currently creating a custom grid view, which means that I am creating a class that has a lot in common with UITableView. One of the things that I want to get right is the communication of the cells and the grid view.
I was therefore wondering how a table view cell talks to its table view. For example, how does the cell notify the table view that its delete button was tapped and the cell needs to be removed from the table view?
There are several possible scenarios, but I am not sure which one is being used by Apple since the headers of UITableView or UITableViewCell reveal this (or am I overlooking something).
Ultimately, the goal is to let the cell and the grid view communicate in private, that is, without exposing any public methods or protocols (if this is possible).
Now a delete button might be a poor example because iOS has a built in method which allows you to delete rows and notify your datasource called:
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
However, for the sake of understanding if you wanted to add a button to your tableview cell and have it perform an action that isn't in the standard iOS library you would create a delegate in your cell and set your tableview's datasource file as the delegate.
Basically you would subclass UITableViewCell like so
MyCustomCell.h
#protocol MyCustomCellDelegate;
#interface MyCustomCell : UITableViewCell
#property (nonatomic, unsafe_unretained) id <MyCustomCellDelegate> delegate; //Holds a reference to our tableView class so we can call to it.
#property (nonatomic, retain) NSIndexPath *indexPath; //Holds the indexPath of the cell so we know what cell had their delete button pressed
#end
/* Every class that has <MyCustomCellDelegate> in their .h must have these methods in them */
#protocol MyCustomCellDelegate <NSObject>
- (void)didTapDeleteButton:(MyCustomCell *)cell;
#end
MyCustomCell.m
#synthesize delegate = _delegate;
#synthesize indexPath = _indexPath;
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self)
{
/* Create a button and make it call to a method in THIS class called deleteButtonTapped */
UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
button.frame = CGRectMake(5, 5, 25, 25);
[button addTarget:self action:#selector(deleteButtonTapped:) forControlEvents:UIControlEventTouchUpInside];
}
return self;
}
/**
* This is the method that is called when the button is clicked.
* All it does is call to the delegate. (Whatever class we assigned to the 'delegate' property)
*/
- (void)deleteButtonTapped:(id)sender
{
[self.delegate didTapDeleteButton:self];
}
Your TableView's datasource would look something like this.
MyDataSource.h
/* We conform to the delegate. Which basically means "Hey you know those methods that we defined in that #protocol I've got them and you can safely call to them" */
#interface MyDataSource : UIViewController <MyCustomCellDelegate, UITableViewDelegate, UITableViewDataSource>
#property (nonatomic,retain) NSArray *tableData;//We will pretend this is the table data
#property (nonatomic,retain) UITableView *tableView;// We will pretend this is the tableview
#end
MyDataSource.m
//We will pretend we synthesized and initialized the properties
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
MyCustomCell *cell = [tableView dequeueReusableCellWithIdentifier: #"MyCustomCell"];
if (!cell)
cell = [[DownloadQueueCell alloc] initWithStyle: UITableViewCellStyleDefault reuseIdentifier: #"MyCustomCell"];
cell.delegate = self; // Make sure we set the cell delegate property to this file so that it calls to this file when the button is pressed.
cell.indexPath = indexPath;// Set the indexPath for later use so we know what row had it's button pressed.
return cell;
}
- (void)didTapDeleteButton:(MyCustomCell *)cell;
{
// From here we would likely call to the apple API to Delete a row cleanly and animated
// However, since this example is ignoring the fact that they exist
// We will remove the object from the tableData array and reload the data
[self.tableData removeObjectAtIndexPath:cell.indexPath];
[self.tableView reloadData];
}
Basically, long story short. For your gridview you would just create a delegate method that tells the user a certain button was pressed.
UITableViewCell items are subviews of UITableView. So you could use it to communicate between cells and tableView. In its turn UITableView has the delegate and datasource to communicate with its controller. This might help.
I'm not sure that a private communication channel is needed.
The table view imposes a delete view adjacent to a given cell by resizing the table view cell and creating a new view in the open space.
The imposed delete view is instantiated with the table view, the index path, and the table view delegate. The delete view handles the touch and sends a message to the table view delegate including the table view and index path. The table view delegate does the work of removing the entry from the data source, animating the cell removal and refreshing the table view. Upon refresh, the table view redraws all the visible cells according to the data source.
You can have your custom cell UIViews have a private property of the type of your Grid View. When you add these cells to your GridView, update that property to the gridView.
I have my custom grid and do it this way.
Another way is having a method in your grid to pass a cell, and that will return you the index. UITableView has those methods too. That way when a button in a cell is pressed, all you have to do is get the cell and pass it to the grid, that will return an index. With that index you access the data...
You may use categories.
You declare your private methods in a separate category, and place it to the separate file. In the implementation file of class which wants to use these private methods, you import this file with private category, and use the private methods. So the public .h of the class which uses them is left intact.
Example:
MyGridViewCell.h:
#interface MyGridViewCell : UIView
// ...
#end
MyGridViewCell.m:
#implementation MyGridViewCell : UIView
// ...
#end
Now the private methods category interface:
MyGridViewCellPrivate.h:
#interface MyGridViewCell (Private)
- (void) privateMethod1;
#end
And implementation:
MyGridViewCellPrivate.m:
#implementation MyGridViewCell (Private)
- (void) privateMethod1
{
// ...
}
#end
Header remains the same as before:
MyGridView.h:
#interface MyGridView : UIView
- (void) publicMethod1;
#end
But the implementation may use the private API:
MyGridView.m:
#import "MyGridViewCell.h"
#import "MyGridViewCellPrivate.h"
- (void) publicMethod1
{
// Use privateMethod1
}