I have a UITableView that should have 33 rows. Each row represents a specific time slot in a day. When the view that holds the table view loads, I need it to populate each row accordingly.
I have an array of reservation objects that gets passed to the view. Each reservation contains a slot number, a reservation name and the duration of the reservation in slots.
What is the best way to populate the table, I am currently iterating through the array of reservations in the - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath method.
This is not giving me the results or the behavior I am expecting. The performance is extremly poor as it keeps iterating through loops and cells that shouldn't be blue are blue after scrolling. What is the best way to approach this? I have included the code below.
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 33;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
NSString *timeStamp = [NSString stringWithFormat:#"%.2f", (indexPath.row + 14.0 ) / 2.0];
timeStamp = [timeStamp stringByReplacingOccurrencesOfString:#".50" withString:#":30"];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
cell.textLabel.text = [NSString stringWithFormat:#"%#: ", timeStamp];
for (Reservation *temp in bookingsArray) {
if ((temp.slotNumber - 1) == indexPath.row) {
cell.textLabel.text = [NSString stringWithFormat:#"%#: %#", timeStamp, temp.reservationName];
cell.contentView.backgroundColor = [UIColor blueColor];
}
for (NSNumber *tempNo in temp.slotIdentifiers) {
if ([tempNo intValue] -1 == indexPath.row) {
//cell.textLabel.text = [NSString stringWithFormat:#"%#: Booked", timeStamp];
cell.contentView.backgroundColor = [UIColor blueColor];
}
}
}
return cell;
}
UPDATE
Trying the following gives me strange behaviour where all the cells turn blue after I start scrolling.
- (void)viewDidLoad
{
[super viewDidLoad];
bookManager = appDelegate.bookingManager;
bookingsArray = [[NSArray alloc] initWithArray:[bookManager getBookingsForCourt:1 onDate:[NSDate date]]];
namesArray = [[NSMutableDictionary alloc] init];
slotIndexSet = [NSMutableIndexSet indexSet];
for (int c = 0; c < 33; c++) {
[namesArray setObject:#"Available" forKey:[NSNumber numberWithInt:c]];
}
for (Reservation *temp in bookingsArray) {
[namesArray setObject:temp.reservationName forKey:[NSNumber numberWithInt:temp.slotNumber]];
for (NSNumber *slotNo in temp.slotIdentifiers) {
[slotIndexSet addIndex:[slotNo intValue] + 1];
}
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
NSString *timeStamp = [NSString stringWithFormat:#"%.2f", (indexPath.row + 14.0 ) / 2.0];
timeStamp = [timeStamp stringByReplacingOccurrencesOfString:#".50" withString:#":30"];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
cell.textLabel.text = [NSString stringWithFormat:#"%#: ", timeStamp];
cell.textLabel.text = [namesArray objectForKey:[NSNumber numberWithInt:indexPath.row]];
if ([slotIndexSet containsIndex:indexPath.row]) {
cell.contentView.backgroundColor = [UIColor blueColor];
}
return cell;
}
You need to do two things to speed this up:
Convert bookingsArray to a bookingBySlotNumber array in such a way that the object at index i has slotNumber - 1 equal to i. You can do it by iterating over the original bookings array when you receive it.
Create a NSIndexSet called isBookedBySlotNumber containing indexes of items that have been booked. You can prepare it by going through all Reservation.slotIdentifiers, and marking the indexes of isBookedBySlotNumber for items that have been booked.
With these two pre-processed items in place, you can eliminate the nested loops altogether: the outer one will be replaced by a lookup in bookingBySlotNumber, and the inner one - by a loopup in isBookedBySlotNumber.
Related
I have just implementet a bookmark/favorites function in my app, favorites from a tableview, with the following code, using NSUserDefaults. The cell has 2 labels, the name of the item and a price - stored in the Arrays - theArray and thePriceArray -> like this:
NSIndexPath *indexPath = [_ListTableView indexPathForCell:(UITableViewCell *) [[sender superview] superview]];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSMutableArray *OrderList = [[defaults objectForKey:ORDER_KEY] mutableCopy];
if (!OrderList) OrderList = [NSMutableArray array];
[OrderList addObject:[theArray objectAtIndex:indexPath.row]];
[OrderList addObject:[thePriceArray objectAtIndex:indexPath.row]];
[defaults setObject:OrderList forKey:ORDER_KEY];
[defaults synchronize];
I am now adding the two arrays theArray & thePriceArray, to the NSMutableArray.I now want to show these information (the information from the arraies) in another tableview. I do this like so:
In My viewDidAppear:
NSUserDefaults *defaults = kSettings;
NSMutableArray *theOrderList = [NSMutableArray arrayWithArray:[defaults objectForKey:ORDER_KEY]];
And to show the contents in the tableview:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [theOrderList count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *simpleTableIdentifier = #"TheTableCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpleTableIdentifier];
}
cell.textLabel.text = [theOrderList objectAtIndex:indexPath.row];
return cell;
}
This is almost working as I want, the contents of theArray and thePriceArray is shown, but in a long list like so:
I want the "key and the value" to be in one cell, so the price and the item name in one cell (Hat - 30) and not seperate, how can I do that? I have tried to use a NSDictionary, but without luck and can I use NSDictionary for this?
You can create two new arrays for favItem and favPrize. Just add your favorite Items to favItem Array and add favorite Prizes to favPrize Array. Now use these arrays to set the labels and detailLabels of your Tableview like :
cell.textLabel.text = [favItems objectAtIndex:indexPath.row];
cell.detailTextLabel.text = [favPrize objectAtIndex:indexPath.row];
Keep both array theArray and thePriceArray seperate and use one as your main array for data source of tableView.
Note : add data from NSUserDefault to respective array.
Now method will be :
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [theArray count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *simpleTableIdentifier = #"TheTableCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubTile reuseIdentifier:simpleTableIdentifier];
}
cell.textLabel.text = [theArray objectAtIndex:indexPath.row];
cell.detailTextLabel.text = [thePriceArray objectAtIndex:indexPath.row];
return cell;
}
You can simply do it using the following code:
NSString *label = [NSString stringWithFormat:#"%# %#",[theOrderList objectAtIndex:(indexPath.row * 2)],[theOrderList objectAtIndex:(indexPath.row * 2)+1]
cell.textLabel.text = label;
And also change your numberOfRowsInSection like:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [theOrderList count]/2;
}
I'm trying to get a tableview starting from a string in this format "name1,link1,name2,link2..."
So what i'm actually doing is this:
-Get the string and put links and names into an array, then split the array into two lesser ones by the position of the objects
- (void)viewDidLoad
{
NSString *dataString = #"a,www.google.it,b,www.apple.it,c,www.youtube.it";
dataArray = [dataString componentsSeparatedByString:#","];
for (int i=0; i<[dataArray count]; i++) {
if (i%2==0) {
[dataArrayName addObject:[dataArray objectAtIndex:i]];
}
else {
[dataArrayLink addObject:[dataArray objectAtIndex:i]];
}
}
[super viewDidLoad];
}
-Set up the table view
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section;
{
return [dataArrayName count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *MyIdentifier = #"myCell";
// Try to retrieve from the table view a now-unused cell with the given identifier.
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier];
// If no cell is available, create a new one using the given identifier.
if (cell == nil) {
// Use the default cell style.
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"myCell"];
}
// Set up the cell.
NSString *cellName = [dataArrayName objectAtIndex:indexPath.row];
cell.textLabel.text = cellName;
return cell;
}
But when i run the application the view with the tableview is clear (there are empty rows)
What's the problem?
Have you initialized dataArrayName and dataArrayLink (ie, at some point, before you started adding objects to it, did you say dataArrayName = [[NSMutableArray alloc] init] or an equivalent?
I have a UITableView that I'm using to try and display 2 different objects - receipts and milages.
This is the original code I was using to try and accomplish this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
Project *project = [[Project alloc] init];
project = [appDelegate.projects objectAtIndex:indexPath.section];
if (indexPath.row >= [project.receipts count])
{
if (indexPath.row >= [project.milages count]){
return NULL;
}
else {
//Create a new journey cell and set its UI values
MilageCell *cell = (MilageCell *)[tableView
dequeueReusableCellWithIdentifier:#"MilageCell"];
Milage *milage = [project.milages objectAtIndex:indexPath.row];
cell.locationLabel.text = milage.location;
cell.dateLabel.text = [NSString stringWithFormat:#"%#", milage.date];
cell.totalLabel.text = [NSString stringWithFormat:#"£%#", milage.total];
return cell;
}
}
else
{
ReceiptCell *cell = (ReceiptCell *)[tableView
dequeueReusableCellWithIdentifier:#"ReceiptCell"];
Receipt *receipt = [project.receipts objectAtIndex:indexPath.row];
cell.dateLabel.text = receipt.receiptDate;
cell.descriptionLabel.text = receipt.descriptionNote;
cell.amountLabel.text = [NSString stringWithFormat:#"£%#", receipt.amount];
return cell;
}
}
I'm aware that if I have 1 object in each of the arrays (project.receipts and project.milages) then this code won't work at all. I also know you can't return NULL from this method. What I'm trying to do is somehow display all my receipt objects, then all my milage objects (so say section 0 in the table view had 3 receipts and 3 milages, it would firstly display the receipts, then the milages). However I have absolutely no idea how to do this, can someone explain how I might solve this problem? Is there some way I could construct a single array of all the milage and receipt objects and then somehow discern which what kind of object I have in this method in order to use the appropriate cell type?
Thanks a lot,
Jack
Try this, I've just removed an if condition and subtracted the [project.milages count] from the index.row for the Receipt cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
Project *project = [[Project alloc] init];
project = [appDelegate.projects objectAtIndex:indexPath.section];
if (indexPath.row >= [project.milages count]){
ReceiptCell *cell = (ReceiptCell *)[tableView
dequeueReusableCellWithIdentifier:#"ReceiptCell"];
Receipt *receipt = [project.receipts objectAtIndex:indexPath.row-[project.milages count]];
cell.dateLabel.text = receipt.receiptDate;
cell.descriptionLabel.text = receipt.descriptionNote;
cell.amountLabel.text = [NSString stringWithFormat:#"£%#", receipt.amount];
return cell;
}
else {
//Create a new journey cell and set its UI values
MilageCell *cell = (MilageCell *)[tableView
dequeueReusableCellWithIdentifier:#"MilageCell"];
Milage *milage = [project.milages objectAtIndex:indexPath.row];
cell.locationLabel.text = milage.location;
cell.dateLabel.text = [NSString stringWithFormat:#"%#", milage.date];
cell.totalLabel.text = [NSString stringWithFormat:#"£%#", milage.total];
return cell;
}
}
You can also use an UITableView with two different sections.
i have a UItableView and i have two buttons on each cell. You can add or subtract 1 from the cell's textLabel. I add the cells current value +1 with this:
- (IBAction)addLabelText:(id)sender{
num = [NSString stringWithFormat:#"%d",[cell.textLabel.text intValue] +1];//<--- num is an NSNumber
number = [[NSMutableArray alloc]initWithObjects:num, nil];//<---- number is an NSMutableArray
[myTableView reloadData];
}
and I am trying to subtract the text and store it in an array with this:
- (IBAction)subtractLabelText:(id)sender
{
if ( [[cell.textLabel text] intValue] == 0){
num = [NSString stringWithFormat:#"%d",[num intValue] +0];
[number addObject:num];
[myTableView reloadData];
}
else{
num = [NSString stringWithFormat:#"%d",[num intValue] -1];
[number addObject:num];
[myTableView reloadData];
}
}
and im trying to set the cell.textLabel.text in the cellForRow like this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath: (NSIndexPath *)indexPath
{
static NSString *identifier = #"Cell";
cell = [tableView dequeueReusableCellWithIdentifier:identifier];
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier] autorelease];
}
cell.textLabel.text = [number objectAtIndex:indexPath.row];//<---IM USING THIS LINE TO SET THE NEW TEXTLABEL
cell.textLabel.text = #"1";
return cell;
}
MY PROBLEM
So, the addition works, but the subtraction does not. It doesnt work at all when i press the button on the cell. Thanks in advance!!
At the risk of pointing out the obvious... you seem to have hard-coded the text property to #"1".
cell.textLabel.text = [number objectAtIndex:indexPath.row];//<---IM USING THIS LINE TO SET THE NEW TEXTLABEL
cell.textLabel.text = #"1";
The first line is probably doing what you think... but then you're immediately changing it back to #"1".
EDIT - Based on clarification in comments, here's what I think you want to do. I will modify your own code as posted.
Note that I put my addition into viewDidLoad as an example. You could do this in init, or any number of other places, at whatever point you know how many cells you want to show.
- (void)viewDidLoad {
self.number = [[[NSMutableArray alloc] init] autorelease];
for (int i = 0; i < [HOW MANY CELLS DO YOU WANT?]; i++) {
[self.number addObject:#"1"];
}
[myTableView reloadData];
}
- (IBAction)addLabelText:(id)sender {
// Note that I'm assuming here that your button is a direct child of the cell.
// If not, you'll need to change this.
UITableViewCell *cell = [sender superview];
NSInteger newNumber = [cell.textLabel.text intValue] + 1;
NSString *newNumberString = [NSString stringWithFormat:#"%d", newNumber];
[self.number replaceObjectAtIndex:cell.tag withObject:newNumberString];
[myTableView reloadData];
}
- (IBAction)subtractLabelText:(id)sender {
// Note that I'm assuming here that your button is a direct child of the cell.
// If not, you'll need to change this.
UITableViewCell *cell = [sender superview];
NSInteger newNumber = [cell.textLabel.text intValue] - 1;
newNumber = (newNumber < 0) ? 0 : newNumber;
NSString *newNumberString = [NSString stringWithFormat:#"%d", newNumber];
[self.number replaceObjectAtIndex:cell.tag withObject:newNumberString];
[myTableView reloadData];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *identifier = #"Cell";
cell = [tableView dequeueReusableCellWithIdentifier:identifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier] autorelease];
}
cell.tag = indexPath.row; // Important for identifying the cell easily later
cell.textLabel.text = [self.number objectAtIndex:indexPath.row];
return cell;
}
I don't want to get too much more in depth, but I would recommend you take a look at reloading only the cell modified, instead of calling [myTableView reloadData] every time.
I started this project with a simple plist of a dictionary with two arrays of strings. I now want to add more information and want to use this plist structure:
Root - Dictionary - (2 items)
Standard - Array - (3 items)
Item 0 - Dictionary - (4 items)
Color - String - Red
Rvalue - String - 255
Gvalue - String - 0
Bvalue - String - 0
Sorry about typing in the plist but the site would not let me post an image
I know that the RGB values could be numbers instead of strings but I have a reason for them being strings.
This is the code I used to read the simple plist:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSUInteger section = [indexPath section];
NSUInteger row = [indexPath row];
NSString *key = [keys objectAtIndex:section];
NSArray *colorSection = [colors objectForKey:key];
static NSString *SectionsTableIdentifier = #"SectionsTableIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:SectionsTableIdentifier];
if(cell == nil){
cell=[[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier: SectionsTableIdentifier] autorelease];
}
cell.textLabel.text = [colorSection objectAtIndex:row];
[cell setAccessoryType:UITableViewCellAccessoryDetailDisclosureButton]; //add disclosure button to rows
return cell;
}
My question is what is the specific code to retrieve the contents of the color dictionaries to get the Colors for the cell.textLabel.text and also read the RGB values to add a subtitle. I've been working on this for several days and have read references and lots of examples and unfortunately can't solve the problem. Your assistance will be greatly appreciated.
So providing you have your Standard - Array stored against a Array you've defined in your .h file then something like this would work. In this example the array is stored against self.coloursArray.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *SectionsTableIdentifier = #"SectionsTableIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:SectionsTableIdentifier];
if(cell == nil){
cell=[[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier: SectionsTableIdentifier] autorelease];
}
NSString* ColourString = [[self.coloursArray objectAtIndex:indexPath.row] valueForKey:#"Colour"];
NSString* rValue = [[self.coloursArray objectAtIndex:indexPath.row] valueForKey:#"Rvalue"];
NSString* gValue = [[self.coloursArray objectAtIndex:indexPath.row] valueForKey:#"Gvalue"];
NSString* bValue = [[self.coloursArray objectAtIndex:indexPath.row] valueForKey:#"Bvalue"];
cell.textLabel.text = ColourString;
NSString* subCellString = [NSString stringWithFormat:#"%#:%#:%#", rValue, gValue, bValue];
}
Hopefully that'll give a a hand.
First, do not use -[UITableViewCell initWithFrame:reuseIdentifier:]. It has been deprecated and will give you a warning, plus it will make your subtitle harder to implement. This code is a modified version of yours which loads the information, sets the title to the Color property, and sets the subtitle to a string containing the Rvalue, Gvalue, and Bvalue properties.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSUInteger section = [indexPath section];
NSUInteger row = [indexPath row];
NSString *key = [keys objectAtIndex:section];
NSArray *colorSection = [colors objectForKey:key];
static NSString *SectionsTableIdentifier = #"SectionsTableIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:SectionsTableIdentifier];
if(cell == nil) {
cell=[[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier: SectionsTableIdentifier] autorelease];
}
NSDictionary *color = [colorSection objectAtIndex:row];
cell.textLabel.text = [color objectForKey:#"Color"];
cell.detailTextLabel.text = [NSString stringWithFormat:#"%#, %#, %#",[color objectForKey:#"Rvalue"],[color objectForKey:#"Gvalue"],[color objectForKey:#"Bvalue"]];
[cell setAccessoryType:UITableViewCellAccessoryDetailDisclosureButton]; //add disclosure button to rows
return cell;
}