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.
Related
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 currently retrieving objects from Parse.com and saving them to CoreData.
I am then next using NSFetchedResultsController to retrieve objects from CoreData. These objects will then be used to create a table view. Everything i retrieve from CoreData is stored in an NSArray using the following code:
NSArray *fetchedObjects = _fetchedResultsController.fetchedObjects;
Using the fetched objects array i am wanting to load a specific nib file depending on the type of each object. So using the following for loop within cellForRowAtIndexPath i am trying to achieve this:
for (NSManagedObject *o in fetchedObjects)
{
if ([[o valueForKey:#"type"] isEqual: #"Type1"])
{
Type1CustomCell *cell = (Type1CustomCell *)[tableView dequeueReusableCellWithIdentifier:#"type1CustomCell"];
return cell;
}
else if ([[o valueForKey:#"type"] isEqual: #"Type2"])
{
Type2CustomCell *cell = (Type2CustomCell *)[tableView dequeueReusableCellWithIdentifier:#"type2CustomCell"];
return cell;
}
}
The previous code is just an example using 2 types, but within the app there may be more.
The return statement cause the loop to end, which means the loop never gets past the first object. Could someone please give me a point in the right direction of how to load multiple nib files depending on the type of the object I have retrieved?
Thanks
So, the only time you dequeue and return reusable collection view cells is in the datasource method that asks for a cell.
When this method fires, it's given you a specific index path--the index path for the row it's trying to create.
You don't need to be looping through anything in this method. You just need to go to the right index of whatever collection you're storing your data in, grab the object at that index. Use that data to determine what cell to return.
Instead of a forin loop, just grab a single object.
NSManagedObject *obj = [fetchedObjects objectAtIndex:indexPath.row];
if ([[obj valueForKey:#"type"] isEqual: #"Type1"]) {
// etc...
You'll still need a large if-else structure here, I believe, but now we're just checking an object at the specific index the table view is trying to create the cell for.
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).
i'm pretty sure this is really simple. But i can't get to make this work. I have a UITableView where i display dynamically a list of facebook friends, thanks to their FBID. Basically, i would like to return the FBIDs of the friends i selected, in one string separated with commas. If possible, in a IBAction, so i can pass them into parameters of a php.
For example, let's pretend i have a list of 4 friends, A B C D, and their ids are 1,2,3,4.
If i select A and C, i would like to have a string which says 1,3.
All i managed to do is to display the id of the friend i select, one at a time.
Here's my code :
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
rowcount = [[tableView indexPathsForSelectedRows] count];
indexer = [idFriends objectAtIndex:indexPath.row];
aapell = [NSString stringWithFormat:#"%#", indexer];
NSMutableString * arrayIds = [[NSMutableString alloc] init];
[arrayIds appendString:aapell];
NSLog(#"ids: %#", arrayIds);
}
Thank you in advance, i'm sure this is not complicated.
-(IBAction)gatherFBIds
{
NSString *listOfFacebookIDs = #"";
NSArray *indexPathArray = [self.tableView indexPathsForSelectedRows];
for(NSIndexPath *index in indexPathArray)
{
//Assuming that 'idFriends' is an array you've made containing the id's (and in the same order as your tableview)
//Otherwise embed the ID as a property of a custom tableViewCell and access directly from the cell
//Also assuming you only have one section inside your tableview
NSString *fbID = [idFriends objectAtIndex:index.row];
if([indexPathArray lastObject]!=index)
{
listOfFacebookIDs = [listOfFacebookIDs stringByAppendingString:[fbID stringByAppendingString:#", "]];
}
else
{
listOfFacebookIDs = [listOfFacebookIDs stringByAppendingString:fbID];
}
}
NSLog(#"Your comma separated string is %#",listOfFacebookIDs);
//Now pass this list to wherever you'd like
}
The problem is that you are not using the information in indexPathsForSelectedRows. That is telling you all the selected rows. Instead, you are just looking at indexPath.row, which is the one row most recently selected.
What you need to do is cycle through indexPathsForSelectedRows and gather up the info for every row that is currently selected (I'm assuming you've enabled multiple selection here).
Use the following code to get the selected rows in a table view. :)
NSArray *selectedRows=[tableView indexPathsForSelectedRows];
NSMutableArray *rownumberArray=[[NSMutableArray alloc]init];
for (int i=0; i<selectedRows.count; i++) {
NSIndexPath *indexPath = [selectedRows objectAtIndex:i];
NSInteger row = indexPath.row;
NSNumber *number = [NSNumber numberWithInteger:row];
[rownumberArray addObject:number];
}
// rownumberArray contains the row numbers of selected cells.
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.