Calling a New View when selecting a Row in a 'UITableView' - iphone

I am currently writing my first iPhone app, but have encountered an issue. I have a view which contains a UITableView. This is the first time that I have attempted this, and this is the behaviour that I am trying to achieve:
When the user selects one of the rows, I would like this to call a new view, taking the user to a different page displaying info in reference to what they have selected.
I have it currently, so when the user selects a row it displays a UIAlert in the same view, but this doesn;t suit my needs. I have set the UITableView up through interface builder, and inputted the following code into my .m file to set it up.
- (NSInteger)tableView:(UITableView *)tableView
numberOfRowsInSection:(NSInteger)section {
//return the value
return 10;
}
//now we define the cells.
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// Identifier for retrieving reusable cells.
static NSString *cellIdentifier = #"MyCellIdentifier";
// Attempt to request the reusable cell.
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
// No cell available - create one
if(cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:cellIdentifier];
}
// Set the text of the cell to the row index.
cell.textLabel.text = [NSString stringWithFormat:#"iPad %d", indexPath.row];
return cell;
}
This creates a list of ten rows. The following codes gives me my UIAlert when tapped, however, I want to remove this and make it call a new view of my choice;
- (void)tableView:(UITableView *)tableView
didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// Show an alert with the index selected.
UIAlertView *alert = [[UIAlertView alloc]
initWithTitle:#"iPad Selected"
message:[NSString stringWithFormat:#"iPad %d", indexPath.row]
delegate:self
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
[alert release];
}
Can anyone help with this last piece of code? the view I want it to call is called 'ProteinView'.

Alrighty, what we need to do is use one of the UITableView methods that are already readily available to us. We'll do the following.
- (void)tableView:(UITableView *)tableView
didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
ProteinView *detailViewController = [[ProteinView alloc] initWithNibName:#"ProteinView" bundle:nil];
// It is here we'd pass information from the currently selected UITableViewCell to the ProteinView.
// An example of this is the following.
// I would do it like this, but others would differ slightly.
NSString *titleString = [[[NSString alloc] initWithFormat:#"iPad %d",indexPath.row] autorelease];
// title is an object of detailViewController (ProteinView). In my own instances, I have always made a NSString which in viewDiDLoad is made the self.navigationBar.title string. Look below for what my ProteinView.m and .h would look like.
detailViewController.stringTitle = titleString;
// ...
// Pass the selected object to the new view controller.
[self.navigationController pushViewController:detailViewController animated:YES];
[detailViewController release];
}
EDIT
// -------- ProteinView.m -------- //
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
// Here we set the navigationItem.title to the stringTitle. stringTitle is declared in the .h. Think of it as a global scope variable. It is also propertised in the .h and then synthesized in the .m of ProteinView.
self.navigationItem.title = stringTitle;
}
I haven't compiled this, so I don't know if it'll fully work. But that is definitely the fastest and most easiest way to do it!

You could present the view modally like this
YourViewController2 *viewController2 = [[YourViewController2 alloc]initWithNibName:#"YourViewController2" bundle:nil];
[self presentModalViewController:viewController2 animated:YES];
Do you have more than one view to present? If so you will need to create an array with the names, pass it into the tableview and then present the correct view for the row selected based on the indexPath.row.

you'll have to open your MainWindow.xib and add a navigation controller to it. Then add a navigation controller outlet to your app delegate and connect them. Then you'll need to set the navigation controller's view as your main window's view.
You can add a table view to any iPhone app fairly easily, just by creating a new UITableViewController subclass from the File -> New command.
Even if you go this route, I would suggest creating a new navigation-based project to use as a template/cheat-sheet.

Related

Take data from a static label and use it to populate tableView

I'm working on an application that has multiple views, taking data from one view and storing it in a table in another. I have labels that are updated with data when a calculate button and a button that is hopefully going to store the data from those labels on their own cell in the UITable in another cell. I'm currently lost on how I would set up my UITable to create a new cell and pass the data to that cell every time the validate button is pressed.
This is basic MVC behavior. Table cells are loaded at display time in the UITableView datasource delegate methods. The data should be loaded from some type of store, in your case most likely an array.
When you want to update the data (from anywhere), simply update the data store (array).
Reload the UITableView at will with the reloadData method (or whenever the view appears).
So the idea is that you want to store your UILabels' text values in UITableViewCells on the press of a button?
If this is the case, I would store each text value as an element in an NSArray after every time your button is clicked, like so:
// Given:
// 1.) Your labels are IBOutlets
// 2.) Your labels follow the naming convention label1, label2, label3, etc
// 3.) You have an initialized class variable NSMutableArray *labels
// 4.) NUM_OF_LABELS_IN_VIEW is the number of UILabels in your view
// 5.) myTableView is an outlet to your UITableView, and its delegate and datasource are set to your view controller
-(IBAction)buttonPressed:(id)sender{
self.labels = [[NSMutableArray alloc] init];
for (int i=0; i < NUM_OF_LABELS_IN_VIEW; i++){
[labels addObject:[self valueForKey:[NSString stringWithFormat:#"label%i", i]].text ];
}
[self.myTableView reloadData];
}
And your data source methods should look something like this:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [self.labels count];
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellIdentifier = #"MyCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if(!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:Cellidentifier];
}
cell.textLabel.text = self.labels[indexPath.row];
return cell;
}
If the UITableView is in a separate view controller, just assign the NSArray *labels to a #property on the presenting view controller.

In-app populating of UITableView with Xcode? (iPhone)

I'm brand new to Xcode and Objective-C, and fairly new to programming in general so please correct me if I have a severe misunderstanding.
Here's what I'd like to accomplish:
Create a Table View (empty upon startup)
Have user add and name cells via a Bar Button (pushes to a different view with a name Text Field and "Create" button I'm assuming)
Each created cell pushes to a new Table View with similar add/naming functionality
Then each of those cells push to a Text View
It seems to me that the best way to accomplish the first part is by populating a string array, then assigning those elements to the corresponding cells. That's where I get stuck.
How do I populate a Table View with an array?
Should I create a new secondary Table View for every cell, or just populate the same one differently depending on which parent cell is selected?
And same question as previous for the Text Views at the end of the chain...multiple Text Views or just different text passed each time?
If you got this far, I sincerely thank you and please let me know if I need to clarify anything.
I hope you know how to create a table view.
Now you should create a NSMutableArray to save your strings.
#property (retain) NSMutableArray *stringsArray = _stringsArray;
so your viewDidLoad will look like this.
- (void)viewDidLoad
{
[super viewDidLoad];
UITableView *myTableView = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStyleGrouped];
myTableView.dataSource = self;
myTableView.delegate = self;
[self.view addSubview:myTableView];
[myTableView release];
self.stringsArray = [NSMutableArray array];
}
Now the table view delegates
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return [self.stringsArray count];
}
- (UITableViewCell *)tableView:(UITableView *)tmpTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"MainCell";
UITableViewCell *cell = [tmpTableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (nil == cell) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
cell.textLabel.text = [self.stringsArray objectAtIndex:indexPath.row];
return cell;
}
Now when you click on your barbutton to add new user, you must add it to strings array and just call [tableview reloadData];
I didn't understand why you want a text view.
Hope this helps!
Read from here for complete reference of uitableview UITableView Tutorial : Introduction to iPhone TableView

Pushing viewController from a tableview that is inside a viewController not working

I am having an issue pushing my view controller from a tableview that was dropped into a viewController, and not a TableViewController.
I have found a thread on here with a solution here
pushViewController is not pushing
The problem is.. I have done his solution and my view still does not push. I have connected my tableview via ctrl+drag to the view controller and set both the dataSource and delegate. In the post i mentioned, they say the solution was to 'create a referencing outlet from the tableview to the view controller.' But I don't exactly know what that means. So help with that would be good as well. I have followed this tutorial and am stuck on step 9 obviously.
The line of code not triggering is
[self.navigationController pushViewController:facility animated:YES];
My code is as follows...
viewTable methods
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSLog(#"list Size: %#",list);
return [list count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = (UITableViewCell *) [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if(cell == nil){
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
CustomerListData *customer = [self.list objectAtIndex:indexPath.row];
cell.textLabel.text = customer.facilityName;
// cell.textLabel.text = #"Detail";
return cell;
}
-(void)setValue:(NSMutableArray*)array
{
//NSMutableArray *newArray = [[NSMutableArray alloc] init];
list = array;
}
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
FacilityMissionListViewController *facility = [self.storyboard instantiateViewControllerWithIdentifier:#"facilityMissionList"];
[self.navigationController pushViewController:facility animated:YES];
//UINavigationController* navController = self.navigationController;
}
the very last commented out line is there to check if navigationcontroller was null. It is not.
From what I have gathered the issue here lies in the fact the table view is inside the view controller and its not a "tableviewcontroller." This method apparently works if that is the case, but some other steps must be taken if not.. but I don't know them.
Thanks in advance.
It sounds like your NavigationController is not set up correctly. Could you post the code that sets it up?
Put a :
NSLog(#"%#", self.navigationController);
in your didSelectRowAtIndexPath method and you'll see if the method is being called and if the navigationController is nil. It might be one of this cases.
Otherwise make sure that the FacilityMissionListViewController is not nil either.

Unrecognized Selector when Using UITableView in a Subview

I am building an app that uses a TabBarController to display several other views. In one of these views, I'm using a navigation controller to navigate some tabular data. When the user click's this tab, I load in the NavigationController which in turn loads the TableView that I'm using. The issue is that I get the following error upon load of the TableView Controller:
-[UIViewController tableView:numberOfRowsInSection:]: unrecognized selector sent to instance 0x6b37b10
I have read everywhere that this type of error usually comes from a misconnection in IB or that a class is not correct in the view controller. Here is my code and screenshots of IB as to help debug this for me.
Thanks in advance!
The Error I'm Receiving
The TableView's Connections in IB
The File Owner's Class
My Interface File
#interface FAQListViewController : UIViewController <UITableViewDataSource, UITableViewDelegate> {
// Dictionary that will hold the FAQ Key/Values
NSMutableArray *arrFAQData;
}
Implementation
#implementation FAQListViewController
// Implementation for UITableView numberOfRowsInSection protocol
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [arrFAQData count];
}
// Implementation for UITableView cellForRowAtIndexPath protocol
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// See if we have any cells available for reuse
UITableViewCell *objCell = [tableView dequeueReusableCellWithIdentifier:#"FAQCell"];
if (objCell == nil) {
// No reusable cell exists, so let's create a new one
objCell = [[UITableViewCell alloc] initWithStyle: UITableViewCellStyleDefault reuseIdentifier: #"FAQCell"];
}
// Give it data
NSDictionary *objRow = [arrFAQData objectAtIndex: [indexPath row]];
objCell.textLabel.text = [objRow valueForKey:#"Title"];
// Return the created cell
return objCell;
}
// Selection Event Handler
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// Get the value for the selected key
NSDictionary *dictRow = [arrFAQData objectAtIndex:[indexPath row]];
NSString *strURL = [dictRow valueForKey: #"URL"];
// Alert result
UIAlertView *objAlert;
objAlert = [[UIAlertView alloc] initWithTitle:#"Alert" message: strURL delegate:nil cancelButtonTitle:#"Ok" otherButtonTitles:nil];
// Release created objects
[objAlert show];
[objAlert release];
// Deselect row
[tableView deselectRowAtIndexPath:indexPath animated:YES];
}
- (void)viewDidLoad {
// Pull in FAQ from Plist
NSString *strFAQPlist = [[NSBundle mainBundle] pathForResource:#"FAQData" ofType:#"plist"];
arrFAQData = [[NSMutableArray alloc] initWithContentsOfFile: strFAQPlist];
[super viewDidLoad];
}
- (void)dealloc {
[arrFAQData release];
[super dealloc];
}
First of all, this is a well-constructed question, I wish everyone posted screen shots and full source code. :-)
It looks like the message is being sent to a generic UIViewController, instead of FAQListViewController. This tells me that perhaps when you instantiate the FAQListViewController, you are creating an instance of UIViewController instead?
To instantiate the FAQ list, you should be using something like:
[[FAQListViewController alloc] initWithNibName: #"FAQViewController" bundle: nil];
Your error might arise if you were instead instantiating it like:
[[UIViewController alloc] initWithNibName: #"FAQViewController" bundle: nil];
Try to make the Delegate & Datasource connections programmatically. I had a similar issue were an optional method of the UITableViewDelegate protocol was not being called for no apparent reason (while other delegate methods were working as expected) unless I made the connection programmatically. Since the method was optional, however, no exception was thrown.

cellForRowAtIndexPath not called; sections returns 1 and rows returns 4

After parsing JSON data in a Data class, I set the UIViewController's NSArray *headlines property in a fillArrays method of the same Data class. In the viewDidAppear method of my UIViewController, I call reloadData on my UITableView. numberOfSectionsInTableView fires and returns 1, then numberOfRowsInSection fires and returns an array count of 4 (for 4 strings in the array). However, control never gets to cellForRowAtIndexPath and I'm having the hardest time understanding why, especially since I have valid sections and rows. The cells are all visible.
I've added the UITableViewDataSource and UITableViewDelegate protocols to the UIViewController interface and set the UITableView's delegate and dataSource to self in viewDidLoad (which also is verified by the row and section count methods being called).
I'm wondering if it has something to with me reinitializing the UIViewController in Data.m in order to set its properties.
In Data.m:
- (void)fillArrays:(NSArray *)jsonObjs {
NSLog(#"fillArrays");
HeadlinesRootViewController *hrvc = [[HeadlinesRootViewController alloc] init];
hrvc.headlines = [self getJsonValuesForKey:#"headline" inArrayOfObjects:jsonObjs];
[hrvc viewDidAppear:NO];
}
In ViewController.m:
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(#"viewDidLoad");
// Table view
headlineTableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 180, self.view.bounds.size.width, 300) style:UITableViewStylePlain];
[headlineTableView setDelegate:self];
[headlineTableView setDataSource:self];
// Temporary
self.headlines = [[NSMutableArray alloc] initWithObjects:#"headline1", #"headline2", #"headline3", #"headline4", nil];
[self.view addSubview:headlineTableView];
self.headlineTableView = headlineTableView;
[headlineTableView release];
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
NSLog(#"viewdidappear");
NSLog(#"headlines: %#", self.headlines); // Returns an array of 4 headlines
if( [self.headlines count] != 0 ){
[self.headlineTableView reloadData];
}
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
NSLog(#"numberOfSectionsInTableView: 1");
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
NSLog(#"numberOfRowsInSection: %d", [self.headlines count]);
return [self.headlines count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSLog(#"cellForRowAtIndexPath");
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease];
}
cell.text = [[NSString alloc] initWithFormat:#"%#", [self.headlines objectAtIndex:indexPath.row]];
return cell;
}
In fillArrays, you create another view controller - but you never do anything with it or its view, you would never see that view. You would never call viewDidAppear manually either, that happens automatically when a view controllers view is displayed (ONLY in the context of a navigation controller though).
Normally the flow is, you create a view controller and either add that view as a subview of a current view, or push it as a new window via a navigation controller. I'm pretty sure your whole issue is that they table is never added to a view anyone actually sees, so the table calls the other methods but never calls cellForRow because its layoutSubviews code is simply not being called.
Have you added your tableView to the view of UIViewController?
It happened to me, and when I added this
[self.view addSubview:table];
[table release];
then cellForRowAtIndexPath started working.
For Google's sake:
If tableView:numberOfRowsInSection returns zero for whatever reason tableView:cellForRowAtIndexPath will not get called because there are no rows to call it for.
Check to make sure that the tableView delegate and dataSource are pointed to the viewController
I cannot see anything wrong with the code as-is, have you verified with breakpoints that cellForRow is never reached (even though I see you have a log statement)?
Also I would try just for a sanity check to return "1" explicitly in rowsInSection, and hardcode a string in the cell you are returning in cellForRow.
If all else fails, create a new table view controller from the XCode templates and put your calls in there - then when that works, work backwards to why your code does not.
Also, it would be good to see your viewDidLoad setup code (add to answer above please).
if you're setting the delegate and datasource at viewDidLoad, then that may be the source of your bug. Can you set the datasource and delegate in init?
I'm not sure that you add your UITableView as subview to UIViewController.view. This was my approach anyway.
In this approach, I found execution did not get into cellForRowAtIndexPath until I sent UIViewController.view to the back after adding UITableView as subview.
Getting this far was only part of the problem. At this point, it seemed that my other view controllers no longer respond to touch events. I found that when I also add the UITableView as a subview to the rootViewController, all my views got the appropriate touch events.
Thank you so much pxl. When I move the UITableView initialization from viewDidLoad to:
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil"
it works perfectly when ever I make delete or update some rows the UITableView gets reloded to my UIView.
Swift version
Add self.table.layoutIfNeeded() and then self.tableView.reloadData()