In tableViewControllerA.h I declare a delegate ,and in tableViewControllerA.m I implement the method when click a cell of the tableView, A will pass the value to B, and execute the perfromSegueWithIdentifier to jump to tableViewControllB,in tableViewControllB.m the getting method is called, and I get the value, but after the viewDidLoad() method call, the value become nil. Please help me to solve the problem.By the way , the property of the value is strong,nonatomic.
DTZHomeTableViewController.h
#import <UIKit/UIKit.h>
#import "PassData.h"
#protocol PassDataDelegate
-(void)passData:(PassData*) dataToProgram;
#end
#interface DTZHomeTableViewController :
UITableViewController<UITableViewDelegate,UITableViewDataSource>
#property(assign,nonatomic) id<PassDataDelegate> dataDelegate;
#end
PassData is an user-defined class
DTZHomeTableViewController.m
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
ProgramTableViewController *programViewController=
[[ProgramTableViewController alloc]init];
self.dataDelegate=programViewController;;
PassData *jump=[[PassData alloc]init];
jump.livingStatus=YES;
jump.role=0;
jump.index=indexPath.row;
jump.curOnlineNum=305605;
[self.dataDelegate passData:jump];
[self performSegueWithIdentifier:#"showSelectedProgram" sender:self];
}
ProgramTableViewController.h
#import <UIKit/UIKit.h>
#import "DTZHomeTableViewController.h"
#interface ProgramTableViewController :
UITableViewController<PassDataDelegate,UITableViewDataSource,UITableViewDelegate>
#property(strong,nonatomic) PassData * dataFromHome;
#end
ProgramTableViewController.m
#implementation ProgramTableViewController
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(#"view did load%#",self.dataFromHome); // log :null
}
- (void) passData:(PassData *)dataToProgram
{
self.dataFromHome=dataToProgram;
}
#end
Delegation isn't the proper way to pass data to a segued view controller. Instead, you want to pass the data in prepareForSegue:sender:
Remove the protocol and delegate.
Add properties to your ProgramTableViewController for the data you want to pass.
#property (nonatomic, assign) BOOL livingStatus;
#property (nonatomic, assign) NSInteger role;
#property (nonatomic, assign) NSInteger row;
#property (nonatomic, assign) NSInteger curOnlineNum;
In prepareForSegue:sender:, set the ProgramTableViewController's passed properties to the values associated with the selected row:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)__unused sender
{
if ([[segue identifier] isEqualToString:#"showDetail"])
{
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
ProgramTableViewController *controller = (ProgramTableViewController *)[segue destinationViewController];
controller.livingStatus = YES;
controller.role = 0;
controller.row = indexPath.row;
controller.curOnlineNum = 305605;
}
}
You shouldn't allocate and init ProgramTableViewController in tableView:didSelectRowAtIndexPath:. Your segue will instantiate the destination view controller for you.
You don't even need to have a tableView:didSelectRowAtIndexPath: method. You can create a push segue from the tableViewCell to ProgramTableViewController, right within Storyboard. Give the segue an identifier (such as "showDetail"). Then in prepareForSegue, you assign the values, as I explained.
When you tap on that row, the segue will create a ProgramTableViewController, set the parameters, and present it for you.
Related
I have a custom UITableViewCell xib that has two buttons on it. An edit button, designed to pass the info for that cell into a new view controller is the one I am currently having issues with. I create a property for the TableViewController in my custom cell, and hook up the button to this code:
-(IBAction) editItem {
self.itemsList = [[TheItemsList alloc] init];
[self.itemsList editItem];
NSLog(#"EDIT");
}
Inside TheItemsList I have this code:
-(void)editItem {
NSLog(#"EDITAGAIN");
EditItem *detailViewController = [[EditItem alloc] initWithNibName:#"EditItem" bundle:nil];
detailViewController.selectedCountry = self.selectedCountry;
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
detailViewController.therow = indexPath.row;
//Pass the selected object to the new view controller.
// Push the view controller.
[self.navigationController pushViewController:detailViewController animated:YES];
}
The console prints my NSLog for each method, but the view does not push. This code was previously used in the didSelectForIndexPath part of the table view, so I know that the view controller it is pushing is fine, I just can't figure out what is going on here.
UPDATE:
I have it SOMEWHAT working, emphasis on the somewhat.
ItemCell.h
#import <UIKit/UIKit.h>
#import "TheItemsList.h"
#class TheItemsList;
#class ItemCell;
#protocol ItemCellDelegate <NSObject>
#required
-(void)editTheItem:(ItemCell *)theCell;
#end
#interface ItemCell : UITableViewCell
#property (assign) id <ItemCellDelegate> delegate;
#property (nonatomic, retain) IBOutlet UILabel *itemName;
#property (nonatomic, retain) IBOutlet UILabel *itemPrice;
#property (nonatomic, retain) IBOutlet UILabel *itemAisle;
#property (nonatomic, retain) IBOutlet UIImageView *thumbnail;
#property (nonatomic, retain) TheItemsList *itemsList;
#end
.m
#import "ItemCell.h"
#import "EditItem.h"
#implementation ItemCell
#synthesize itemName = _itemName;
#synthesize itemAisle = _itemAisle;
#synthesize itemPrice = _itemPrice;
#synthesize thumbnail = _thumbnail;
#synthesize itemsList, delegate;
- (void)awakeFromNib {
[super awakeFromNib];
// Initialization code
}
-(IBAction) editItem {
[self.delegate editTheItem:self];
}
#end
Now, in the TableView
.h
#import <UIKit/UIKit.h>
#import "ItemCell.h"
#class ItemCell;
#protocol ItemCellDelegate;
#interface TheItemsList : UITableViewController <ItemCellDelegate>
.m
-(void)editTheItem:(ItemCell *)theCell {
NSLog(#"EDITAGAIN");
EditItem *detailViewController = [[EditItem alloc] initWithNibName:#"EditItem" bundle:nil];
detailViewController.selectedCountry = self.selectedCountry;
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
detailViewController.therow = indexPath.row;
//Pass the selected object to the new view controller.
// Push the view controller.
[self.navigationController pushViewController:detailViewController animated:YES];
}
What I'm running into is two problems.
1, the first item in the tableview doesn't do anything when the button is pressed.
2, the other rows will run code, but it only does it for the first row's data.
You should use the delegate pattern inside your UITableViewCell to delegate the button press back to the UIViewController which contains the UITableView using the cell.
In cellForRowAt:IndexPath you then retrieve the cell and set its delegate like cell.delegate = self. In the view controller you implement the delegate and handle the action.
In the cell you setup a delegate variable (var delegate: MyCellDelegate?) and inside the editItem function you make a call to the delegate like self.delegate?.editItem()
Your issue is basically because of this line
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
On clicking the button on the cell, it does not select the row necessarily. The button takes the touch. A way to solve this would be to find the NSIndexPath of the cell in which the button is present and then do your operations with it. A way to do is to use the cell reference that is being supplied from the editAction delegate
-(void)editTheItem:(ItemCell *)theCell
Replace [self.tableView indexPathForSelectedRow] with [self.tableView indexPathForCell:theCell]
I am trying to pass selected cell text from CategoryViewController to DescribeViewController. But it does not call the method in the DescribeViewController method.
CategoryViewController.h
#import <UIKit/UIKit.h>
#protocol CategoryViewControllerDelegate <NSObject>
- (void)didSelectRow:(NSString *)cellDataString;
#end
#interface CategoryViewController : UIViewController<UITableViewDelegate, UITableViewDataSource>
#property (weak, nonatomic) id<CategoryViewControllerDelegate> delegate;
#end
CategoryViewController.m
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [categoryTableView cellForRowAtIndexPath:indexPath];
NSString *cellText = cell.textLabel.text;
[self.delegate didSelectRow:cellText];
[[self navigationController] popViewControllerAnimated:YES];
}
DescribeViewController.h
#import <UIKit/UIKit.h>
#import "CategoryViewController.h"
#interface DescribeViewController : ProductAwareBaseViewController<UITextFieldDelegate, CategoryViewControllerDelegate>
The following didSelectRow method is not getting called. I could not able to find out the root of the problem.
DescribeViewController.m
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
CategoryViewController *popoverTableViewController = [[CategoryViewController alloc] init];
popoverTableViewController.delegate = self;
}
- (void)didSelectRow:(NSString *)cellDataString
{
self.cellDataString = cellDataString;
}
ProductAwareBaseViewController.h
#import UIKit;
#class Product;
#interface ProductAwareBaseViewController : UIViewController
#property (nonatomic, strong) Product *product;
#end
ProductAwareBaseViewController.m
#import "ProductAwareBaseViewController.h"
#import "Product.h"
#interface ProductAwareBaseViewController ()
#end
#implementation ProductAwareBaseViewController
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.destinationViewController isKindOfClass:[ProductAwareBaseViewController class]]) {
ProductAwareBaseViewController *vc = (ProductAwareBaseViewController *)segue.destinationViewController;
vc.product = self.product;
}
}
#end
Try setting the delegate object in didSelectRow. And call that delegate method after that. Because delegate is weak, may be it is released from the memory.
CategoryViewController *popoverTableViewController = [[CategoryViewController alloc] init];
popoverTableViewController.delegate = self;
UITableViewCell *cell = [categoryTableView cellForRowAtIndexPath:indexPath];
NSString *cellText = cell.textLabel.text;
[self.delegate didSelectRow:cellText];
[[self navigationController] popViewControllerAnimated:YES];
Most common reason for delegate method not being called is dealing with incorrect objects.
Ensure that CategoryViewController object created from
DescribeViewController is the same which you are presenting on
screen and that the delegate is being set on the same object. I truly believe you are creating a new CategoryViewController object and setting delegate on that.
In DescribeViewController, before calling delegate, check the
existence of delegate and that it implements the protocol method (if
its an optional method). This is a safety check, you can also put a NSLog statement to double check if your delegate exists or not. You are failing here.
->
if (delegate && [delegate respondsToSelector:(didSelectRow:)]) {
[self.delegate didSelectRow:cellText];
}
PS: If you are segueing from DescribeViewController to CategoryViewController then you set delegate in prepareForSegue:.
Follow these guidelines and I am sure you would be able to fix your issue!
try
#property (nonatomic, weak) id <CategoryViewControllerDelegate> delegate;
for declaring your delegate.
EDITED
try for checking the delegate is returning some value or not.
By this, whenever yo are setting the values.
if(self.delegate && [self.delegate respondsToSelector:#selector(didSelectRow:)])
{
[self.delegate didSelectRow:(NSString *) cellDataString];
}
and also if you are using the segue to transfer the data between two controllers then check there for the delegates.
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
DescribeViewController *obj = (DescribeViewController *)segue.destinationViewController;
obj.delegate =self;
}
i think this will help you.
I am using static UITableView to show profile of a customer.
It has SelectCountryCell to select country which, when clicked, opens new UITableView with country list.
When I select country from Country TableView, It should display in previous TableView's SelectCountryCell.
How do I do this?
You should bind the static cells to outlets in your UITableViewController and put the profile syncing methods to the - viewWillAppear method.
When the user changes the country in the Country list the profile updates. After that when the user moves back to the UITableViewController instance with static content the profile data will be automatically updated.
You can define a delegate in your CityTableView and then define a method in this delegate.
You should realize this method in your CountryTableView.
Then you may get what you want.
I only offer you a thought. You should find the detail by yourself.
MasterViewController.h
#import "DetailViewController.h"
#interface MasterViewController : UITableViewController <DetailProtocol> // Note this.
#property (strong, nonatomic) DetailViewController *detailViewController;
#property (strong, nonatomic, readwrite) NSString *selectedCountry;
#end
MasterViewController.m
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if (!self.detailViewController) {
self.detailViewController = [[DetailViewController alloc] initWithNibName:#"DetailViewController" bundle:nil];
}
self.detailViewController.delegate = self; // THIS IS IMPORTANT...
[self.navigationController pushViewController:self.detailViewController animated:YES];
}
// Note this -- It's a delegate method implementation
- (void)setCountry:(NSString *)country
{
self.selectedCountry = country;
}
DetailViewController.h
#protocol DetailProtocol <NSObject>
-(void)setCountry:(NSString *)country;
#end
#interface DetailViewController : UIViewController
#property (strong, nonatomic) IBOutlet UITableView *tableView;
#property (unsafe_unretained) id <DetailProtocol> delegate; // Note this
#end
DetailViewController.m
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[self.tableView deselectRowAtIndexPath:indexPath animated:YES];
[self.delegate setCountry:[countries objectAtIndex:indexPath.row]]; // Note this
[self.navigationController popViewControllerAnimated:YES];
}
i've been trying to implement this protocol for several hours and it doesn't seem to work for some reason. Basically i have a split view which has a view controller and a table controller, one class holds these two together. The main class creates an instance of the table and runs perfectly, but if i select a cell i want the view controller to react. So i wanted to create a protocol for when a table cell is selected it will do something in the main class.
TableSplitViewController, this is the main class:
#interface TableSplitViewController : UIViewController <updateView>
{
ChildrenTableViewController *firstController;
IBOutlet UITableView *firstTable;
IBOutlet UITableViewCell *tablecell;
NSString *name;
}
#property (nonatomic, strong) NSString *name;
#property (nonatomic, strong) IBOutlet UILabel *childnamelabel;
#end
THis is the TableSplitViewController.m:
- (void)viewDidLoad
{
[super viewDidLoad];
if (firstController == nil) {
firstController = [[ChildrenTableViewController alloc] initWithStyle:UITableViewStyleGrouped];
}
[firstTable setDataSource:firstController];
[firstTable setDelegate:firstController];
firstController.view = firstController.tableView;
// Do any additional setup after loading the view.
}
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"ShowChildrenDetails"]) {
ChildrenDetailViewController *detailViewController = [segue destinationViewController];
NSIndexPath *myIndexPath = [firstController.tableView indexPathForSelectedRow];
detailViewController.childrenDetailModel = [[NSArray alloc]
initWithObjects: [firstController.childname objectAtIndex:[firstController.index row]], nil];
}
}
- (void) setNameLabel:(NSString *)sender
{
// self.name = sender;
NSLog(#"ran");
}
This is the ChildrenTableViewController.h:
#protocol updateView <NSObject>
#required
- (void) setNameLabel:(NSString *)sender;
#end
#interface ChildrenTableViewController : UITableViewController
{
NSIndexPath *index;
id <updateView> delegate1;
}
#property (nonatomic, strong) NSMutableArray *childname;
#property (nonatomic, strong) NSIndexPath *index;
#property (retain) id delegate1;
#end
This is the critical part of ChildrenTableViewController.m:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[[self delegate1] setNameLabel:[self.childname objectAtIndex:[indexPath row]]];
NSLog(#"rannn");
As you can see in the last code i'm trying to call the method using the protocol function. It doesn't seem to work for some reason, i've put in NSLOG and it doesn't even run the setNameLabel method at all. :( Will appreciate any help offered :)
In the code above I cant see you setting the delegate as so:
- (void)viewDidLoad
{
[super viewDidLoad];
if (firstController == nil) {
firstController = [[ChildrenTableViewController alloc] initWithStyle:UITableViewStyleGrouped];
}
[firstTable setDataSource:firstController];
[firstTable setDelegate:firstController];
firstController.view = firstController.tableView;
// Set up the delegate for the controller
[firstController setDelegate1:self];
// Do any additional setup after loading the view.
}
Also, the delegate property should usually be (weak) rather than (retain).
I have a UITableView, which when I select a row, I want to segue to another view, where the details of the tank are displayed... The segue happens, but the delegate does not send the object etc over... I think I have my knickers in a twist over where the delegate is declared/synthesized and implemented etc...
Here is where I declare the delegate in the header file of the UITableViewController:
#class iTanksV2ListViewController;
#protocol iTanksV2ListViewControllerDelegate
- (void) iTanksListViewController:(iTanksV2ListViewController *) sender choseTank:(tank *)tank;
#end
#interface iTanksV2ListViewController : UITableViewController
#property (weak, nonatomic) id <iTanksV2ListViewControllerDelegate> delegate;
#end
and this is what I have in the UITableViewControllers m file:
#implementation iTanksV2ListViewController
#synthesize delegate = _delegate;
- (void)viewDidLoad
{
[super viewDidLoad];
self.tankTableView.delegate = self;
self.tankTableView.dataSource = self;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
self.selectedTank = [self.tanks objectAtIndex:indexPath.row];
[self.delegate iTanksListViewController:self choseTank:self.selectedTank];
}
...and then here is the code I have for the delegate in the DetailViewController implementation file...
#interface tankDetailViewController () <iTanksV2ListViewControllerDelegate>
#end
-(void)iTanksListViewController:(iTanksV2ListViewController *)sender choseTank:(id)tank
{
self.tankToShow = tank;
}
All the other variables etc are declared, I have left them out in the hope it makes things clearer - the code compiles, and the segue happens but the delegate doesn't seem to do anything!!!
You mention segues so I'm assuming you're using a storyboard. If that's true, forget about delegates for this purpose. A delegate is supposed to be an object that performs some action for another but all you need is data transfer.
I suggest using the prepareForSeque: method instead in your iTanksV2ListViewController class. Keep the line that sets self.selectedTank when a row is tapped but then put something like this in prepareForSegue:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
tankDetailViewController *next = (tankDetailViewController *)[segue destinationViewController];
next.tankToShow = self.selectedTank;
}