I have been following this tutorial on YouTube (part 1 and part 2).
I have completed both videos and have hooked up the view controller with the parent view controller using this code:
- (IBAction)searchButtonClicked:(id)sender {
NSLog(#"It works.");
SearchViewController *searchViewControl = [self.storyboard instantiateViewControllerWithIdentifier:#"SearchControllerNav"];
[self presentViewController:searchViewControl animated:YES completion:nil];
}
This code indeed works since this is the same format that I use for my other modal view controllers, so i know that's not the problem.
Anyway, when I tap on the search button in the view controller, it should pop up the SearchViewController. However, the app crashes instead and it gives me this error message:
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '-[UITableViewController loadView] loaded the "jp7-vt-IdA-view-Jer-xW-qlD" nib but didn't get a UITableView.'
I am using Storyboards for this app.
Is there something that I'm missing? Thank you in advance.
A side question: I'm also getting a warning, saying Comparison between pointer and integer ('BOOL *' (aka 'signed char *') and 'int') whenever isFiltered == YES is shown. Is there anyway to fix it?
Here is the code for SearchViewController:
SearchController.h
#import <UIKit/UIKit.h>
#interface SearchViewController : UITableViewController <UITableViewDelegate, UITableViewDataSource, UISearchBarDelegate> {
}
- (IBAction)cancelButtonTapped:(id)sender;
#property (weak, nonatomic) IBOutlet UISearchBar *mySearchBar;
#property (weak, nonatomic) IBOutlet UITableView *myTableView;
#property (nonatomic, strong) NSMutableArray *itemsInCloudApp;
#property (nonatomic, strong) NSMutableArray *filteredList;
#property BOOL *isFiltered;
#end
SearchViewController.m
#import "SearchViewController.h"
#interface SearchViewController ()
#end
#implementation SearchViewController
#synthesize mySearchBar, myTableView, itemsInCloudApp, filteredList, isFiltered;
- (void)viewDidLoad
{
[super viewDidLoad];
// Set title.
UILabel *titleLabel = [[UILabel alloc] initWithFrame:CGRectZero];
titleLabel.text = #"Search";
titleLabel.adjustsFontSizeToFitWidth = YES;
titleLabel.clipsToBounds = YES;
titleLabel.numberOfLines = 1;
titleLabel.font = [UIFont fontWithName:#"Avenir-Medium" size:18];
titleLabel.textColor = [UIColor blackColor];
titleLabel.autoresizingMask = UIViewAutoresizingFlexibleHeight;
titleLabel.textAlignment = NSTextAlignmentCenter;
[titleLabel sizeToFit];
self.navigationItem.titleView = titleLabel;
// Alloc and init list.
itemsInCloudApp = [[NSMutableArray alloc]initWithObjects:#"http://www.apple.com/", #"http://www.trijstudios.com/", #"http://www.google.com/", #"http://www.squarespace.com/", #"http://www.youtube.com/", nil];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
if (isFiltered == YES) {
return [filteredList count];
} else {
return [itemsInCloudApp count];
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
// Configure the cell...
if (isFiltered == YES) {
cell.textLabel.text = [filteredList objectAtIndex:indexPath.row];
cell.detailTextLabel.text = [filteredList objectAtIndex:indexPath.row];;
} else {
cell.textLabel.text = [itemsInCloudApp objectAtIndex:indexPath.row];
cell.detailTextLabel.text = [itemsInCloudApp objectAtIndex:indexPath.row];
}
return cell;
}
-(void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText {
if (searchText.length == 0) {
// Set bollean flag
isFiltered = NO;
} else {
// Set boolean flag
isFiltered = YES;
// Alloc and init our fliteredData
filteredList = [[NSMutableArray alloc] init];
// Fast enumeration
for (NSString *name in itemsInCloudApp) {
NSRange nameRange = [name rangeOfString:searchText options:NSCaseInsensitiveSearch];
if (nameRange.location != NSNotFound) {
[filteredList addObject:name];
}
}
}
// Reload tableView
[myTableView reloadData];
}
-(void)searchBarSearchButtonClicked:(UISearchBar *)searchBar {
[mySearchBar resignFirstResponder];
}
- (IBAction)cancelButtonTapped:(id)sender {
[self dismissViewControllerAnimated:YES completion:nil];
}
#end
NOTE: There are a few edits that I made to fit my needs.
have you tried changing your #interface SearchViewController : UITableViewController to #interface SearchViewController : UIViewController
I strongly suspect that either you have not attached your UITableview as View in XIB or your class should be derived UIViewController instead of UITableviewController class..
I had a similar error. I was able to resolve it using #Dinesh's suggestion, but I didn't like this because I was afraid that there could be some unintended consequences.
What I figured out was that when I looked at the scene hierarchy in the storyboard, I noticed that I had this structure (sorry, I don't know how to format this - it's supposed to be a tree structure):
View Controller
View
Table View
When I took out the View that sat in the middle, my problem went away. However, before doing so, you need to delete any outlets that might exist between the view and either the view controller or the table view. After you make sure that these are gone, follow these final steps:
Drag the Table View so that it is a direct descendant of the View Controller.
Delete the View
Command-Drag from the View Controller to the Table View, thereby creating a new outlet directly between the two.
Also, leave the .h file as a subclass of UITableView (not UIView).
Anyway, that solved the issue for me. If anyone comes across this, I hope that it helps.
For you side question regrading the warning, the warning comes because you have made the BOOL isFiltered as a pointer.
For your first problem, you needed to check the storyboard. I am sure that your file owner's view is connected to a UIView. To solve this, you must drag UITableView and view must be connected to the UITableView.
For your second problem, declare BOOL as
#property(assign,nonatomic) BOOL isFiltered;
I encountered this when building a iOS7 Universal app with a simple, dumb error: I'd built part of the iPhone app only, but had the scheme set to iPad simulator. After getting the error and looking here, I saw my mistake, switched the scheme to iPhone, and the app ran with the proper storyboard for the proper simulator. Hope that helps.
Related
I try to search this problem in this site and I found this link How to insert items to a UITableView when a UIButton is clicked in iOS. But my problem is, I already copy the code on that link and It doesn't reload the data when I insert a value to my array.
here's the code "ViewController.m"
#import "ViewController.h"
#interface ViewController ()
#property(nonatomic,strong) NSMutableArray * array;
#property(nonatomic,weak) IBOutlet UITableView * tableView;
#end
#implementation ViewController
-(NSMutableArray *) array{
if(_array==nil){
_array=[[NSMutableArray alloc] init];
}
return _array;
}
- (void)viewDidLoad
{
[super viewDidLoad];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
- (IBAction)addInfo:(UIBarButtonItem *)sender {
[self.array addObject:#"sample"];
[self.tableView reloadData];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return [self.array count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *simpleTableIdentifier = #"TodoListItem";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpleTableIdentifier];
}
cell.textLabel.text = [self.array objectAtIndex:indexPath.row];
return cell;
}
#end
here's the code "ViewController.h"
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController <UITableViewDataSource,UITableViewDelegate>
#end
Is there something wrong with my code or there is something that I need to setup to my tableview? I'm very confuse of this and try to figure out the missing part of my code. I'm still studying the code of object c and I'm still noob for this. Please help me and thanks in advance.
How did you insert the UITableView?, on nib or programatically?, either way you need to flag the table's delegate and datasource, if on nib, right click on table and see that delegate and data source are connected to the files owner, if programatically check
self.MyTable.datasource = self
self.MyTable.delegate = self
please see how to check if connected [dataSource and delegate should be with a dot, if not click on circle and drag line to file's owner for both]
table delegate and datasource on interface builder "NIB"
Also please note that on this image outlet is not connected, you have to connected also to call the reload as you are doing now
edit, check if the button is connected?, put a log or a break point on ibaction for your button to know if is called
edit 2, you have to init the array, are you calling it? do this in view will appear or when you want to use it
edit 3, try this
- (IBAction)addInfo:(UIBarButtonItem *)sender {
if(self.array==nil){
self.array=[NSMutableArray array];
}
[self.array addObject:#"sample"];
[self.tableView reloadData];
}
I'm building an iPad app that will have a table view in one sector of the window with other labels, images, buttons, etc elsewhere. I know how to create a tableView when using a tableViewController as the class of the scene, but can't figure out how to populate the table cells when the table view is embedded in a scene with a UIViewController class.
I have updated my .h file as follows:
#interface SKMainViewController : UIViewController <UITableViewDataSource, UITableViewDelegate>
I'm returning data from an asynchronous API call into the viewController, but can't figure out how to initiate the required tableView methods (tableView:numberOfRowsAtIndexPath and tableView:cellForRowAtIndexPath). How can I trigger them to run once I've got my data back? Is there a simple method call I don't know about?
I've dissected a functioning UITableViewController class and don't see what triggers the population of the table cells.
EDITED: Adding large sections of code for your review. I've trimmed out the other imports and property declarations to simplify.
This is SKMainViewController.h:
#import "SKWelcomeViewController.h"
#import "SKAgenda.h"
#import "SKAgendaManager.h"
#import "SKAgendaCommunicator.h"
#import "SKAgendaManagerDelegate.h"
#import "SKAgendaTableViewCell.h" // I'm using a custom cell
#interface SKMainViewController : UIViewController <SKFlipsideViewControllerDelegate, UIPopoverControllerDelegate, NSURLConnectionDelegate, SKGreetingManagerDelegate, SKWeatherManagerDelegate, UITableViewDataSource, UITableViewDelegate>
{
NSArray *_agenda;
SKAgendaManager *_aManager;
}
#pragma mark Agenda Detail
#property (strong, nonatomic) NSArray *agendaItems;
#property (strong, nonatomic) IBOutlet UITableView *agendaTableView;
#end
This is SKMainViewController.m:
#import "SKMainViewController.h"
#interface SKMainViewController ()
#end
#implementation SKMainViewController
- (void)viewDidLoad
{
[self startFetchingAgenda:_agendaItems];
}
-(void)startFetchingAgenda:(NSNotification *)notification
{
NSInteger deviceID = [[NSUserDefaults standardUserDefaults] integerForKey:#"deviceID"];
if(deviceID == 0)
{
// todo: add error handling here
NSLog(#"Bad stuff happened");
}
[_aManager fetchAgendaForDeviceID:deviceID];
}
-(void)didReceiveAgenda:(NSArray *)agendaItems
{
NSLog(#"Received these agenda items: %#", agendaItems);
_agendaItems = agendaItems;
if(agendaItems.count == 0)
{
// set defaults to populate a single cell
NSLog(#"No Items!");
}
else
{
// populate data and present
NSLog(#"Some Items!");
agendaTableView.delegate = self; // Error Here: use of undeclared identifier 'agendaTableView'
agendaTableView.dataSource = self; // Error Here: use of undeclared identifier 'agendaTableView'
}
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 2;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return [_agendaItems count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
[self.agendaSpinner startAnimating];
self.agendaSpinner.hidden = NO;
static NSString *tableIdentifier = #"agendaTableCell";
SKAgendaTableViewCell *cell = (SKAgendaTableViewCell *)[tableView dequeueReusableCellWithIdentifier:tableIdentifier];
if(cell == nil)
{
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"agendaTableCell" owner:self options:nil];
cell = [nib objectAtIndex:0];
}
// set the data related to the agenda item
SKAgenda *agendaDetails = [_agendaItems objectAtIndex:indexPath.row];
cell.agendaTitle.text = agendaDetails.title;
cell.agendaDescription.text = agendaDetails.description;
NSString *eventTimes = [NSString stringWithFormat:#"%# - %#", agendaDetails.start, agendaDetails.end];
cell.agendaTimes.text = eventTimes;
//todo: add functionality for background and text colors. Will need to use RGB colors instead of HEX. Change in webiste.
// todo: accommodate for no events in the agenda
[self.agendaSpinner stopAnimating];
self.agendaSpinner.hidden = YES;
return cell;
}
Thanks.
Just set the data source and delegate:
myTableView.delegate = self;
myTableView.dataSource = self;
Where self is your SKMainViewController that adopts the UITableViewDataSource and UITableViewDelegate protocols.
If at any time you'd like to reload your table data:
[myTableView reloadData];
Yes, the simple method is reloadData. This causes the table view to call its data source methods. You need to put it in a completion block (or delegate that's called after the data is received) if you're using an asynchronous api.
I am trying to implement the concept shown in this example project. My goal is to separate my view controller class and the datasource protocol. Instead of implementing the table view datasource methods in my table view controller class, I try to put it in its own class, and in my view controller, I only call this method to set up my table view:
- (void)setupTableView
{
void (^configureCell)(JVRTodoItemCell *, JVRTodoItem *) = ^(JVRTodoItemCell *cell, JVRTodoItem *todoItem)
{
[cell configureForTodoItem:todoItem];
};
NSArray *todoItems = currentUser.todoItems;
self.todoArrayDataSource = [[JVRArrayDataSource alloc] initWithItems:todoItems withCellIdentifier:TodoCellIdentifier withConfigureCellBlock:configureCell];
self.tableView.dataSource = self.todoArrayDataSource;
[self.tableView registerClass:[JVRTodoItemCell class] forCellReuseIdentifier:TodoCellIdentifier];
}
The data source is separated into its own class:
#interface JVRArrayDataSource ()
#property (copy,nonatomic) NSArray *items;
#property (copy,nonatomic) NSString *cellIdentifier;
#property (copy,nonatomic) void (^configureCellBlock)(id item, id cell);
#end
#implementation JVRArrayDataSource
...
#pragma mark - UITableViewDataSource
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return self.items.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:self.cellIdentifier forIndexPath:indexPath];
id item = [self itemAtIndexPath:indexPath];
self.configureCellBlock(cell,item);
return cell;
}
The interesting part is that creating the cell based on the identifier (using dequeueReusableCellWithIdentifier:forIndexPath:) seems to be successful, because the correct cell gets allocated, but its labels remain nil. I try setting up my cell using the following method, but the values remain nil (aTodoItem has valid properties):
- (void)configureForTodoItem:(JVRTodoItem *)aTodoItem
{
self.todoItemTitle.text = aTodoItem.title;
self.todoItemPriority.text = [NSString stringWithFormat:#"%d", aTodoItem.priority];
}
I am trying to figure out what could possibly be missing here, but so far, I haven't managed to fix the issue yet, and I'm starting to lose hope. Any help would be appreciated.
UPDATE:
To make it clear, the issue is shown on this picture.
It seems that the cells get created, but its labels don't.
If all you want to do is separate your tableview datasource delegate from the view controller you can create a separate class called TableViewDataSource. Within that class you can manage the datasources and their table view cells; configuring the them in your view controller, but letting the TableViewDataSource manage them.
TDSTableViewDataSource.h
#import <Foundation/Foundation.h>
#protocol TDSTableViewDataSourceDelegate <NSObject>
- (NSString *)fetchCellIdentifierForObject:(id)object;
- (UITableViewCell *)configureCell:(UITableViewCell *)cell usingObject:(id)item;
#end
#interface TDSTableViewDataSource : NSObject <UITableViewDataSource>
#property (strong, nonatomic) NSArray *items;
#property (strong, nonatomic) id<TDSTableViewDataSourceDelegate> delegate;
#end
TableViewDataSource.m
#import "TDSTableViewDataSource.h"
#implementation TDSTableViewDataSource
- (NSArray *)items {
if (!_items) _items = [[NSArray alloc] init];
return _items;
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
if ([self.items count]) {
return [self.items count];
} else {
NSLog(#"numberOfSectionsInTableView could not be determined. self.items is nil or empty.");
return 0;
}
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if ([self.items count]) {
return [self.items count];
} else {
NSLog(#"numberOfRowsInSection could not be determined. self.items contains fewer section requested does not contain any items.");
return 0;
}
}
/*
Single dimension Array of items belonging to a UITableView section
The method checks if the cell implements the HZConfigureTableViewCellDelegate, which is required.
The delegate should be the View Controller.
*/
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
id obj = [self.items objectAtIndex:indexPath.row];
UITableViewCell *cell = nil;
if ([self.delegate conformsToProtocol:#protocol(TDSTableViewDataSourceDelegate)]) {
NSString *cellIdentifier = [self.delegate fetchCellIdentifierForObject:obj];
cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (obj)
cell = [self.delegate configureCell:cell usingObject:obj];
}
return cell;
}
#end
This class and the protocol will essentially allow you to fetch and configure UITableViewCell's and not have to implement the protocols into your View Controller.
Inside your view controller, you create a datasource property using the protocol above.
#import "TDSViewController.h"
#import "TDSTableViewDataSource.h"
#interface TDSViewController () <UITableViewDelegate, TDSTableViewDataSourceDelegate>
#property (strong, nonatomic) TDSTableViewDataSource *dataSource; // UITableView data source.
#property (weak, nonatomic) IBOutlet UITableView *tableView;
#end
#implementation TDSViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.tableView.dataSource = self.dataSource;
self.dataSource.delegate = self;
}
#pragma mark - UITableView methods
-(NSString *)fetchCellIdentifierForObject:(id)object {
// Check if this is an event or a Reminder class.
if ([object isKindOfClass:[UITableViewCell class]]) {
// Return the cell identifier for this particular cell.
return #"com.myapp.defaultcell";
}
return #"blankcell";
}
- (UITableViewCell *)configureCell:(UITableViewCell *)cell usingObject:(id)item {
UITableViewCell *configuredCell = cell;
// Check if this is an event or a reminder.
if ([item isKindOfClass:[UITableViewCell class]]) {
// Configure the cell to present what data we want here...
}
return configuredCell;
}
#end
This is a complete example project. You can use this to configure any kind of cell you want, without having to add the datasource methods to your view controllers.
The view controller is used by the ConfigureTableViewCellDelegate protocol to configure the UITableViewCell's and use them in the Table View. Since the code is segregated now, the TableViewDataSource class now handles presenting the data to the table view. The View Controller is simply used to configure the cell's. This allows you to use custom UITableViewCells' on each ViewController if you want, and not have to deal with implementing the data sources each time.
UPDATED
Provided a better example, a complete project template.
In the ViewDidLoad register the nib, it fix the problem :)
-(void)viewDidLoad
{
[self.leftTableView registerNib:[UINib nibWithNibName:NIB_FILE bundle:nil] forCellReuseIdentifier:CELL_IDENTIFIER];
}
After hours of digging, I've managed to solve the issue (for now), by changing my custom cell's outlets to strong properties, and initializing them in the cell's init method:
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
self.todoItemTitle = [[UILabel alloc] init];
self.todoItemPriority = [[UILabel alloc] init];
}
return self;
}
This is very strange, since I thought that creating my views in storyboard, this should be automatically taken care of, and I've never had to do this manually before.
Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Questions asking for code must demonstrate a minimal understanding of the problem being solved. Include attempted solutions, why they didn't work, and the expected results. See also: Stack Overflow question checklist
Closed 9 years ago.
Improve this question
Plz code Help Can anyone tell me how to do that task?
In main screen user selects footballer,in 2nd screen in Table view cell user select specific row and save that row and go back to main view.in main view then it shows the specific row videos.
Basically i want to know about speific row selection,save that selection in table view and show thier contetnts in main screen.
go through the below code, it implements the delegate concept and also implements the solution for ur question hope this helps u :)
//in your main view controller
#import "ViewController.h"
#import "FootBallPlayersViewController.h"
#interface ViewController ()<FootballPlayerDelegate>//confirms to this delegate
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (IBAction)whenSelectButtonClicked:(id)sender
{
FootBallPlayersViewController *controller = [[FootBallPlayersViewController alloc]initWithNibName:#"FootBallPlayersViewController" bundle:nil];
controller.delegate = self; //u must set to self
[self presentViewController:controller animated:YES completion:nil];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)selectedFootBallPlayer:(NSString *)player
{
//implementation of your delegate method
//hear u are getting the football player name and u can continue further hear
NSLog(#"%#",player);
if([player isEqualToString:#"player1"])
{
UIButton *aButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[aButton setTitle:player forState:UIControlStateNormal];
[aButton addTarget:self action:#selector(whenFirstPlayerButtonClicked:) forControlEvents:UIControlEventTouchUpInside]; //add the target to self for click events
aButton.frame = CGRectMake(50, 50, 200, 55);
[self.view addSubview:aButton];
}
else
{
UIButton *aButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[aButton setTitle:player forState:UIControlStateNormal];
aButton.frame = CGRectMake(50, 105, 200, 55);
[aButton addTarget:self action:#selector(whenSecondPlayerButtonClicked:) forControlEvents:UIControlEventTouchUpInside]; //same hear
[self.view addSubview:aButton];
}
}
//now define the action methods
- (void)whenFirstPlayerButtonClicked:(UIButton *)sender
{
NSLog(#"player 1 video start");
}
- (void)whenSecondPlayerButtonClicked:(UIButton *)sender
{
NSLog(#"player 2 video start ");
}
#end
in the view that contain's the tableview do somthing like this
//in FootBallPlayersViewController.h
#import <UIKit/UIKit.h>
#protocol FootballPlayerDelegate <NSObject> //define a protocol named FootballPlayerDelegate
- (void)selectedFootBallPlayer:(NSString *)player;
#end
#interface FootBallPlayersViewController : UIViewController
{
NSArray *players;
NSString *selectedPlayer;
}
#property (retain, nonatomic) IBOutlet UITableView *playerTable;
#property (nonatomic, assign) id<FootballPlayerDelegate>delegate; //create a delegate
#end
in your FootBallPlayersViewController.m file
#import "FootBallPlayersViewController.h"
#interface FootBallPlayersViewController ()<UITableViewDataSource,UITableViewDelegate>
{
}
#end
#implementation FootBallPlayersViewController
#synthesize delegate; //synthesizing the delegate
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
players = [[NSArray alloc]initWithObjects:#"player1",#"player2", nil];
// players = [[NSArray alloc]initWithObjects:#"player1","player2", nil];
// Do any additional setup after loading the view from its nib.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)dealloc
{
[players release];
[_playerTable release];
[super dealloc];
}
- (IBAction)whenDoneButtonClicked:(id)sender {
//when done button clicked -->
//send a delegate to main controller
if([self.delegate respondsToSelector:#selector(selectedFootBallPlayer:)])//to avoid crash
{
[self.delegate selectedFootBallPlayer:selectedPlayer]; //call the delegate method hear
}
//dismiss the view
[self dismissViewControllerAnimated:YES completion:nil];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return players.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableHeaderFooterViewWithIdentifier:#"cell"];
if(cell == nil)
{
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"cell"];
}
cell.textLabel.text = [players objectAtIndex:indexPath.row];
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
//u can manage check mark and all, i am getting the selected player name
selectedPlayer = [players objectAtIndex:indexPath.row];
}
#end
Simple solution ...
As you are a newbie , I am clarifying each point.
First make a property in AppDelegate.h
#property int selectedRow;
Save the selected indexpath.row in 2nd screen that is your Table view screen, and also import AppDelegate.h
(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
self.appDelegate=(AppDelegate *)[[UIApplication sharedApplication] delegate];
self.appDelegate.selectedRow=indexPath.row; //saving the row
}
On main screen's viewWillAppear()
-(void)viewWillAppear:(BOOL)animated
{
if(self.appDelegate.selectedRow!=-1)//check wether row is selected or not
{
//action to show the specific row videos
}
}
Good Ways to accomplish this:
Custom Delegate
NSNotificationCenter
NSUserDefaults (edit: unnecessary disk writes)
Maintaining a Common NSObject Subclass and refreshing data on -willAppear
Other Ways:
Database (Core Data / SQLite) or plist (all too heavy for your case) (edit: unnecessary disk writes)
UIPasteBoard
A quick delegate tutorial:
Part 1: Creating the delegate
Suppose this is in the .h of the UITableViewController subclass that I have named YourTableViewControllerClassName
//declare the protocol
#class YourTableViewControllerClassName;
#protocol YourTableViewControllerClassNameDelegate <NSObject>
//#required //uncomment to specify required delegate methods as below
//- (void)requiredMethodNotUsedForThisExample;
#optional
- (void)selectedRow: (NSString *)selectedObj;
#end
#interface YourTableViewControllerClassName : UITableViewController
//declare a weak property to store any object
#property (nonatomic, weak) id <YourTableViewControllerClassNameDelegate> delegate;
#end
Suppose this is the -didSelectRowAtIndexPath of the corresponding UITableViewController subclass:
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
//the following line is the main thing and can be called
//in any method within this class (placed wisely)
if([[self delegate] respondsToSelector:#selector(selectedRow)]) { //avoid crash
[[self delegate] selectedRow:cell.textLabel.text];
}
[self.navigationController popViewControllerAnimated:YES];
}
Part 2: Implementing the delegate
Suppose this is the code somewhere in the former UIViewController subclass:
//call this method somewhere
-(void)pushMyTableViewController
{
//declare "UILabel lblText;" in the .h of this class
//lblText = [UILabel alloc] init];
//[lblText setFrame: CGRectMake(0,0,100,35)];
//[self.view addSubview:lblText];
YourTableViewControllerClassName *tvcObj = [[YourTableViewControllerClassName alloc] init];
//for the following line, remember to declare
//<YourTableViewControllerClassNameDelegate> in the .h of this class
//hence declaring that this class conforms to the delegate protocol
[tvcObj setDelegate:self];
[self.navigationController pushViewController:tvcObj animated:YES];
}
And this will be the delegate method you could implement in the former UIViewController subclass:
#pragma mark - Optional YourTableViewControllerClassName Delegate Methods
-(void)selectedRow:(NSString *)selectedObj
{
[lblText setText:selectedObj];
}
NOTE: This will not solve your particular issue because we are only setting a label depending on the selected row from the UITableViewController subclass.
The point was to show how delegation works.
Also, if you can get the cell.textLabel.text and set it on a UILabel in the former class then you can make changes at the appropriate places (mainly the method/s within #protocol)and pass the array index of the selected item instead or any object/variable/whatever that makes your life easier
*If you want something easier then go for NSNotificationCenter or NSUserDefaults or maybe even UIPasteBoard (if it floats your boat)
Use the tableView delegate called when you select any row
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
AppDelegate* appDelegate = [UIApplication sharedApplication].delegate;
appDelegate.selectedindex = indexpath.row;
or
[[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithInt:indexpath.row] forKey:#"SelcetedIndex"];
}
then there is 3 things you can do to get your selected index
1) make a app delegate variable for index path so that you can set here and get the value on other controller
// add property at appDelegate file
#property int selectedIndex;
2) Using NSUserDefault to set the selected index value
// read userDefault value
[[[NSUserDefaults standardUserDefaults] objectForKey:#"SelcetedIndex"] intValue];
3) using delegate to return back the value to previous controller
// try to google and first understand the concept and let me know if you want to go with delgate
I have an app that is selecting a person from their contacts list and takes their First name, last name and email. It then saves the first name to a nsmutablearray and puts it into a uitableview cell. My problem occurs once the contact is selected in the simulator.
Code:
.h:
#import <UIKit/UIKit.h>
#import <AddressBookUI/AddressBookUI.h>
#interface FirstViewController : UIViewController < ABPeoplePickerNavigationControllerDelegate, UITableViewDelegate, UITableViewDataSource>
- (IBAction)showPicker:(id)sender;
#property (weak, nonatomic) IBOutlet NSString *firstName;
#property (weak, nonatomic) IBOutlet NSString *email;
#property (weak, nonatomic) IBOutlet NSString *lastName;
#property (weak, nonatomic) IBOutlet UITableView *myTableView;
#property (strong, nonatomic) NSMutableArray *contacts;
#end
.m:
#import "FirstViewController.h"
#interface FirstViewController ()
#end
#implementation FirstViewController
#synthesize firstName;
#synthesize email;
#synthesize lastName;
#synthesize contacts;
#synthesize myTableView;
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
contacts = [[NSMutableArray alloc]initWithObjects:nil];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - UITableView Datasource
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return contacts.count;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
}
cell.textLabel.text = [contacts objectAtIndex:indexPath.row];
return cell;
}
- (IBAction)showPicker:(id)sender {
ABPeoplePickerNavigationController *picker =
[[ABPeoplePickerNavigationController alloc] init];
picker.peoplePickerDelegate = self;
[self presentModalViewController:picker animated:YES];
}
- (void)peoplePickerNavigationControllerDidCancel:
(ABPeoplePickerNavigationController *)peoplePicker
{
[self dismissModalViewControllerAnimated:YES];
}
- (BOOL)peoplePickerNavigationController:
(ABPeoplePickerNavigationController *)peoplePicker
shouldContinueAfterSelectingPerson:(ABRecordRef)person {
[self displayPerson:person];
[self dismissModalViewControllerAnimated:YES];
return NO;
}
- (BOOL)peoplePickerNavigationController:
(ABPeoplePickerNavigationController *)peoplePicker
shouldContinueAfterSelectingPerson:(ABRecordRef)person
property:(ABPropertyID)property
identifier:(ABMultiValueIdentifier)identifier
{
return NO;
}
- (void)displayPerson:(ABRecordRef)person
{
NSString* name = (__bridge_transfer NSString*)ABRecordCopyValue(person,
kABPersonFirstNameProperty);
self.firstName = name;
NSString* last = (__bridge_transfer NSString*)ABRecordCopyValue(person,
kABPersonLastNameProperty);
self.lastName = last;
ABMultiValueRef emails = ABRecordCopyValue(person, kABPersonEmailProperty);
NSString *emailId = (__bridge NSString *)ABMultiValueCopyValueAtIndex(emails, 0);//0 for "Home Email" and 1 for "Work Email".
self.email = emailId;
if (!(contacts))
{
contacts = [[NSMutableArray alloc]init];
}
[contacts insertObject:firstName atIndex:0];
NSIndexPath * indexPath = [NSIndexPath indexPathForRow:0 inSection:0];
[self.myTableView insertRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
}
#end
UITableView must be kept in sync with the data source at all times. Special care must be taken if the data source can change in a background thread.
When something is added to the data source, call beginUpdate/insert/endUpdate as soon as possible. You don't have to worry about caching these, the UITableView will cache changes to be executed when it determines there is enough cpu time and resources.
The moment endUpdates is called, the UITable will ask the dataSource for the number of sections and rows again. If your number of sections and row feeds directly from the dataSource, then number sections and rows, plus insertions, minus deletions must equal the numbers returned by the end calls for numberOfSections and numberOfRowsInSection.
One last tip: avoid mixing calls to 'reloadData' and beginUpdate/endUpdate pairs. Use one or the other, not both.
I have encountered same problem as this. All you have to do is change
[self.myTableView insertRowsAtIndexPaths:#[indexPath]withRowAnimation:UITableViewRowAnimationAutomatic];
to
[self.myTableView beginUpdates];
[self.myTableView insertRowsAtIndexPaths:#[indexPath]withRowAnimation:UITableViewRowAnimationAutomatic];
[self.myTableView endUpdates];
From UITableView Documentation
beginUpdates
Begin a series of method calls that insert, delete, or select rows and sections of the receiver.
When you use beginUpdates, you must call endUpdates and not reloadData.
You can check this link for more UITableView information.
The comments above about implementing -tableView:numberOfRowsInSection: are correct. The assertion is checking the returned value expecting it will increase.
However since you didn't set your dataSource on the UITableView its calling (or not calling) a method that doesn't exist and getting 0.
You need to set the myTableView.dataSource and (since you also implement the delegate protocol) myTableView.delegate to self.
You're also likely to need something like
- (void)viewDidLoad
{
[super viewDidLoad];
[self.myTableView registerClass:[UITableViewCell class] forCellReuseIdentifier:#"Cell"];
...
}
Unless you're registering that somewhere else or your storyboard has a "Prototype Cell" with the identifier "Cell" which your code asks for.
I find this problem commonly occurs when I am placing a table view inside of a View Controller. If you're using a UITableViewController jump to 3.
These steps may help:
1: In your View Controller .h file make sure you add the following:
#interface YourViewController : UIViewController <UITableViewDataSource, UITableViewDelegate>
2: Next create an IBOutlet for your table view by ctrl + drag to your .h class. It should look like:
#property (weak, nonatomic) IBOutlet UITableView *tableView;
3: Next step is to ctrl + drag to your View Controllers icon (see image)
You need to do this twice selecting:
- delegate
- datasource
Finally, in your .m file, you should have the following method:
- (void) viewWillAppear:(BOOL)animated{
//[self.tableView beginUpdates];
//[self.tableView endUpdates];
[self.tableView reloadData];
}
You can use either beginUpDates/endUpdates or reloadData, however Apple docs recommend reloadData.
Once done your table should work fine.
You need to maintain count for contacts array and increment accordingly.
and while creating indexPath you need to set appropriate indexPathForRow: and section count(if required).
- (void)displayPerson:(ABRecordRef)person
{
..........
[contacts insertObject:firstName atIndex:0];
NSIndexPath * indexPath = [NSIndexPath indexPathForRow:0 inSection:0]; // you cant do this increment appropriately
.........
}
Please check your tableview datasource and delegate methods. You may be passing empty array to datasource methods. And don't forget to reload tableview after getting data.
I know it's stupid - I got the same error because I forgot to set the delegate and dataSource.
So after inserting rows and doing tableView.endUpdates() the tableView thought it must have some rows - but due to the unlinked dataSource, it has not.