I have a UITableView which is being filled from Core Data by a NSFetchedResultsController. Everything works great. However, I have just implemented a UISegmentedControl at the top of the UITableView, which I would like to sort the results. There are three segments: #"All", #"Boys", #"Girls". In viewDidLoad, I instantiate a NSDictionary with three NSFetchedResultsController. Each has the exact same fetch request with a different predicate.
allFetchRequest.predicate = [NSPredicate predicateWithFormat:#"school.schoolID == %#", [NSNumber numberWithInt:[_schoolID intValue]]];
boysFetchRequest.predicate = [NSPredicate predicateWithFormat:#"school.schoolID == %# AND gender == %#", [NSNumber numberWithInt:[_schoolID intValue]], #"M"];
girlsFetchRequest.predicate = [NSPredicate predicateWithFormat:#"school.schoolID == %# AND gender == %#", [NSNumber numberWithInt:[_schoolID intValue]], #"F"];
When the UISegmentControl value is changed, I call a method which changes the view controller's "currentFetchedResultsController" instance variable to the corresponding NSFetchedResultsController for that segment, calls perform fetch, then calls reloadData on the tableView in the main thread.
- (void)showBoys
{
self.currentFetchedResultsController = self.fetchResultsControllerDictionary[#"boys"];
[self.currentFetchedResultsController performFetch:nil];
[self.tableView performSelectorOnMainThread:#selector(reloadData) withObject:nil waitUntilDone:NO];
}
It all seems to work great, except that the UITableView never seems to update its UI. It seems to show the correct number of sections for all, boys, and girls, but the object at each index doesn't change. For example, let's say we have 11 people in Core Data. Five guys, six girls. The view loads with the "All" segment pre-selected, so all 11 people load into the UITableView. However, when I switch the segment to "Boys", the number of rows will drop to five, but the objects in those rows never change. The UITableView will continue to show the first five objects that were already in the table, even if some are girls (gender == "F" in Core Data).
I know that the fetch is working properly because I have set up a small test:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
User *user = [self.currentFetchedResultsController objectAtIndexPath:indexPath];
NSLog(#"Username = %# & Gender = %#", user.username, user.gender);
}
Now, when I select a row, it logs the correct username and gender that SHOULD be at that indexPath. However, the logged username is different than the one that appears in the UITableView at that row.
Table View Data Source Methods:
- (UserTableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Home Ranked Cell";
UserTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
User *user = [self.currentFetchedResultsController objectAtIndexPath:indexPath];
NSString *name = user.name;
if(!name || [name isEqualToString:#""])
name = user.username;
cell.name.text = name;
UserTableViewCell *previousCell = nil;
if(indexPath.row != 0)
previousCell = (UserTableViewCell *)[self tableView:tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:indexPath.row - 1 inSection:indexPath.section]];
NSInteger previousVotes = 0;
if(previousCell)
previousVotes = [previousCell.votes.text integerValue];
if(!previousCell) {
cell.rank.text = [NSString stringWithFormat:#"%i", 1];
} else if(previousVotes == [user.votes integerValue]) {
cell.rank.text = previousCell.rank.text;
} else {
cell.rank.text = [NSString stringWithFormat:#"%i", [previousCell.rank.text integerValue] + 1];
}
if(user.profilePicture && user.profilePicture.thumbnailData && ![user.profilePicture.thumbnailData isEqualToString:#""]) {
NSData *imageData = [[NSData alloc] initWithBase64EncodedString:user.profilePicture.thumbnailData options:0];
cell.imageView.image = [UIImage imageWithData:imageData];
}
if(!cell.imageView.image)
cell.imageView.image = [UIImage imageNamed:#"xIcon.png"];
cell.votes.text = [NSString stringWithFormat:#"%i", [user.votes integerValue]];
cell.upButton.tag = indexPath.row;
cell.downButton.tag = indexPath.row;
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
User *user = [self.currentFetchedResultsController objectAtIndexPath:indexPath];
NSLog(#"Username = %# & Gender = %#", user.username, user.gender);
}
- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView
{
return [self.currentFetchedResultsController sectionIndexTitles];
}
- (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index
{
return [self.currentFetchedResultsController sectionForSectionIndexTitle:title atIndex:index];
}
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
return self.segmentControl;
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return [[self.currentFetchedResultsController sections] count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
id sectionInfo = [[self.currentFetchedResultsController sections] objectAtIndex:section];
return [sectionInfo numberOfObjects];
}
I have tried nearly everything. I have gone through every related thread and nothing has worked. What am I doing wrong?
** Newest Findings:
When a cell is selected, the cell does not become highlighted (or show any UI change for that matter), unless it is the correct cell for that specific index path. For example, let's say Sally is in row 0 for all Users, and Tom is in row 1. If I switch the UISegmentedControl to "Male" and tap the first cell (row 0, which currently shows Sally), there is absolutely no UI indication that the cell has been tapped, although tableView: didSelectRowAtIndexPath still gets called, logging the cell information that belongs there (Tom's User info, since he belongs in row 0 of the "Male" Users).
It seems to me that it would be easier to put the segmentation logic into the fetched results controller method. When switching the segmented control, just set your FRC to nil and account for the proper filter in the FRC creation code. You do not need 3 FRCs. Thus:
-(void)segmentedControlDidChange:(UISegmentedControl*)control {
self.fetchedResultsController = nil;
[self.tableView reloadData];
}
and when creating the FRC:
NSPredicate *basePredicate = [NSPredicate predicateWithFormat:
#"school.schoolID = %#", _schoolID];
NSPredicate *secondPredicate = [NSPredicate predicateWithValue:YES];
NSInteger i = self.segmentedControl.selectedSegmentIndex;
if (i > 0) {
secondPredicate = [NSPredicate predicateWithFormat:
#"gender = %#", i == 1 ? #"M" : #"F"];
}
fetchRequest.predicate = [NSCompoundPredicate
andPredicateWithSubPredicates:#[basePredicate, secondPredicate]];
It turns out I made a silly mistake when initializing my UITableViewCell. Instead of calling
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
I was calling
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier **forIndexPath:indexPath**];
The latter returns the current UITableViewCell dequeued from the indexPath mentioned. For some reason, if the cell is dequeued from an existing cell, the properties on the cell are readonly, causing all of my changes to be simply ignored. I hope this helps someone in the future, as I did not see anything about this on Stack Overflow, I just happened to stumble upon my mistake after hours of analyzing each line of code.
Related
I have an array of dictionaries where I what to implement search functionality and display data on tableView cells.
Search works quite fine, I have tested it:
-(void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText {
NSString *dictionryKey = #"eViela";
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"%K CONTAINS [cd] %#", dictionryKey, searchText];
NSArray *filteredArray = [self.allItems filteredArrayUsingPredicate:predicate];
NSLog(#"%#", filteredArray);
self.sortedText = filteredArray;
[self.tableView reloadData];
}
Now I need tableView to fill data only for key "eViela" while user is typing info in search bar. As of now I can show only all items as per below code:
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"SearchCell" forIndexPath:indexPath];
EvielasLibrary *library = [[EvielasLibrary alloc] init];
cell.textLabel.text = [[library.library objectAtIndex:indexPath.row]
return cell;
}
I know that it should be easy but I can't figure it out.
Thanks for help!
You need to get Model from array instead of create new one .
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"SearchCell" forIndexPath:indexPath];
EvielasLibrary *library = [self.sortedText objectAtIndex:indexPath.row]; // assume that self.sortedText is your data source array of tableview
cell.textLabel.text = #"" // populate your string here
return cell;
}
NSMutableArray *arrayForTableView = [[NSMutableArray alloc] init];
NSString *dictionryKey = #"eViela";
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF contains[c] %#",dictionryKey];
NSArray *filteredArray = [self.allItems filteredArrayUsingPredicate:predicate];
for (NSInteger i = 0; i<filteredArray.count; i++){
NSString *matchValue = [NSString stringWithFormat:#"%#",[filteredArray objectAtIndex:i]];
if ([matchValue isEqualToString:dictionryKey]){
[arrayForTableView addObject:matchValue];
}
}
Use arrayForTableView array for your tableview
cell.textLabel.text = [arrayForTableView objectAtIndex:indexPath.row];
Problem is that you not updating numberOfRowsInSection delegate method for sortedArray
You need to check if search bar is active or not. if search bar is active then use sortedText array else if search bar is not active then use library.library array
For check search bar active create a global variable isSearchBarActive And assign it true value in searchBarTextDidBeginEditing delegate function and assign false in searchBarTextDidEndEditing
Add Condition in numberOfRowInSection
if searchBarActive {
return self.sortedText.count
}
else {
return library.library.count
}
Add Condition in cellForRowAtIndexPath like this
if searchBarActive {
cell.textLabel.text = [self.sortedText objectAtIndex:indexPath.row]
}
else {
cell.textLabel.text = [library.library objectAtIndex:indexPath.row]
}
i'm trying to do a weather application
#import "LocationTableViewController.h"
#interface LocationTableViewController (){
NSArray *hourlyData;
NSArray *dailyData;
}
#end
#implementation LocationTableViewController
- (void)viewDidLoad {
[super viewDidLoad];
NSString *str = #"https://api.forecast.io/forecast/cd8edc928426f2ac3e341441c7a9c6d3/37.8267,-122.423";
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:str]];
NSDictionary *dataFromWeb = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil];
i took a json query that gives hourly and daily data, then converted it into dictionary. Here i created two dictionaries ie hourly dictionary and daily dictionary which contains fields like temperature, humidity, etc etc
My main goal is to create a weather app using both of the dictionaries hourly and daily by loading them into a table view.
NSDictionary *hourlyDict = [dataFromWeb objectForKey:#"hourly"];
hourlyData = [hourlyDict objectForKey:#"data"];
NSDictionary *dailyDict = [dataFromWeb objectForKey:#"daily"];
dailyData = [dailyDict objectForKey:#"data"];
NSLog(#"%#", [[dailyData objectAtIndex:0] objectForKey:#"humidity"]);
}
By here i successfully created both the dictionaries and tried to NSlog the data it works fine.
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
I feel like my problem starts here in loading the dictionary into tableView.
1) In storyboard i embedded the table view into NavigationView.
2) i made the table View content as dynamic protocol
3) named the cell identifier as cell
i think problem starts here in sending data into table. Basically my app should contains 3 sections summary,hourly data and daily data. but i just want to try for now only on daily data.
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
Since i don't want any section, i removed this no.of. sections, but it threw me error, so kept it back and returned 0. , i also tried making it 1, but app crashes.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
NSLog(#"hello");
return dailyData.count;
}
Here i want columns as no.of rows in dictionary, so i made dailyData.count.
Here starts the main problem, this - (UITableViewCell *)tableView:........ function is not being called, i tried to NSlog a message, it didn't show up
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"cell" forIndexPath:indexPath];
NSLog(#"hello");
cell.textLabel.text = [[dailyData objectAtIndex:indexPath.row]objectForKey:#"sunsetTime"];
return cell;
}
can some one help me out please. Thanks in advance. Im new to programming.
here i attached the Google Drive link for project
To fix your issue you need to set your number of sections to 1 (you must always have at least 1 section for anything to display).
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
The reason you are getting a crash when you set your number of sections to 1 is because you are trying to use an NSDictionary as an NSString. You need to get an NSString from the NSDictionary. The below code will get the summary from the daily data for that row and display the summary.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"cell" forIndexPath:indexPath];
NSDictionary *rowData = [dailyData objectAtIndex:indexPath.row];
cell.textLabel.text = [rowData objectForKey:#"summary"];
return cell;
}
// sections should be 1 instead of 0
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
[dailyData objectAtIndex:indexPath.row] has following dictionary that cannot be assigned as Cell label text , you have to assign some string value to Cell Label text
{
apparentTemperatureMax = "60.49";
apparentTemperatureMaxTime = 1462316400;
apparentTemperatureMin = "52.96";
apparentTemperatureMinTime = 1462276800;
cloudCover = "0.92";
dewPoint = "50.37";
humidity = "0.8100000000000001";
icon = "partly-cloudy-day";
moonPhase = "0.89";
ozone = "351.23";
precipIntensity = 0;
precipIntensityMax = 0;
precipProbability = 0;
pressure = "1014.68";
summary = "Mostly cloudy throughout the day.";
sunriseTime = 1462281126;
sunsetTime = 1462331002;
temperatureMax = "60.49";
temperatureMaxTime = 1462316400;
temperatureMin = "52.96";
temperatureMinTime = 1462276800;
time = 1462258800;
visibility = "8.779999999999999";
windBearing = 275;
windSpeed = "5.83";
}
cell.textLabel.text = [dailyData objectAtIndex:indexPath.row]; you are assigning a dictionary to tableViewCell label. if you want specific key value then check below code
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"cell" forIndexPath:indexPath];
//Number to Sting --> [Number stringValue]
cell.textLabel.text = [[[dailyData objectAtIndex:indexPath.row]objectForKey:#"sunsetTime"] stringValue];
return cell;
}
I was trying to pass float into my table cell text, theres the mistake. We have to pass only text into that one, but i was trying to insert float values. now i passes some text values it works fine
Thank you all for your lovable support. Thank you once again
use this below code, And your code is working for me,
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
NSLog(#"hello");
return dailyData.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"cell" forIndexPath:indexPath];
NSLog(#"hello %#",[[dailyData objectAtIndex:indexPath.row] objectForKey:#"humidity"]);
cell.textLabel.text = [NSString stringWithFormat:#"%#",[[dailyData objectAtIndex:indexPath.row] objectForKey:#"humidity"]];
return cell;
}
And your output is given below,
humidity value is printed
hope its helpful
You must show not to come out the data, because your dailyData is a NSArray! How do you use an array as a string to use?Suggest you use a model, to store data.
NSDictionary *hourlyDict = [dataFromWeb objectForKey:#"hourly"];
hourlyData = [hourlyDict objectForKey:#"data"];
NSDictionary *dailyDict = [dataFromWeb objectForKey:#"daily"];
dailyData = [dailyDict objectForKey:#"data"];
for (id obj in hourlyData) {
newsModel *model=[[newsModel alloc]init ];
model.name=obj[#"summary"];
[_dataArray addObject:model];
}
NSLog(#"%#", [[dailyData objectAtIndex:0] objectForKey:#"humidity"]);
In the table view delegate :
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString *cellIde=#"cell";
UITableViewCell *cell=[tableView dequeueReusableCellWithIdentifier:cellIde];
if (!cell) {
cell=[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIde ];
}
newsModel *model=_dataArray[indexPath.row];
cell.textLabel.text=model.name;
return cell;
}
I have created a UITableView with custom cell & stored name,no,pincode in to these cell.
Here is my Code for array:-
for (int i =0; i<[tempArr count]; i++)
{
NSString *rawData = [tempArr objectAtIndex:i];
if (rawData !=nil)
{
Persons *newPerson = [[Persons alloc]init];
NSArray *data = [rawData componentsSeparatedByString:#"\t"];
newPerson.name = [NSString stringWithFormat:#"%#",[data objectAtIndex:0]];
newPerson.no = [[data objectAtIndex:1] integerValue];
newPerson.pincode = [[data objectAtIndex:2] integerValue];
[allPersons addObject:newPerson];
}
}
Here is my Customcell.h
#interface Customcell : UITableViewCell
#property(weak) Persons* person;
#end
UITableView Datasrouce method:-
-(UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
Customcell *cell = [tblStations dequeueReusableCellWithIdentifier:#"personCell"];
if (tableView == self.searchDisplayController.searchResultsTableView)
{
cell.person = filteredContentList[indexPath.row];
[cell.textLabel setText:cell.person.name];
}
else
{
cell.person = allPersons[indexPath.row];
[cell.textLabel setText:cell.person.name];
}
return cell;
}
How do i create Section & index list for all names from A to Z & give title by cell.textLabel.text?
I am following This Tutorial but it has static keys & names added to NSDictionary,NSArray.
In my example i do not know how many names starting with same letter can come in the array. i am also using UISearchDisplayController for search person name.
I want to add number of sections & title for those sections by names that is in the array or cell.textLabel.text dynamically.
i do not know about UISearchDisplayController that these sections & index list will be displaying in UISearchDisplayController so i do not want these sections & index list while searching.
You need to spend a little more time trying to make your questions more clear.
Include a custom implementation of the necessary UITableView data source and delegate methods...
NOTE my assumption that your variable allPersons is an NSMutableArray.
NOTE these do not include for your search results data sets!
Return an NSInteger for the number of sections in your UITableView...
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
NSSet *setData = nil;
NSInteger integerData = 0;
setData = [NSSet setWithArray:allPersons];
integerData = [setData count];
return integerData;
}
UPDATE
Return an NSString for section header titles...
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
NSOrderedSet *setData = nil;
NSString *stringData = nil;
setData = [NSOrderedSet orderedSetWithArray:allPersons];
stringData = [[setData allObjects] componentsJoinedByString:#" "];
return stringData;
}
...plus others if I have the time...
I have custom contact book sorted by A-Z sections. I am trying to add to an array selected contacts
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSMutableDictionary *contactInfo = [NSMutableDictionary new];
Cell *cell = (Cell *)[self.contTableView cellForRowAtIndexPath:indexPath];
//NSLog(#"CELL %#", cell.contact.fullname);
if (!cell.contact.contactChecked) {
cell.contactImage.image = [UIImage imageNamed:#"cell_blue_circle.png"];
cell.contact.contactChecked = YES;
//NSLog(#"DID SELECT %#", cell.contact.fullname);
NSLog(#"index checked row %d section %d", indexPath.row, indexPath.section);
[contactInfo setValue:cell.contact.fullname forKey:#"name"];
[contactInfo setValue:cell.contact.numbers.firstObject forKey:#"phone"];
[self.seletedPeople insertObject:contactInfo atIndex:indexPath.row];
} else {
NSLog(#"index unchecked row %d section %d", indexPath.row, indexPath.section);
cell.contactImage.image = [UIImage imageNamed:#"cell_gray_circle.png"];
cell.contact.contactChecked = NO;
[self.seletedPeople removeObjectAtIndex:indexPath.row];
}
NSLog(#"DICT SELECTED %#", self.seletedPeople);
}
What happens, that in some cell app crashing with error
* Terminating app due to uncaught exception 'NSRangeException', reason: '* -[__NSArrayM insertObject:atIndex:]: index 1 beyond
bounds for empty array'
*** First throw call stack: (0x29c02fef 0x38150c8b 0x29b1cf8f 0xf7fe9 0x2d36e56b 0x2d41d43b 0x2d2d2a91 0x2d24d38f 0x29bc8fed 0x29bc66ab
0x29bc6ab3 0x29b13201 0x29b13013 0x313f2201 0x2d2b7a59 0x10c075
0x386dcaaf) libc++abi.dylib: terminating with uncaught exception of
type NSException
UPDATE:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellID = #"Cell";
Cell *cell = [tableView dequeueReusableCellWithIdentifier:cellID forIndexPath:indexPath];
if (searchResults) {
//NSLog(#"CELL %#", cell.contact.fullname);
contact = [searchResults objectAtIndex:indexPath.row];
cell.contact = contact;
cell.firstNameLabel.text = contact.fullname;
cell.avatar.image = contact.image;
cell.avatar.layer.borderColor = [UIColor grayColor].CGColor;
cell.avatar.layer.borderWidth = 0.5;
cell.avatar.layer.cornerRadius = 25.0;
cell.avatar.layer.masksToBounds = YES;
cell.number.text = contact.numbers.firstObject;
} else {
NSString *sectionTitle = [[[namesDictionary allKeys] sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)]
objectAtIndex:indexPath.section];
NSArray *sectionContacts = [namesDictionary objectForKey:sectionTitle];
contact = [self getContactFromArray:[sectionContacts objectAtIndex:indexPath.row]];
cell.firstNameLabel.text = [sectionContacts objectAtIndex:indexPath.row];
cell.avatar.image = contact.image;
cell.avatar.layer.borderColor = [UIColor grayColor].CGColor;
cell.avatar.layer.borderWidth = 0.5;
cell.avatar.layer.cornerRadius = 25.0;
cell.avatar.layer.masksToBounds = YES;
cell.number.text = contact.numbers.firstObject;
cell.contact = contact;
cell.tag = indexPath.row;
}
if (contact.contactChecked) {
cell.contactImage.image = [UIImage imageNamed:#"cell_blue_circle.png"];
} else {
cell.contactImage.image = [UIImage imageNamed:#"cell_gray_circle.png"];
}
return cell;
}
The way I use in such cases. I create a model class and load the tableview with models. Now when i select a cell or deselect a cell. I just add that model in another array. After that when i de select the already selected cell, i can get the same model from the indexpath.row and then i can use NSArray method to fetch that model in that selected array and remove it from there. To fix your issue you can use indexPath.row as another key in dictionary during selection. After that when you deselect the cell use a predicate to get the added dictionary from the array that you are using to store selected ones. Once you find it delete it from the array.
the problem here is the coupling of model with view objects , you shouldn't inquire about a certain property from the view itself (in your case the Cell) however the contact checked should have a reflect on its model from the data source object (the one you used to feed the cellForRowAtIndexPath: , where it should be inquired from.
Otherwise the code is buggy and unstable due to that coupling since it might point to an empty object
The problem is here:
[self.seletedPeople insertObject:contactInfo atIndex:indexPath.row];
if selectedPeople is empty and the user clicks on row 2, then it's going to try to insert contactInfo into row 2 which is "beyond the bounds of an empty array". Simply use addObject: instead. You'll also need to change how you remove items from that array then (probably better to use a dictionary instead).
The solution was adding contact record id to my dictionary and search with predicate this contact id. then remove it. kudos to #jassi
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSMutableDictionary *contactInfo = [NSMutableDictionary new];
Cell *cell = (Cell *)[tableView cellForRowAtIndexPath:indexPath];
if (!cell.contact.contactChecked) {
cell.contactImage.image = [UIImage imageNamed:#"cell_blue_circle.png"];
cell.contact.contactChecked = YES;
//NSLog(#"DID SELECT %#", cell.contact.fullname);
NSLog(#"index checked %# ", [indexPath description]);
[contactInfo setValue:cell.contact.fullname forKey:#"name"];
[contactInfo setValue:cell.contact.numbers.firstObject forKey:#"phone"];
[contactInfo setValue:#(cell.contact.contactId) forKey:#"contactId"];
[self.seletedPeople addObject:contactInfo];
} else {
NSLog(#"index unchecked %#", [indexPath description]);
cell.contactImage.image = [UIImage imageNamed:#"cell_gray_circle.png"];
cell.contact.contactChecked = NO;
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"contactId == %d", cell.contact.contactId];
NSArray *resultTemp = [self.seletedPeople filteredArrayUsingPredicate:predicate];
if(resultTemp.count>0)
[self.seletedPeople removeObject:resultTemp[0]];
}
NSLog(#"DICT SELECTED %#", self.seletedPeople);
}
As user2320861 said, the problem is on the line where you use insertObject. I would do the following:
Change self.selectedPeople to a NSMutableDictionary using the following code:
//in your #interface
#property (nonatomic, strong) NSMutableDictionary *selectedPeople;
Change the code in didSelectCellAtIndexPath to:
//Since phone numbers are unique.
self.selectedPeople[cell.contact.numbers.firstObject] = contactInfo;
Retrieve all of the contacts later using this code:
for(id key in self.selectedPeople) {
NSDictionary contactInfo = [self.selectedPeople objectForKey:key];
//Do something with that contactInfo
}
I know the question of two UITableViews on one UIViewController is a common one but I have not found a solution to one being dependant on the choice of another.
I have a UIViewController with two UITableViews. Table 1 is populated from a CoreData entity with a distinct list of names. When I choose a name from Table 1 I would like Table 2 to be populated with all records related to that person.
The following code works in that Table 1 is populated correctly. However when I choose a name from Table 1 the array based on the selection (which is correct) goes into Table 1 and not Table 2.
I also realise that were a second name to be chosen from Table 1 it will not quite work since it does not distinguish which table has been chosen. Any suggestions here welcome too. I have read that tagging tables is the answer but I have had little success.
Many thanks in advance.
Can anyone help me
- (void)viewDidLoad
{
tableLoad=1;
[tableView setDelegate: self];
[tableView setDataSource: self];
.... code for populating teacherNames with names from Core data
[self.tableView reloadData];
}
-(void) loadSecondTable;
{
[observationTableView setDelegate: self];
[observationTableView setDataSource: self];
tableLoad=2;
[self.tableView reloadData];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (tableLoad==1)
{
return self.teacherNames.count;
}
else
{
return self.observationNames.count;
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (tableLoad == 1)
{
static NSString *CellIdentifier = #"teacherCell";
UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
NSString *currentList = [[self.teacherNames objectAtIndex:indexPath.row] objectForKey:#"obsTeacherName"];
cell.textLabel.text = currentList;
return cell;
}
else
{
static NSString *CellIdentifier = #"observationCell";
UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
Observations *currentList = [self.observationNames objectAtIndex:indexPath.row];
cell.textLabel.text = [NSString stringWithFormat:#"Class %# - %# - %#", currentList.obsClassName, currentList.obsDate, currentList.obsDateTimeStart];
return cell;
}
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if(tableLoad==1)
{
teacherChosenFromTable = [[self.teacherNames objectAtIndex:indexPath.row] objectForKey:#"obsTeacherName"];
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:#"Observations"];
fetchRequest.sortDescriptors = [NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:#"obsTeacherName" ascending:YES]];
NSPredicate *predicate = [NSPredicate predicateWithFormat:[NSString stringWithFormat:#"obsTeacherName == '%#'", teacherChosenFromTable]];
[fetchRequest setPredicate:predicate];
NSError *error = nil;
self.observationNames = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
[self loadSecondTable];
}
else
{
.... load next view based on selection of Table 2
}
}
You don't control the redrawing of tableViews when you scroll it the IOS framework call the DataSource when they need it. The algorithme to populate the data must take into account that. You need to verify which tableView call the delegate.
Try this :
- (void)viewDidLoad
{
[tableView setDelegate: self];
[tableView setDataSource: self];
[observationTableView setDelegate: self];
[observationTableView setDataSource: self];
.... code for populating teacherNames with names from Core data
[self.tableView reloadData];
}
//-(void) loadSecondTable;
//{
// [observationTableView setDelegate: self];
// [observationTableView setDataSource: self];
// [self.tableView reloadData];
//}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
//Caution you name your first tableView tableview and mask the parameter methode you need to rename it
- (NSInteger)tableView:(UITableView *)p_TableView numberOfRowsInSection:(NSInteger)section
{
if (p_tableView == tableView )
{
return self.teacherNames.count;
}
else
{
return self.observationNames.count;
}
}
- (UITableViewCell *)tableView:(UITableView *)p_TableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (p_TableView == tableView )
{
static NSString *CellIdentifier = #"teacherCell";
UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
NSString *currentList = [[self.teacherNames objectAtIndex:indexPath.row] objectForKey:#"obsTeacherName"];
cell.textLabel.text = currentList;
return cell;
}
else
{
static NSString *CellIdentifier = #"observationCell";
UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
Observations *currentList = [self.observationNames objectAtIndex:indexPath.row];
cell.textLabel.text = [NSString stringWithFormat:#"Class %# - %# - %#", currentList.obsClassName, currentList.obsDate, currentList.obsDateTimeStart];
return cell;
}
}
- (void)tableView:(UITableView *)p_TableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if (p_TableView == tableView )
{
teacherChosenFromTable = [[self.teacherNames objectAtIndex:indexPath.row] objectForKey:#"obsTeacherName"];
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:#"Observations"];
fetchRequest.sortDescriptors = [NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:#"obsTeacherName" ascending:YES]];
NSPredicate *predicate = [NSPredicate predicateWithFormat:[NSString stringWithFormat:#"obsTeacherName == '%#'", teacherChosenFromTable]];
[fetchRequest setPredicate:predicate];
NSError *error = nil;
self.observationNames = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
[self.tableView reloadData];
}
else
{
.... load next view based on selection of Table 2
}
}
Don't do tagging. The methods are called on the same thread but there's no way in general to control the state of tableLoad correctly. All datasource methods have a tableView as an argument, compare the argument value with the values of linked outlets or variables where you should save the references to your table views after they are initialized.
Distinguish the table view where the cell is selected in the same way.