I have a data model with entity Customer. The Customer has attributes like name, address...etc. One of these attributes is a call back date. I want to load into the table only the Customers with call back date of today. below is the code I have to check to see if the dates are equal and then to create the cell. The problem is when the dates are not equal and it skips the creation of the cell. How do I skip that specific customer and move to the next one?
if(date==date2 && month==month2 && year==year2)
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
NSString *string = [NSString stringWithFormat:#"%# %#", cust.firstName, cust.lastName];
cell.textLabel.text = string;
return cell;
}
return nil;
}
I'd take a different route altogether.
Rather than not showing anything in the cell, simply don't provided data for there to even be a cell. You mention that you're using models so I assume you're using core data?
If so, then change your predicate when you fetch your models to ignore all objects that don't meet your criteria. Then you can just show every object in your table as you know that there aren't any you don't want.
Alternatively, fetch everything (if perhaps you're not using core data) then apply a predicate to the array of data you're using and filter it out that way.
Related
I have a custom TableViewCell that I am getting data from a database in an asynchronous function that returns a UserObject that has the data I need in it. My problem is that the cellForRowAtIndexPath is returning the cell before that Asynchronous block is completed. How do i solve this problem?
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *simpleTableIdentifier = #"ImageWorkoutCellCollapsed";
ImageWorkoutCellCollapsed *cell = (ImageWorkoutCellCollapsed *)[tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if(cell == nil)
{
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"ImageWorkoutCellCollapsed" owner:self options:nil];
cell = [nib objectAtIndex:0];
}
WorkoutObject *workout = [[WorkoutObject alloc]init];
workout = [appDelegate.workoutObjectsArray objectAtIndex:indexPath.row];
cell.workoutTitle.text = workout.workoutTitle;
cell.workoutViewCount.text = workout.workoutDescription;
__block UserObject *cellUserObject = [[UserObject alloc]init];
[dbPointer getUserObjectFromDB:workout.workoutOwnerID completion:^(UserObject *result)
{
cellUserObject = result;
}];
cell.userName.text = cellUserObject.username;
return cell;
}
You should turn the cell update into a reload of the row as follows:
1) You should use dequeueReusableCellWithIdentifier:forIndexPath rather than the version you are using.
2) You should not need to check (cell == nil) as 1) should never return nil.
3) Add a new NSMutableDictionary property to cache UserObject's by workoutOwnerID.
4) When you are asked for a cell, lookup the dictionary from 3) to see if it has your data object. if not, then run the DB query. If it has the object, set the cell values.
5) In your completion handler for the DB lookup, cache the returned object into the new dictionary by workoutOwnerID. Then simply request the table to reload the row.
The result is that the cells are updated when the data they represent is updated.
You should make sure cellForRowAtIndexPath is not being called before the data is available.
You have a count of the number of rows, you must be setting this count to N but you haven't yet fetched N data items.
Don't set this number until you have fetched all the data.
OR
As data arrives continually update the number or rows and refresh the table view.
OR
Return the cell with placeholder data. Then once the actual data for the cell is available update the content and refresh the table.
OR
All of the above solutions involves moving the data fetch out of cellForRowAtIndexPath. However IFF the call to fetch the data is quick and thus won't slow down the drawing of the table, you need to convert the fetch from being asynchronous to synchronous. But it is not good design for a view controller component to directly access a db, instead it should be going to a model and the model should abstract away the implementation detail that the data is in a database.
Ok so I have very little experience with programming so please be patient. Let me explain further...I have a player class that has different properties. In the table view I number first name and last name of the player. I want to loop through each cell, and take the number first name and last name and add it to a new array. I don't want to be just add them as strings. I want to put them into a pickerview after ward. I have an dictionary that matches each number to a player ID. How would i do all of this? I have a loop that goes through tableviewcells which is like this
for (_tableViewCell in self.homePlayers.visibleCells)
{
if (_tableViewCell.accessoryType == UITableViewCellAccessoryCheckmark)
{
[_homeConfirmedPlayersArray addObject:[NSString stringWithFormat:#"%d %# %#",_homePlayer.number,_homePlayer.firstName,_homePlayer.lastName]];
}
}
homePlayers is the tableView that I am looping through. The problem is that it does go through each cell but it only takes the data from the last cell and adds it the new array once for each cell. I end up with 8 objects of the number first name and last name.
I set up the homePlauer object in cell for row at index path like this...
if ([tableView isEqual:self->_homePlayers])
{
static NSString *cellIdentifier = #"cell";
//Step 1: Check to see if we can reuse cell from a row that is now off the screen.
_tableViewCell = [_homePlayers dequeueReusableCellWithIdentifier:cellIdentifier];
//Step 2: If no reusable cells create a new one
if (_tableViewCell == nil)
{
_tableViewCell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
}
//Add detail view accessory
_tableViewCell.accessoryType = UITableViewCellAccessoryCheckmark;
//Step 3: Set up cell text
_homePlayer = _homePlayersArray[indexPath.row];
_tableViewCell.textLabel.text = [NSString stringWithFormat:#"%d %# %#",_homePlayer.number,_homePlayer.firstName,_homePlayer.lastName];
//Step 4: Return the cell
return _tableViewCell;
}
If you need anymore information please ask. Thanks for the answers ahead of time!
It looks like you did a problem a lot of new programmer do, globalize everything. _homePlayer looks like it should be a local variable. What I assume is happening is when the UITableView is populating itself by calling tableView:cellForRowAtIndexPath:, the last cell that will be generated will set the global _homePlayer. Then when you other loop function gets called, _homePlayer will have already been set, and you also never change it in your loop function. That's why you get the same 8 objects. Here's how to fix it:
Make step 3 this:
id homePlayer = _homePlayersArray[indexPath.row];
tableViewCell.textLabel.text = [NSString stringWithFormat:#"%d %# %#", homePlayer.number, homePlayer.firstName, homePlayer.lastName];
You should replace 'id' with the homePlayer type so the compiler will assist you with auto completion.
I think you mentioned it was a NSDictionary, so replace id with NSDictionary *.
For your loop function do this: (comments for explanation)
// create a new and empty array
// (your local array will forever fill up with repeated objects unless emptied somewhere)
NSMutableArray *homeConfirmedPlayersArray = [[NSMutableArray alloc] init];
// loop through all indexpaths for the visible cells
for (NSIndexPath *indexPath in [self.homePlayers indexPathsForVisibleRows]){
// get the tablecell, only to check the accessory type
UITableViewCell *cell = [self.homePlayers cellForRowAtIndexPath:indexPath];
// I don't think this is necessary, especially if every cell has a checkmark
if (cell.accessoryType == UITableViewCellAccessoryCheckmark){
// get the record from the home players array
NSDictionary *homePlayer = _homePlayersArray[indexPath.row];
// add to the copy
[homeConfirmedPlayersArray addObject:[NSString stringWithFormat:#"%d %# %#",homePlayer.number,homePlayer.firstName,homePlayer.lastName]];
}
}
Remember to only use global variables only when you need them to be global.
I am populating a tableview from data that is received from a server. The data is a list of user activities within a given timeframe. One such activity is "Login". I do not wish to populate my tableview with this string but I'm not sure how to skip it when populating my tableview.
Here is how I populate the cell
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{ static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
#try{
NSManagedObject *object = [self.fetchedResultsController objectAtIndexPath:indexPath];
NSString *action = [object valueForKey:#"theActionName"];
if ([action isEqualtoString:#"Login"]) {
return cell;
}
return cell;
}#catch (NSException *ex) {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
return cell;
}
}
As you can see I tried using return cell but as you probably know it gives me a blank cell when the table is displayed. I'm sure there is a simple line of code for this but I came up blank with the search terms I used. Could someone please enlighten me! Thanks!
P.S. you may be thinking I am not putting anything in any of the cells but I pulled out a bunch of code to keep this short.
UPDATE:
Thanks for the heads up on "isEqualtoString:" Everything worked fine with "isEqual" but I changed it given that I received so many suggestions to do so. But this is not what I am asking.
To be more clear if I had an array containing the terms: view, view, login, view. When my tableview was populated I would have 4 cells that said; view, view, login, view. I simply want to ignore the term login so that I would have 3 cells that all said view. Thanks!
There can be many way to do this.
I Belive that UITabelView should display what its datasource (here datasource is self.fetchedResultsController) contains.
What you can do is create another NSArray from self.fetchedResultsController which does not contain this object.
Try this:
NSMutableArray *newSource = [[NSMutableArray alloc] init];
for(int i = 0; i < self.fetchedResultsController.count ; i++)
{
NSManagedObject *object = [self.fetchedResultsController objectAtIndexPath:indexPath];
NSString *action = [object valueForKey:#"theActionName"];
if (![action isEqual:#"Login"])
{
[newSource addObject:action];
}
}
[tableView reloadData];
Now use newSource instead of self.fetchedResultsController
You might think that using one more array is not good. But believe it it is far easier than using the same array with condition. You don't have to worry about that condition when you perform some operation with your UITableView like remove object by using indexpath.
try using if ([action isEqualToString:#"Login"])
When you want to compare strings you need to use this isEqualToString .
Change this line
if ([action isEqualToString:#"Login"]) {
return cell;
}
You are using the wrong function to compare your input string and the given data variable.
They both are NSString objects so use :
if([action isEqualToString:#"Login"])
{
//enter your code here
}
#Ben : I am assuming that you have registered you cell through nib as you are using dequeueReusableCellWithIdentifier.
Make your tableview content as "Dynamic prototype" (You can see this in Attributes Inspector of table view) and change your table view cell style as custom (You can see this in Attributes Inspector of tableview cell).
Hi I am new to iphone Application Development, but this question is more of coding related.I am using a tableview to display users name(textLabel) with phoneNumbers(detailtextLabel) using Addressbook of course. I have stored both of them in separate arrays. Also, I have an email addresses stored in a separate array. and using all this I am successfully able to display the contacts in sorted order in the table view created. The problem comes in the implementation of searchDisplayController. Here I want to be able to search by users name and emailid(either).
So, I am using an NSDictionary to store name+email as a value (NSString format) with keys as the index of the row on which it is displaying that contact. Now I am successfully getting an array of all the matching contacts using the following code.
- (void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope{
NSPredicate *resultPredicate = [NSPredicate
predicateWithFormat:#"SELF contains[cd] %#",
searchText];
searchResults = nil;
matchingContacts = nil;
NSArray *allContactValues = [allContactNamesWithEmailIDs allValues];
searchResults = (__bridge CFArrayRef)([[allContactValues sortedArrayUsingSelector:#selector(caseInsensitiveCompare:)] filteredArrayUsingPredicate:resultPredicate]);
matchingContacts = [[NSMutableArray alloc] initWithArray:(__bridge NSMutableArray*)searchResults];
The matchingContacts gives back the array of all the matching contact Names. all
But using this has a drawback when there are two users with same name(same value bt different keys), I could not trace it back to different keys. It is display the same names with same phoneNumbers twice which would be a problem.
The code creating the cells is as follows:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
if(tableView == self.searchDisplayController.searchResultsTableView){
if (farmMarkets.count>0) {
destIndex = [[[allContactNamesWithEmailID allValues] sortedArrayUsingSelector:#selector(caseInsensitiveCompare:)] indexOfObject:matchingContacts[indexPath.row]]; //?? I believe this is what needs to be modified to display multiple contacts. Just don't know how.
[cell.textLabel setText:farmMarkets[destIndex]];
if (destIndex<0) {
[cell.detailTextLabel setText:#""];
}
else{
[cell.detailTextLabel setText:phoneNumbersArray[destIndex]];
}
}
}
}
Can someone give me an example with code, explaining how this should be better implemented? All ideas will be helpful. In an all I will summarise it now. I want to implement a tableView displaying contacts with Name and phone Number and a search bar at the top. And when i search using either name or email ID it should display the names with corresponding phone Number.
From your description you have a lot of different methods of storing data and your problem comes form trying to correlate them together at different times.
I would simplify your structure so you have a single array of source information where that array contains dictionaries. Each dictionary contains all of the data for one person.
Now, you can sort that array as required and on any (combination of) key in the dictionaries. Also, when you filter, you filter the array (or, better, a copy of it so you still have the original) so the order is maintained and you don't need to correlate to anything else.
I'm populating a TableViewController given an array of NSManagedObjects of class "SampleClass". Although the number of objects in the fetched array returns correctly (i.e. if the table is to display 59 objects, 59 rows are created), the other parameters for the cell are "glitchy" to display.
For example, where there should be a title set by object.name, "null" until you click on the the cell, select another cell, and then come back to the initial cell.
I've tried `[tableview reloadData]' in the view will appear, and it still takes too long to load the data. Is there a better way to fill in the cell instead of:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
}
Class *classObject = [dataArray objectAtIndex:indexPath.row];
cell.textLabel.text = [classObject name].....
where dataArray points to the NSMutableFetchedArray from a coreData call
Note: the simulator misses filling more of the cells than the actual iPad. once another tab is selected however, all the cells display correctly when you return to the tab.`
You may want to look into NSFetchedResultsController
http://developer.apple.com/library/iOS/ipad/#documentation/CoreData/Reference/NSFetchedResultsController_Class/Reference/Reference.html
It is a special class which is meant specifically for populating a table with results from a core data query.
In the past when using core data I have always used this class over other means.