UITableView in Storyboard appears empty - iphone

It's my first time using Storyboard to build an app. I'm trying to create a UITableView with a custom cell. I've created the cell in IB and created a custom tableViewCell class for it (added some labels, created appropriate outlets, connected them in IB and assigned the custom class to the custom cell in IB).
In the ViewController responsible for the TableView I create the data source (a dict with some arrays) and fill out all the obligatory methods (I've tried both with UITableViewControler and with a UIViewController that is set as a delegate and data source for the tableview)
And when I run the app - the tableview is empty. And after doing some NSLogging I've noticed that the data source methods are never executed. And I have NO IDEA why.
I'm going crazy about this for a few hours now. Please help me out :)
Let me know if you need to see the code or Storyboard, or whatever else.
UPDATE: Alright, after some digging, I've decided to test out the same code but using an NSMutableArray as data source instead of a NSMutableDictionary. And it works!
Now, could someone explain to me why it didn't work with a dict?
Here's what did. I had a dict with 5 arrays, and each array had 5 strings.
In the numberOfRowsForSection method, I returned [dict count]
And in the cellForRowAtIndexPath method, I've used this code
NSArray * routeArray = [dealsDict objectForKey:#"route"];
cell.routeName.text = [routeArray objectAtIndex:indexPath.row];
NSArray * companyArray = [dealsDict objectForKey:#"company"];
cell.companyName.text = [companyArray objectAtIndex:indexPath.row];
NSArray * priceArray = [dealsDict objectForKey:#"price"];
cell.priceLabel.text = [priceArray objectAtIndex:indexPath.row];
NSArray * dateArray = [dealsDict objectForKey:#"date"];
cell.dateLabel.text = [dateArray objectAtIndex:indexPath.row];
NSArray * monthArray = [dealsDict objectForKey:#"month"];
cell.monthLabel.text = [monthArray objectAtIndex:indexPath.row];
NSLog(#"I'm in here");
return cell;
why didn't it want to show anything?

In the IB, select your table view, and make sure that the delegate and dataSource outlets are set for your controller

UITableView need some collection dataset as data source. Its delegate method "cellForRowAtIndexPath" get called equals to number of elements of collection (ARRAY). like numberOfRowsInSection delegate tells the table view that it need to call cellForRowAtIndexPath for every element in collection. and it also need some iterator to read each time the next element, in case of dictionary it always stick to same data element at each call of cellForRowAtIndexPath. I hope it wil give you the idea of its working, Please let me know if you need anything else to know.
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [self.data count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
MessagesCell *cell = [tableView dequeueReusableCellWithIdentifier:#"messageCellIdentifier"];
Message *msg = [self.data objectAtIndex:indexPath.row];
cell.Name.text = msg.Name;
cell.Message.text = msg.MessageText;
cell.Time.text = msg.Time;
return cell;
}

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.

How to get a UITableViewCell's subtitle show how many times that specific cell was tapped?

I have a uitableview that displays the values of an array. I would like to know if there is a way to update the subtitle of its table cells, based on how many times each cell was tapped.
Thank you!
First of all, you'll want to use a NSMutableArray so you can change its contents after instantiation. Here's a basic over view of what I just tried to achieve your intended results:
In your interface
#property (strong, nonatomic) NSMutableArray *tapCountArray;
In your implementation
- (void)viewDidLoad
{
[super viewDidLoad];
self.tapCountArray = [NSMutableArray new];
int numberOfRows = 20;
for (int i = 0; i < numberOfRows; i ++) {
[self.tapCountArray addObject:#(0)];
}
}
Then the important part!
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return self.tapCountArray.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell" forIndexPath:indexPath];
NSString *text = [self.tapCountArray[indexPath.row] stringValue];
[cell.textLabel setText:text];
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[self.tapCountArray replaceObjectAtIndex:indexPath.row withObject:#([self.tapCountArray[indexPath.row] intValue] + 1)];
[self.tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
}
When each cell is tapped the number in its detailTextLabel will be incremented up by one.
You shouldn't create a new array or set, that could lead to problems if the two arrays get out of sync with each other. The way to do it, as you suggested in your comment, is to use dictionaries. The way you said you were doing that is probably not the way, however. You want an array of dictionaries where the values for one key would be whatever your main data is and the value for the other key would be the number of taps. For example, lets call the two keys main and sub, and your main data is a set of names. The array of dictionaries would look like this: ({main:#"Tom",sub:1}, {main:#"Dick", sub:0}, {main:#"Harry",sub:2}, .....). In the tableView:cellForRowAtIndexPath:indexPath method you would provide the data to the cells like this:
cell.textLabel.text = [[array objectAtIndex:indexPath.row] valueForKey:#"main"];
cell.detailTextLabel.text = [[array objectAtIndex:indexPath.row] valueForKey:#"sub"];
I think you can just set up another array of the same length as the one you have now. Then when your didSelectRowAtIndexPath is triggered, increment your indexPath.row entry of the new array and refresh that cell. If you don't expect to shuffle the table, you don't need a dictionary.
You can insert the object into an NSCountedSet, and on your cellForRowAtIndexPath method, you would take the model object for the cell and verify the number of times it has been inserted into the NSCountedSet instance.
Take a look at the NSCountedSet documentation: https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSCountedSet_Class/Reference/Reference.html

How can I store my UITableViewCells in a NSMutableArray?

Basically I'm making a list view that you can add things to the top of. The best way I can think of doing this is to store the UITableViewCells themselves in a NSMutableArray — Because I can simply pull them from the array them with all their data inside the object, and this list view will never be over 10 cells long.
Also note that I'm using Storyboards, hence the initWithCoder use.
The following code is what I'm trying, and it doesn't work:
// This is where my NSMutableArray is initialized:
- (id)initWithCoder:(NSCoder *)aDecoder
{
if (self = [super initWithCoder:aDecoder]) {
if (!_CellsArray) {
UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"TestCell"];
_CellsArray = [NSMutableArray arrayWithObject:cell];
}
}
return self;
}
//UITableView Delegate & DataSource Methods
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"TestCell"];
[_CellsArray insertObject:cell atIndex:0];
return [_CellsArray objectAtIndex:indexPath.row];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 10;
}
I realize I may be approaching this in the wrong way, that's why I'm here though :)
Thank you.
edit: fixed a type in the code (TimerCell -> UITableViewCell)
Let's look at the order things get called in and what happens.
Your view controller is unarchived, so your initWithCoder: method is called. This method creates a mutable array and puts one instance of TimerCell into it. Said instance is not further configured (unless you've overridden initWithStyle:reuseIdentifier: to do some configuration).
Your data source method tableView:numberOfRowsInSection: is called, and it tells the table view there are ten rows.
Thus, your tableView:cellForRowAtIndexPath: is called ten times. Each time, it creates a new instance of UITableViewCell and inserts it into your mutable array. (After ten calls, your mutable array contains one TimerCell at index 10 and ten UITableViewCells at indices 0-9.) It does nothing to configure the cell's contents or appearance, then it returns the cell at the specified row index. On the first call, you're asked for row 0, so the cell you just created and inserted at index 0 is returned. On the second call, you're asked for row 1, so the cell at index 1 in your array is returned -- since you just inserted a new cell at index 0, the cell you created on the last call has shifted to index 1, and you return it again. This continues with each call: you return the same unconfigured UITableViewCell ten times.
It looks like you're trying to out-think UIKit. This is almost never a good thing. (It's been said that premature optimization is the root of all evil.)
UITableView already has a mechanism for cell reuse; it's best to just keep track of your own cell content and let that mechanism do its thing. I took so long to type this that other answers have been written describing how to do that. Look to them, or to Apple's documentation or any third-party UITableView tutorial.
Why don't you just store the cell information in an array. Then in the -cellForRowAtIndexPath: method, just extract the data needed to change each cell.
Here is a simple example:
//Lets say you have an init like this that inits some cell information
- (id)initWithCoder:(NSCoder *)aDecoder
{
if (self = [super initWithCoder:aDecoder]) {
cellArray = [NSArray alloc] initWithObjects:#"firstCell",#"secondCell",#"thirdCell",nil];
}
return self;
}
//then for each cell, just extract the information using the indexPath and change the cell that way
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
// Configure the cell...
cell.textLabel.text = [cellArray objectAtIndex:indexPath.row];
return cell;
}
Table views don't store things. Rather, they just ask for the data they want to display, and you typically get that data from elsewhere (like an NSArray, or an NSFetchedResultsController). Just store the things you want into some data container, and let the table display them for you.
// Probably your data model is actually a member of your class, but for purposes of demonstration...
static NSArray* _myArray = [[NSArray alloc] initWithObjects:#"Bob", #"Sally", #"Joe", nil];
- (NSInteger) tableView:(UITableView*)tableView numberOfRowsInSection:(NSInteger)section
{
return [_myArray count];
}
- (UITableViewCell*) tableView:(UITableView*)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath
{
static NSString* CellIdentifier = #"TestCell";
// Make a cell.
UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if( cell == nil ) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
// Setup the cell with the right content.
NSString* aString = [_myArray objectAtIndex:[indexPath row]];
cell.textLabel = aString;
return cell;
}
Now if you want more stuff in the list, add it to your array, and you're done.
Edit: On another note, initWithCoder: isn't generally the best place to do initialization for a view controller. Reason being, at the point that it's called, there's a good chance that stuff isn't loaded yet (IBOutlets, for example). I tend to prefer viewDidLoad (don't forget to cleanup in viewDidUnload in that case), or awakeFromNib.

Using a Table Cell's Text to Access Corresponding Object

Currently, I have a savedWorkout class that is simply a Table View populated with different exercises in each cell. My goal now is for the user to be able to click on each individual exercise, which will take you to a new view filled with detailed information about it.
For this, I have created an Exercise class that will hold the detailed information about the new object. Is this possible?
Here is some pseudo-code I have written up:
if (Table View Cell's Text == ExerciseObject.exerciseName) {
Populate a view with the corresponding information;
}
Being new to iPhone programming, I'm not exactly sure what would be the best way to do this, and this is what i'm thinking would be the best way to go about it.
My Exercise class holds an NSString to keep track of the exercise name, and three NSMutableArray's to hold different information.
Please let me know if I am going in the right direction.
EDIT:
After trying to implement my pseudo-code this is what I came up with:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
Exercise *exerciseView = [[Exercise alloc] initWithNibName:#"Exercise" bundle:nil]; //Makes new exercise object.
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
NSString *str = cell.textLabel.text; // Retrieves the string of the selected cell.
exerciseView.exerciseName.text = str;
[self presentModalViewController:exerciseView animated:YES];
}
However, this doesn't seem to work. When the new view is presented, the label doesn't show up (I connected the UILabel exerciseName to my desired string). Am I implementing this wrong?
Yes, of course it's possible. Just use the delegate method:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath;
and check your data source cell based on the index location.
You might need to post your cellForRowAtIndexPath method. If done traditionally, it uses the indexPath.row to access an array of exercises to get a particular exercise, then changes cell properties based on the particular exercise. Is that about right?
It so, then you're halfway home.
EDIT
1) Use the code inside your cellForRowAtIndex path to init your str, as indicated here.
2) The new view controller view hasn't been built yet. You can't initialize a subview in the view hierarchy before the VC is ready. You need to pass the string to a property in that view controller (in a custom init method if you want), then on that class's viewDidLoad, you can set the exerciseName field to the string property you saved earlier. That subview shouldn't be part of the classes public interface.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// There should be an array of exercises, the same one used in cellForRowAtIndexPath:
NSString *str = [self.myArrayOfExercises objectAtIndex:indexPath.row];
// Just made code up here, but however you get a string to place in the cell
// in cellForRowAtIndexPath do that same thing here.
Exercise *exerciseView = [[Exercise alloc] initWithNibName:#"Exercise" bundle:nil];
// Might be wise to rename this ExerciseViewController, since it's probably (hopefully) a ViewController subclass
// no need to get a table cell, you have the info you need from your exercise array
//UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
//NSString *str = cell.textLabel.text; // Retrieves the string of the selected cell.
exerciseView.exerciseName.text = str;
[self presentModalViewController:exerciseView animated:YES];
}

Table view with custom cells

I have a tableview with custom cells in my App and each cell contains two checkbox buttons.
The problem is, when a scrolling event is triggered (up or down) it's reloading the tableview. Therefore, the checkbox buttons become to initial state.
Please give me a solution.
Thanks you
You're going to have to maintain a list yourself to determine which cells should be checked or not. Remember that in tableView:cellForRowAtIndexPath:, a proper implementation will recycle cells so that you never have more than 10-15 cells instantiated. This can cause some funky results if you don't handle for it properly. When I've done a poor implementation, I've seen certain cell properties "carry over" from one cell to the next.
Anyway, here's what I'd recommend (based on what I think you're asking):
1. Create a class to back each UITableViewCell
2. Create a property in that class to determine which of the two checkboxes (or neither or both) should be checked.
3. In your ViewController/TableViewController, maintain an NSMutableArray/NSArray where 1 item in the array = 1 cell in the UITableView.
4. In your tableView:cellForRowAtIndexPath: method, get a reference to the appropriate item in your array.
5. Then, check that instance's properties and set the checkbox values appropriately.
Sample Code:
TableView.h
#interface TableView : UITableViewController
#property (strong, nonatomic) NSMutableArray *itemArray;
#end
TableView.m
#implementation TableView
#synthesize itemArray;
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// Assume you get a valid, custom UITableViewCell at this point (named "cell")
// Configure the cell...
NSObject *classItem = [[self itemArray] objectAtIndex:[indexPath row]];
[[cell checkBox1] setChecked:[classItem checkbox1Checked]];
[[cell checkBox2] setChecked:[classItem checkbox2Checked]];
return cell;
}
#end
You should set the state of the button in the datasource and load this state when creating the cell. I wrote a small Xcode project to demonstrate this.
Well you should not use the TableView as the datasource.
Every time the cell comes into view the UITableViewDataSource is asked for the UITableViewCell at as indexpath.
- (void) tableView:(UITableView *)tableView setImage:(UIImage *)image forCellAtIndexPath:(NSIndexPath *)indexPath
In the is method you should set the checkbox state as it is reflected in your dataSource.
When the checkbox is changed save it in the data source with the selected state.
Example:
- (UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellIdentifier = #"CheckedTableViewCell";
CheckedTableViewCell *cell = (CheckedTableViewCell *)[tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (!cell) {
[[NSBundle mainBundle] loadNibNamed:cellIdentifier owner:self options:nil];
cell = (CheckedTableViewCell *)self.nibCell;
self.nibCell = nil;
}
item *item = [self objectAtIndexPath:indexPath];
cell.titleLabel.text = item.title;
cell.switch.on = item.selected;
return cell;
}
You could save the state in NSUserDefaults right when you click it. Just add a target with #selector(changedOne: ) and add the void statement:
- (void)changedOne: (id)sender {
NSUserDefaults *df = [NSUserDefaults standardUserDefaults];
NSString *row = [NSString initWithFormat:#"toggleOneRow%i",indexPath.row];
if (sender.on) {
[df setBool:YES forKey:row];
}
else {
[df setBool:NO forKey:row];
}
}
Are you using cellForRowAtIndexPath. If yes, then instead of
static NSString CellIdentifier=#"CellIdentifier"
use
NSString *CellIdentifier=[NSString stringWithFormat=#"CellIdentifier%d",indexPath.row];
Other approach you can take is assign tags to checkboxbuttons and take one dictionary in appDelegate file and set value for checkbox tag.initially you may set is either checked or unchecked.and set the values in cellforrowatindexpath method. set the values of checkboxes as per appdelegate dictionary.and update the state in appdelegate dictionary when user selects or deselects the button.