I am trying display cells in a UITableView but newly added cells are not displayed unless viewDidLoad is used.
This is the full list:
If I go to the New Note screen and come back this is fine but if this screen is accessed by the side menu the note title New disappears until I click on the New Note button and come back.
Below is the code for this page:
- (void)viewDidLoad {
[super viewDidLoad];
NSMutableArray *leftButtonsArray = [self.navigationItem.leftBarButtonItems mutableCopy];
UIBarButtonItem *newNoteButton = [[UIBarButtonItem alloc] initWithTitle:#"New Note" style:UIBarButtonItemStyleBordered target:self action:#selector(addNote:)];
[leftButtonsArray addObject:newNoteButton];
self.navigationItem.leftBarButtonItems = leftButtonsArray;
}
-(void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
self.notes = [NSMutableArray array];
ontracAppDelegate *delegate = (ontracAppDelegate*)[[UIApplication sharedApplication] delegate];
NSManagedObjectContext *moc = [delegate managedObjectContext];
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:#"Notes"];
[request setReturnsObjectsAsFaults:NO];
NSError *error = nil;
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"updated" ascending:NO];
[request setSortDescriptors:#[sortDescriptor]];
NSArray *results = [moc executeFetchRequest:request error:&error];
NSSortDescriptor *sd = [[NSSortDescriptor alloc] initWithKey:#"updated" ascending:NO];
NSArray *sortDescriptors = [NSArray arrayWithObject:sd];
NSArray *sortedArray = [results sortedArrayUsingDescriptors:sortDescriptors];
NSNumber * NOTEID = #"noteID";
NSString * PACKID = #"pack_id";
NSString * NOTE = #"note";
NSString * CREATED = #"created";
NSString * UPDATED = #"updated";
NSDictionary * dict;
[self.notes removeAllObjects];
for(Notes *note in results){
if(self.dataObject.pack_id == [note.dataObject.dataPack.pack_id integerValue]){
dict = [NSDictionary dictionaryWithObjectsAndKeys:
note.noteID, NOTEID,
note.pack_id, PACKID,
note.note, NOTE,
note.created, CREATED,
note.updated, UPDATED,
nil];
if((note.created == nil || note.updated == nil || note.noteID == nil)){
[moc deleteObject:note];
}else{
[self.notes addObject:dict];
}
}
}
NSLog(#"mArray %#", self.notes);
}
-(void)viewDidDisappear:(BOOL)animated{
[super viewDidDisappear:animated];
}
-(void)viewDidAppear:(BOOL)animated{
[super viewDidAppear:animated];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection: (NSInteger)section
{
NSLog(#"notes count: %lu", (unsigned long)[self.notes count]);
return [self.notes count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:#"myCell"];
if (!cell)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:#"myCell"];
}
NSMutableDictionary *d = [self.notes objectAtIndex:indexPath.row];
//NSNumber *noteID = [d valueForKey:#"noteID"];
NSString *note = [d valueForKey:#"note"];
NSString *created = [d valueForKey:#"created"];
NSString *updated = [d valueForKey:#"updated"];
cell.textLabel.text = note;
cell.detailTextLabel.text = [NSString stringWithFormat:#"Date Created: %# | Last Updated %#", created, updated];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSMutableDictionary *d = [self.notes objectAtIndex:indexPath.row];
NSNumber *noteID = [d valueForKey:#"noteID"];
NID = noteID;
ontracSuperClassViewController *viewController = [[ontracNotesViewController alloc] init];
[self.navigationController pushViewController:viewController animated:YES];
}
-(IBAction)addNote:(id)sender{
NID = [NSNumber numberWithInteger:-1];
ontracSuperClassViewController *viewController = [[ontracNotesViewController alloc] init];
[self.navigationController pushViewController:viewController animated:YES];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
When you fill your array with latest updated data then you have to reload tableview to display that latest data. So, if your self.notes prints updated data properly in viewWillAppear in NSLog then you just need to reload your tableview like,
[tableView reloadData];
and if you are not getting updated data in self.notes then first solve that and then reload tableview!
whenever you add new note in that function after adding new node just reload the tableview
add this line in your viewDidLoad() method
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(self.Methodtablereloadata(_:)), name:"NotificationIdentifier", object: nil)
-(void) Methodtablereloadata(notification: NSNotification){
//Take Action on Notification
self.notes = [NSMutableArray array];
ontracAppDelegate *delegate = (ontracAppDelegate*)[[UIApplication sharedApplication] delegate];
NSManagedObjectContext *moc = [delegate managedObjectContext];
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:#"Notes"];
[request setReturnsObjectsAsFaults:NO];
NSError *error = nil;
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"updated" ascending:NO];
[request setSortDescriptors:#[sortDescriptor]];
NSArray *results = [moc executeFetchRequest:request error:&error];
NSSortDescriptor *sd = [[NSSortDescriptor alloc] initWithKey:#"updated" ascending:NO];
NSArray *sortDescriptors = [NSArray arrayWithObject:sd];
NSArray *sortedArray = [results sortedArrayUsingDescriptors:sortDescriptors];
NSNumber * NOTEID = #"noteID";
NSString * PACKID = #"pack_id";
NSString * NOTE = #"note";
NSString * CREATED = #"created";
NSString * UPDATED = #"updated";
NSDictionary * dict;
[self.notes removeAllObjects];
for(Notes *note in results){
if(self.dataObject.pack_id == [note.dataObject.dataPack.pack_id integerValue]){
dict = [NSDictionary dictionaryWithObjectsAndKeys:
note.noteID, NOTEID,
note.pack_id, PACKID,
note.note, NOTE,
note.created, CREATED,
note.updated, UPDATED,
nil];
if((note.created == nil || note.updated == nil || note.noteID == nil)){
[moc deleteObject:note];
}else{
[self.notes addObject:dict];
}
}
}
[YOURTABLE reloadData];
}
your next viewcontoller that you add new notes . after add notes just write one line for reload table
NSNotificationCenter.defaultCenter().postNotificationName("NotificationIdentifier", object: nil)
Refer this link
https://developer.apple.com/reference/foundation/nsnotificationcenter
Related
I am working on XMPP project. i have successfully completed login process and online ofline rosters. but now i dont know how to go to next view controller with particular user's field and chat with him. here is my try.
Now what i have to write in UITableview's Delegate Method
friendsviewcontroller.m file // Fetch online and offline rosterlist
#pragma mark -Tableview datasource method
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return [[[self fetchedResultsController] sections] count];
}
- (CGFloat)tableView:(UITableView *)aTableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
switch (indexPath.row){
case 0:
if(indexPath.section==0)
return 60.0; // first row is 123pt high
default:
return 60.0; // all other rows are 40pt high
}
}
- (NSString *)tableView:(UITableView *)sender titleForHeaderInSection:(NSInteger)sectionIndex
{
NSArray *sections = [[self fetchedResultsController] sections];
if (sectionIndex < [sections count])
{
id <NSFetchedResultsSectionInfo> sectionInfo = [sections objectAtIndex:sectionIndex];
int section = [sectionInfo.name intValue];
switch (section)
{
case 0 : return #"Available";
case 1 : return #"Away";
default : return #"Offline";
}
}
return #"";}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)sectionIndex{
NSArray *sections = [[self fetchedResultsController] sections];
if (sectionIndex < [sections count])
{
id <NSFetchedResultsSectionInfo> sectionInfo = sections[sectionIndex];
return sectionInfo.numberOfObjects;
}
return 0;}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *simpleTableIdentifier = #"SimpleTableItem";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if( cell == nil){
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:simpleTableIdentifier];
}
XMPPUserCoreDataStorageObject *user = [[self fetchedResultsController] objectAtIndexPath:indexPath];
cell.textLabel.text = user.displayName;
[self configurePhotoForCell:cell user:user];
// cell.detailTextLabel.text= [self.tblchathistory objectAtIndex:indexPath.row];
cell.textLabel.textColor=[UIColor whiteColor];
return cell;
}
#pragma Mark - segue method
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"chathistory"])
{
CreateGroupViewController *vc = [segue destinationViewController];
}
}
- (BOOL)xmppStream:(XMPPStream *)sender didReceiveIQ:(XMPPIQ *)iq
{
NSXMLElement *queryElement = [iq elementForName: #"query" xmlns: #"jabber:iq:roster"];
if (queryElement)
{
NSArray *itemElements = [queryElement elementsForName: #"item"];
for (int i=0; i<[itemElements count]; i++)
{
NSLog(#"Friend: %#",[[itemElements[i] attributeForName:#"jid"]stringValue]);
}
}
return NO;
}
- (NSFetchedResultsController *)fetchedResultsController
{
if (fetchedResultsController == nil)
{
NSManagedObjectContext *moc = [[self appDelegate] managedObjectContext_roster];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"XMPPUserCoreDataStorageObject"
inManagedObjectContext:moc];
NSSortDescriptor *sd1 = [[NSSortDescriptor alloc] initWithKey:#"sectionNum" ascending:YES];
NSSortDescriptor *sd2 = [[NSSortDescriptor alloc] initWithKey:#"displayName" ascending:YES];
NSArray *sortDescriptors = [NSArray arrayWithObjects:sd1, sd2, nil];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
[fetchRequest setEntity:entity];
[fetchRequest setSortDescriptors:sortDescriptors];
[fetchRequest setFetchBatchSize:10];
fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:moc
sectionNameKeyPath:#"sectionNum"
cacheName:nil];
[fetchedResultsController setDelegate:self];
NSError *error = nil;
if (![fetchedResultsController performFetch:&error])
{
DDLogError(#"Error performing fetch:= %#", error);
//NSLog(#"error = %#", error);
}
}
return fetchedResultsController;
}
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
{
[[self tblvwbuddy] reloadData];
}
You can fetch the user by following way
While you are showing in one screen that means you have the jid of the user, so take that user's jid and in next controller you can filter the user.
Here is my code to filter the porticular user from XMPPUserCoreDataStorageObject by means of jid. In my case friendJid is the jid to be filtered
- (XMPPUserCoreDataStorageObject *)fetchTheUser
{
NSManagedObjectContext *moc = [APP_DELEGATE managedObjectContext_roster];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"XMPPUserCoreDataStorageObject"
inManagedObjectContext:moc];
NSPredicate *pred = [NSPredicate predicateWithFormat:#"jidStr=%#",[self.friendJid lowercaseString]];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
[fetchRequest setEntity:entity];
[fetchRequest setPredicate:pred];
NSError *error = nil;
[fetchRequest setPredicate:pred];
NSArray *fetchedObjects = [[APP_DELEGATE managedObjectContext_roster] executeFetchRequest:fetchRequest error:&error];
XMPPUserCoreDataStorageObject *objTemp = fetchedObjects.count?[fetchedObjects objectAtIndex:0]:nil;
return objTemp;
}
Hope it will help you.
accept the answer if you find this useful
I'm attempting to build a game scoring app that utilizes a custom table cell with player photos, names, buttons etc... There are add/subtract buttons directly in the custom cell of the tableview that are hitting my save method, and it's storing it back in Core Data for that specific user.
The problem is with the on-screen score not updating and reflecting the change. After the save action to Core Data is complete, I'm calling the [self.tableView reloadData];... nothing. However, if I restart the app, then the change in score (for any of the players I've clicked on), appears.
Maybe I'm making this harder than it needs to be, either that, or I'm just not grasping the real problem.
Thoughts / comments?
Thanks a load in advance.
:-)
Sorry if this is overkill, but here is the majority of my implementation file:
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self resetViews];
}
- (void)viewDidLoad {
[super viewDidLoad];
AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context = [appDelegate managedObjectContext];
[context setUndoManager:nil];
_managedObjectContext = context;
self.tableView.delegate = self;
[self setNeedsStatusBarAppearanceUpdate];
}
-(void)resetViews {
NSLog(#"\n\n\nresetViews()");
[self setupFetchedResultsController];
[self.tableView reloadData];
[self.view setNeedsDisplay];
}
- (void)setupFetchedResultsController {
NSString *entityName = #"Players";
NSLog(#"Setting up a Fetched Results Controller for the Entity named %#", entityName);
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:entityName];
request.sortDescriptors = [NSArray arrayWithObject:
[NSSortDescriptor
sortDescriptorWithKey:#"playerName"
ascending:YES
selector:#selector(localizedCaseInsensitiveCompare:)]];
self.fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:request
managedObjectContext:self.managedObjectContext
sectionNameKeyPath:nil
cacheName:nil];
NSError *error;
NSArray *results = [_managedObjectContext executeFetchRequest:request error:&error];
_playerArray = [[NSMutableArray alloc]initWithArray:results];
NSLog(#"_playerArray count: %i", [_playerArray count]);
NSLog(#"\n");
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return _playerArray.count;
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellIdentifier = #"playerCell";
ScoringCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
// Configure the cell...
Players *player_info = [_playerArray objectAtIndex:indexPath.row];
NSSet *score = player_info.scores;
for (Scoring *perObj in score){
cell.lblPlayerScore.text = [perObj.score stringValue];
NSLog(#"\n\n\n score for %#: %#", player_info.playerName, perObj.score);
}
cell.lblPlayerName.text = player_info.playerName;
cell.lblPlayerNickName.text = player_info.playerNickName;
cell.btnIncreaseScore.tag = indexPath.row;
cell.btnDecreaseScore.tag = indexPath.row;
cell.imgPlayerPhoto.image = [UIImage imageNamed:#"tmp_playerImage"];
return cell;
}
- (IBAction)increaseScore:(id)sender {
NSLog(#"PageContentViewController: increaseScore()");
UIButton* btn=(UIButton*)sender;
int selectedPlayerInt = btn.tag;
//NSLog(#"Selected row is: %d",btn.tag);
Players *player_info = [_playerArray objectAtIndex:selectedPlayerInt];
[self updateRowScore:player_info:#"add"];
}
- (IBAction)decreaseScore:(id)sender {
NSLog(#"PageContentView: decreaseScore()");
UIButton* btn=(UIButton*)sender;
int selectedPlayerInt = btn.tag;
//NSLog(#"Selected row is: %d",btn.tag);
Players *player_info = [_playerArray objectAtIndex:selectedPlayerInt];
[self updateRowScore:player_info:#"subtract"];
}
-(void)updateRowScore: (Players *)player_info :(NSString *)modifier {
NSLog(#"\n\nupdateRowScore()");
NSLog(#"Update score (%#) for: %#\n", modifier, player_info.playerName);
NSArray *scoreDataArray;
if ([self playerScoreCount:player_info] == 0) {
// NEW score... we've never scored before.
Scoring *scoring_data = [NSEntityDescription
insertNewObjectForEntityForName:#"Scoring"
inManagedObjectContext:_managedObjectContext];
//Since this is the first score, always set it to 1
scoring_data.score = [NSNumber numberWithInt:1];
scoring_data.holeNumber = [NSNumber numberWithInt:_pageIndex];
scoring_data.scoredBy = player_info;
} else {
//Update existing player score..
NSError *error = nil;
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *BEntity = [NSEntityDescription entityForName:#"Scoring" inManagedObjectContext:_managedObjectContext];
[fetchRequest setEntity:BEntity];
NSPredicate *predicate = [NSPredicate
predicateWithFormat:#"(scoredBy = %#)", [player_info objectID]];
[fetchRequest setPredicate:predicate];
NSArray *results = [_managedObjectContext executeFetchRequest:fetchRequest error:&error];
scoreDataArray = [[NSMutableArray alloc]initWithArray:results];
Scoring *score_update = [scoreDataArray objectAtIndex:0];
int currentScore = [score_update.score intValue];
NSLog(#"current score: %d", currentScore);
if ([modifier isEqual: #"add"]) {
currentScore++;
} else {
// Don't allow negative scores.
if (currentScore >= 1) {
currentScore--;
} else {
currentScore = 0;
}
}
NSLog(#"NEW score: %d", currentScore);
score_update.score = [NSNumber numberWithInt:currentScore];
}
// write to database
[self.managedObjectContext save:nil];
[self resetViews];
}
UPDATE:
Thanks for the tip bbarnhart... I had read through that post before and had used that for a basis from which I had started. Decided to take it a step further and refactor a chunk of code using more of the Ray Wenderlich example.
I've seen some improvements to what's being recorded, and reported back through the NSLog's... but the view just still is not changing.
The action is increasing the score, and then I'm resetting the cell using [self configureCell:cell atIndexPath:path]; In there... the method that is responsible for sending text to the display... the NSLog is showing 2014-12-04 22:40:40.199 appName[7153:150248] Score for Tim: 4 when the display still only shows 3.
I know this is some stupid rookie move... I'm just doing something dead wrong that I can't figure out. Here's a snippet of the amended code.
- (NSFetchedResultsController *)fetchedResultsController {
if (_fetchedResultsController != nil) {
return _fetchedResultsController;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription
entityForName:#"Players"
inManagedObjectContext:_managedObjectContext];
[fetchRequest setEntity:entity];
NSSortDescriptor *sort = [[NSSortDescriptor alloc]
initWithKey:#"playerName" ascending:YES];
[fetchRequest setSortDescriptors:[NSArray arrayWithObject:sort]];
NSFetchedResultsController *theFetchedResultsController =
[[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:_managedObjectContext
sectionNameKeyPath:nil
cacheName:#"Root"];
self.fetchedResultsController = theFetchedResultsController;
_fetchedResultsController.delegate = self;
NSError *error;
NSArray *results = [_managedObjectContext executeFetchRequest:fetchRequest error:&error];
_playerArray = [[NSMutableArray alloc]initWithArray:results];
NSLog(#"_playerArray count: %i", [_playerArray count]);
return _fetchedResultsController;
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
id sectionInfo = [[_fetchedResultsController sections] objectAtIndex:section];
return [sectionInfo numberOfObjects];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellIdentifier = #"playerCell";
ScoringCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (!cell) {
cell = [[ScoringCell alloc] initWithStyle:UITableViewCellStyleSubtitle
reuseIdentifier:cellIdentifier];
}
[self configureCell:cell atIndexPath:indexPath];
return cell;
}
- (void)configureCell:(ScoringCell *)cell atIndexPath:(NSIndexPath *)indexPath {
Players *player_info = [_fetchedResultsController objectAtIndexPath:indexPath];
NSSet *scoreSet = player_info.scores;
NSString *cell_score;
for (Scoring *scoreObj in scoreSet) {
cell_score = [scoreObj.score stringValue];
}
NSLog(#"Score for %#: %#", player_info.playerName, cell_score);
if (cell_score != nil) {
cell.lblPlayerScore.text = cell_score;
}
cell.lblPlayerName.text = player_info.playerName;
cell.lblPlayerNickName.text = player_info.playerNickName;
cell.btnIncreaseScore.tag = indexPath.row;
cell.btnDecreaseScore.tag = indexPath.row;
cell.imgPlayerPhoto.image = [UIImage imageNamed:#"demo_playerb"];
[self resetViews];
NSLog(#"\n");
}
- (IBAction)increaseScore:(id)sender {
NSLog(#"PageContentViewController: increaseScore()");
UIButton *senderButton = (UIButton *)sender;
int selectedPlayerInt = senderButton.tag;
NSIndexPath *path = [NSIndexPath indexPathForRow:senderButton.tag inSection:0];
Players *player_info = [_playerArray objectAtIndex:selectedPlayerInt];
[self updateRowScore:player_info:#"add":selectedPlayerInt:path];
}
-(void)updateRowScore:(Players *)player_info :(NSString *)modifier :(int)selectedPlayerInt :(NSIndexPath *)path {
NSArray *scoreDataArray;
if ([self playerScoreCount:player_info] == 0) {
// NEW score... we've never scored before.
Scoring *scoring_data = [NSEntityDescription
insertNewObjectForEntityForName:#"Scoring"
inManagedObjectContext:_managedObjectContext];
//Since this is the first score, always set it to 1
scoring_data.score = [NSNumber numberWithInt:1];
scoring_data.holeNumber = [NSNumber numberWithInt:_pageIndex];
scoring_data.scoredBy = player_info;
} else {
//Update existing player score..
NSError *error = nil;
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *BEntity = [NSEntityDescription entityForName:#"Scoring"
inManagedObjectContext:_managedObjectContext];
[fetchRequest setEntity:BEntity];
NSPredicate *predicate = [NSPredicate
predicateWithFormat:#"(scoredBy = %#)", [player_info objectID]];
[fetchRequest setPredicate:predicate];
NSArray *results = [_managedObjectContext executeFetchRequest:fetchRequest error:&error];
scoreDataArray = [[NSMutableArray alloc]initWithArray:results];
Scoring *score_update = [scoreDataArray objectAtIndex:0];
int currentScore = [score_update.score intValue];
NSLog(#"current score: %d", currentScore);
if ([modifier isEqual: #"add"]) {
currentScore++;
} else {
// Don't allow negative scores.
if (currentScore >= 1) {
currentScore--;
} else {
currentScore = 0;
}
}
NSLog(#"NEW score: %d", currentScore);
score_update.score = [NSNumber numberWithInt:currentScore];
}
// write to database
[self.managedObjectContext save:nil];
static NSString *cellIdentifier = #"playerCell";
ScoringCell *cell = [_tableView dequeueReusableCellWithIdentifier:cellIdentifier];
[self configureCell:cell atIndexPath:path];
[self resetViews];
}
----------
UPDATE:
Been awhile since I've had a chance to revisit, and just noticed a new problem since enabling your tips. When scrolling down or up in the list and pulling beyond the normal boundaries, the tableview data seems to overwrite the display for the row either above or below the current line. Weird... Not sure if this animated Gif will show up in Stack. Here's an example:
The main reason your table view is not updating dynamically is NSFetchedResultsController uses a delegate for notification when changes occur. You'll need to set that delegate, self.fetchedResultsController.delegate = self and then add the delegate methods.
Here is a link to an example for managing a UITableView with a NSFetchedResultsController.
Update
Implement these NSFetchResultsController delegate methods to allow your table to be dynamically updated.
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath: (NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath
- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id )sectionInfo atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type
Generally, these methods contain boilerplate code for updating your table which you will also find in the link above.
I need some help indexing a UITableView, dividing the data in alphabetic sections. The data model is from a core data graph.
I have the index (A-Z), loaded ok, and the section headers are correct. The issue is that the data is not sorted correctly (i.e. alphabetic).
There is an entity attribute in the model that is an alphabetic index. I've looked at the sqlite file and that is ok.
Here are pieces of relevant code. Please help me understand what I'm missing or messing :)
- (void)viewDidLoad {
[super viewDidLoad];
sectionArray = [[NSArray alloc] init];
self.collation = [UILocalizedIndexedCollation currentCollation];
if (languageKey == 0) {
sectionArray = [NSArray arrayWithArray:[#"|α,ά|β|γ|δ|ε,έ|ζ|η,ή|θ|ι,ί|κ|λ|μ|ν|Ξ|ο,ό|π|ρ|σ|τ|υ,υ|φ|χ|ψ|ω,ώ|#"
componentsSeparatedByString:#"|"]];
} else {
sectionArray = [NSArray arrayWithArray:
[#"A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U|V|W|X|Y|Z|#"
componentsSeparatedByString:#"|"]];
}
NSManagedObjectContext *context = [self managedObjectContext];
NSEntityDescription *entityDescription = [NSEntityDescription
entityForName:#"WordEntity" inManagedObjectContext:context];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entityDescription];
[request setPredicate:[self predicate]];
[request setIncludesSubentities:NO];
filteredWordsArray = [[NSMutableArray alloc] init];
self.searchResults = [NSMutableArray arrayWithCapacity:[[self.fetchedResultsController fetchedObjects] count]];
self.searchDisplayController.searchResultsTableView.rowHeight = wordTable.rowHeight;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (tableView == self.searchDisplayController.searchResultsTableView)
{
return [self.searchResults count];
}
else
{
return [[[self.fetchedResultsController sections] objectAtIndex:section] numberOfObjects];
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"WordCell";
UITableViewCell *cell = (UITableViewCell *)[self.wordTable dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
WordEntity *word = nil;
if (tableView == self.searchDisplayController.searchResultsTableView) {
word = [self.searchResults objectAtIndex:indexPath.row];
count = self.searchResults.count;
self.numberWordsLabel.text = [NSString stringWithFormat:#"%lu", (unsigned long)count];
} else {
word = [self.fetchedResultsController objectAtIndexPath:indexPath];
self.numberWordsLabel.text = [NSString stringWithFormat:#"%lu", (unsigned long)fullCount];
}
if (languageKey == 0) {
cell.textLabel.text = word.greekText;
cell.detailTextLabel.text = word.englishText;
} else {
cell.textLabel.text = word.englishText;
cell.detailTextLabel.text = word.greekText;
}
return cell;
}
/*
Section-related methods: Retrieve the section titles and section index titles from the collation.
*/
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
long languageKey = [defaults integerForKey:DEFAULT_KEY_LANGUAGE_NUMBER];
long count = 0;
if (languageKey == 0) {
count = 24;
} else {
count = 26;
}
return count;
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
return [sectionArray objectAtIndex:section];
}
- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView {
return sectionArray;
}
- (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index {
return [collation sectionForSectionIndexTitleAtIndex:index];
}
- (NSFetchedResultsController *)fetchedResultsController {
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] init];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
long languageKey = [defaults integerForKey:DEFAULT_KEY_LANGUAGE_NUMBER ];
if (languageKey == 0) {
sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"greekKey" ascending:YES selector:#selector(localizedCaseInsensitiveCompare:)];
// sectionTitleString = #"greekKey";
} else {
sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"englishKey" ascending:YES selector:#selector(localizedCaseInsensitiveCompare:)];
// sectionTitleString = #"englishKey";
}
NSArray *sortDescriptors = #[sortDescriptor];
[fetchRequest setSortDescriptors:sortDescriptors];
_fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:#"greekKey" cacheName:nil];
_fetchedResultsController.delegate = self;
return _fetchedResultsController;
}
/*
-(NSArray *)partitionObjects:(NSArray *)array collationStringSelector:(SEL)selector
{
collation = [UILocalizedIndexedCollation currentCollation];
NSInteger sectionCount = [[collation sectionTitles] count]; //section count is from sectionTitles and not sectionIndexTitles
NSMutableArray *unsortedSections = [NSMutableArray arrayWithCapacity:sectionCount];
//create an array to hold the data for each section
for(int i = 0; i < sectionCount; i++)
{
[unsortedSections addObject:[NSMutableArray array]];
}
//put each object into a section
for (id object in array)
{
NSInteger index = [collation sectionForObject:object collationStringSelector:selector];
[[unsortedSections objectAtIndex:index] addObject:object];
}
sections = [NSMutableArray arrayWithCapacity:sectionCount];
//sort each section
for (NSMutableArray *section in unsortedSections)
{
[sections addObject:[collation sortedArrayFromArray:section collationStringSelector:selector]];
}
return sections;
}
*/
This is what I'm seeing:
Try adding a sort descriptor to your NSFetchRequest...
NSSortDescriptor *sortDescriptorSecondary = [[NSSortDescriptor alloc]
initWithKey:#"word" ascending:YES];
[request setSortDescriptors:#[sectionArray, sortDescriptorSecondary]];
Note that when you are using sections in your table view, you must always sort by section first and then other criteria.
UPDATE
In a little more detail...
Private properties to include:
#property (nonatomic, strong) NSArray *sectionArray;
Fetch request to include:
// Declare sort descriptors
NSArray *requestSortDescriptors = nil;
NSSortDescriptor *sortDescriptorPrimary = nil;
NSSortDescriptor *sortDescriptorSecondary = nil;
// Set sort descriptors...
// Primary sort descriptor is your section array - sort by sections first.
sortDescriptorPrimary = [NSSortDescriptor sortDescriptorWithKey:self.sectionArray ascending:YES];
// Secondary sort descriptor is your entity attribute `word` - sort by fetched data second.
sortDescriptorSecondary = [NSSortDescriptor sortDescriptorWithKey:#"word" ascending:YES];
// Set sort descriptor array
requestSortDescriptors = #[sortDescriptorPrimary, sortDescriptorSecondary];
// Apply sort descriptors to fetch request
[request setSortDescriptors:requestSortDescriptors];
Hope this assists, let me know if you require further explanation.
SECOND UPDATE
I neglected to mention how to propagate the section data.
Personally, for data that is to be displayed in a TVC with section headers, for each entity I define an entity attribute that I always for simplicity call sectionIdentifier.
Then as a part of my methods to persist data, I ensure "section" data is allocated to this attribute sectionIdentifier (e.g. in your example A, B, C, etc).
In your case however, and for this particular example, you have established a local variable called sectionArray in your viewDidLoad TVC lifecycle method.
Working with your code in mind, I suggest the following (per above)...
Private properties to include:
#property (nonatomic, strong) NSArray *sectionArray;
Also working with your code in mind, I suggest the following (new)...
Alter your TVC lifecycle method viewDidLoad:
- (void)viewDidLoad {
[super viewDidLoad];
self.sectionArray = nil;
self.collation = [UILocalizedIndexedCollation currentCollation];
if (languageKey == 0) {
self.sectionArray = [NSArray arrayWithArray:[#"|α,ά|β|γ|δ|ε,έ|ζ|η,ή|θ|ι,ί|κ|λ|μ|ν|Ξ|ο,ό|π|ρ|σ|τ|υ,υ|φ|χ|ψ|ω,ώ|#"
componentsSeparatedByString:#"|"]];
} else {
self.sectionArray = [NSArray arrayWithArray:[#"A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U|V|W|X|Y|Z|#"
componentsSeparatedByString:#"|"]];
}
// I have commented out code following because I cannot see where you are using it.
// Does the compiler not throw up warnings for this fetch request?
//
// NSManagedObjectContext *context = [self managedObjectContext];
// NSEntityDescription *entityDescription = [NSEntityDescription entityForName:#"WordEntity" inManagedObjectContext:context];
// NSFetchRequest *request = [[NSFetchRequest alloc] init];
// [request setEntity:entityDescription];
// [request setPredicate:[self predicate]];
// [request setIncludesSubentities:NO];
filteredWordsArray = [[NSMutableArray alloc] init];
self.searchResults = [NSMutableArray arrayWithCapacity:[[self.fetchedResultsController fetchedObjects] count]];
self.searchDisplayController.searchResultsTableView.rowHeight = wordTable.rowHeight;
}
Also working with your code in mind, I suggest the following (new)...
Alter your NSFetchedResultsController getter method:
- (NSFetchedResultsController *)fetchedResultsController {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
long languageKey = [defaults integerForKey:DEFAULT_KEY_LANGUAGE_NUMBER ];
// Set up fetch request
NSFetchRequest *fetchRequest = [[NSFetchRequest fetchRequestWithEntityName:#"WordEntity"];
[fetchRequest setPredicate:[self predicate]];
[fetchRequest setIncludesSubentities:NO];
// Declare sort descriptor variables
NSArray *requestSortDescriptors = nil;
NSSortDescriptor *sortDescriptorPrimary = nil;
NSSortDescriptor *sortDescriptorSecondary = nil;
// Set sort descriptors...
// Primary sort descriptor is your section array - sort by sections first.
sortDescriptorPrimary = [NSSortDescriptor sortDescriptorWithKey:self.sectionArray
ascending:YES];
// Secondary sort descriptor is your entity attribute - sort by fetched data second.
if (languageKey == 0) {
sortDescriptorSecondary = [NSSortDescriptor sortDescriptorWithKey:#"greekKey"
ascending:YES
selector:#selector(localizedCaseInsensitiveCompare:)];
// sectionTitleString = #"greekKey";
} else {
sortDescriptorSecondary = [NSSortDescriptor sortDescriptorWithKey:#"englishKey"
ascending:YES
selector:#selector(localizedCaseInsensitiveCompare:)];
// sectionTitleString = #"englishKey";
}
// Set sort descriptor array - section first, then table view data
requestSortDescriptors = #[sortDescriptorPrimary, sortDescriptorSecondary];
// Apply sort descriptors to fetch request
[fetchRequest setSortDescriptors:requestSortDescriptors];
// Your sectionNameKeyPath in your FRC is self.sectionArray (this may be incorrect - try)
_fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:self.managedObjectContext
sectionNameKeyPath:self.sectionArray
cacheName:nil];
_fetchedResultsController.delegate = self;
return _fetchedResultsController;
}
In my app I have a feature that allows the user to create their own table view of ingredients that they are allergic to. Here is my method for creating the array that populates it as well as the methods that are there to populate it:
- (void)viewDidLoad
{
[super viewDidLoad];
badIngredientsArray = [[NSMutableArray alloc] init];
rightButton = [[UIBarButtonItem alloc] initWithTitle:#"Add"
style:UIBarButtonItemStyleDone target:nil action:nil];
self.navigationItem.rightBarButtonItem = rightButton;
rightButton.target = self;
rightButton.action = #selector(addRow);
self.tableView.delegate = self;
self.tableView.dataSource = self;
// Uncomment the following line to preserve selection between presentations.
// self.clearsSelectionOnViewWillAppear = NO;
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem;
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSManagedObjectContext *context = [self managedObjectContext];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"AllergicIngredient" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSError *error;
NSArray *ingredientsArray = [context executeFetchRequest:fetchRequest error:&error];
badIngredientsArray = [NSMutableArray arrayWithArray:ingredientsArray];
}
-(void)addRow
{
UIAlertView *myAlertView = [[UIAlertView alloc] initWithTitle:#"Add a Bad Ingredient" message:#"Type the name of the ingredient" delegate:self cancelButtonTitle:#"Ok" otherButtonTitles:#"Cancel", nil];
myAlertView.alertViewStyle = UIAlertViewStylePlainTextInput;
[myAlertView show];
}
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
NSManagedObjectContext *context = [self managedObjectContext];
AllergicIngredient *allergic = [NSEntityDescription insertNewObjectForEntityForName:#"AllergicIngredient" inManagedObjectContext:context];
NSString *enteredString = [[alertView textFieldAtIndex:0] text];
[allergic setValue:enteredString forKey:#"name"];
NSError *error;
if (![context save:&error])
{
NSLog(#"Couldnt find the save %#", error.localizedDescription);
}
else
{
NSLog(#"It saved properly");
}
[badIngredientsArray addObject:enteredString];
[self.tableView reloadData];
}
It seems as though when I remove the following code:
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSManagedObjectContext *context = [self managedObjectContext];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"AllergicIngredient" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSError *error;
NSArray *ingredientsArray = [context executeFetchRequest:fetchRequest error:&error];
badIngredientsArray = [NSMutableArray arrayWithArray:ingredientsArray];
The functionality works fine, but it loses the core data aspect where if you delete the app and relaunch it the table view is empty. So I can't seem to wrap my head around what is wrong with the above code. For all of you who want to know where the SIGABRT error is happening, it's at this line:
cell.textLabel.text = [badIngredientsArray objectAtIndex:indexPath.row];
UPDATE:
My table view methods:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [badIngredientsArray count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath: (NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell" forIndexPath:indexPath];
if (cell == nil)
{
cell = [tableView dequeueReusableCellWithIdentifier:#"Cell" forIndexPath:indexPath];
}
cell.textLabel.text = [badIngredientsArray objectAtIndex:indexPath.row];
return cell;
}
[badIngredientsArray objectAtIndex:indexPath.row] does not return a NSString but a NSManagedObject.
In tableView:cellForRowAtIndexPath:, try the following code in order to get your name attribute:
AllergicIngredient *object = (AllergicIngredient *)[badIngredientsArray objectAtIndex:indexPath.row];
cell.textLabel.text = object.name;
//or
//NSManagedObject *object = (NSManagedObject *)[badIngredientsArray objectAtIndex:indexPath.row];
//cell.textLabel.text = [object objectForKey:#"name"];
Need help in getting the information on a presence information of a buddy.
I am calling "fetchRoster" function, however am getting the list for roster but not the presence information.
Also i tried calling presence information of a explicitly.But didRecievePresence delegate is not getting called in my iOS app.
Regards,
Cbhat
From Robbiehanson's XMPPFramework - RootViewController class:
Fetch your roster
- (NSFetchedResultsController *)fetchedResultsController
{
if (fetchedResultsController == nil)
{
NSManagedObjectContext *moc = [[self appDelegate] managedObjectContext_roster];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"XMPPUserCoreDataStorageObject"
inManagedObjectContext:moc];
NSSortDescriptor *sd1 = [[NSSortDescriptor alloc] initWithKey:#"sectionNum" ascending:YES];
NSSortDescriptor *sd2 = [[NSSortDescriptor alloc] initWithKey:#"displayName" ascending:YES];
NSArray *sortDescriptors = [NSArray arrayWithObjects:sd1, sd2, nil];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
[fetchRequest setEntity:entity];
[fetchRequest setSortDescriptors:sortDescriptors];
[fetchRequest setFetchBatchSize:10];
fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:moc
sectionNameKeyPath:#"sectionNum"
cacheName:nil];
[fetchedResultsController setDelegate:self];
NSError *error = nil;
if (![fetchedResultsController performFetch:&error])
{
DDLogError(#"Error performing fetch: %#", error);
}
}
return fetchedResultsController;
}
Reload your table whenever a change in a user's presence is recorded in the server
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
{
[[self tableView] reloadData];
}
Display avatars (profile pictures)
- (void)configurePhotoForCell:(UITableViewCell *)cell user:(XMPPUserCoreDataStorageObject *)user
{
// Our xmppRosterStorage will cache photos as they arrive from the xmppvCardAvatarModule.
// We only need to ask the avatar module for a photo, if the roster doesn't have it.
if (user.photo != nil)
{
cell.imageView.image = user.photo;
}
else
{
NSData *photoData = [[[self appDelegate] xmppvCardAvatarModule] photoDataForJID:user.jid];
if (photoData != nil)
cell.imageView.image = [UIImage imageWithData:photoData];
else
cell.imageView.image = [UIImage imageNamed:#"defaultPerson"];
}
}
Break down the table into two sections - Available/Offline
- (NSString *)tableView:(UITableView *)sender titleForHeaderInSection:(NSInteger)sectionIndex
{
NSArray *sections = [[self fetchedResultsController] sections];
if (sectionIndex < [sections count])
{
id <NSFetchedResultsSectionInfo> sectionInfo = [sections objectAtIndex:sectionIndex];
int section = [sectionInfo.name intValue];
switch (section)
{
case 0 : return #"Available";
case 1 : return #"Away";
default : return #"Offline";
}
}
return #"";
}
Display your entire roster with user's display name
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier];
}
XMPPUserCoreDataStorageObject *user = [[self fetchedResultsController] objectAtIndexPath:indexPath];
cell.textLabel.text = user.displayName;
[self configurePhotoForCell:cell user:user];
return cell;
}
This is included in the XMPPFramework which you've downloaded. Experiment with it. These 5 points I stated might be what you need.