Saving data from tableview cells in core data - ios

Hi I am trying to save data into core data and having some trouble... I have a Team Entity and a Player Entity The Team Entity is set up as a one to many relationship with the Player Entity... on my "NewTeamViewController" there are two sections, in the second section is where you add players to the team. In the section header there is a button to add a new player... When that button is pressed a new cell appears with three textFields, each with default text in them (not placeholder text) then I am adding the new player to a MutableSet that will be added as the teams players. The tableview is using a custom cell (which is where the three textFields live)
The team saves correctly, except I am unable to save the data from the three text fields in the player cell. It just saves the default text in the cell for the player.
I am not sure how or where to give the data from the newly added cell to the newly added player object.
Here is some code...
-(void)saveButtonWasPressed {
self.team =[NSEntityDescription insertNewObjectForEntityForName:#"Team" inManagedObjectContext:self.managedObjectContext];
team.schoolName = _schoolName.text;
team.teamName = _teamName.text;
team.season = _season.text;
team.headCoach = _headCoach.text;
team.astCoach = _assistantCoach.text;
player.firstName = cell.playerFirstName.text;
player.lastName = cell.playerLastName.text;
player.number = cell.playerNumber.text;
[self.team addPlayers:_tempSet];
[self.managedObjectContext save:nil];
[self.navigationController popViewControllerAnimated:YES];
}
//////////////////////////////////////////////////////////////////////////////////////////////////
-(void)addPlayerButton {
player = (Player *)[NSEntityDescription insertNewObjectForEntityForName:#"Player"
inManagedObjectContext:self.managedObjectContext];
[_tempSet addObject:player];
[self.tableView reloadSections:[NSIndexSet indexSetWithIndex:1] withRowAnimation:UITableViewRowAnimationFade];
}

Add your NewTeamViewController as a target for the control event UIControlEventEditingChanged for each of your text fields. You can do this in code (cellForRowAtIndexPath...) or in your nib or storyboard. I would use a different action method for each of the three different text fields in each cell. Here is what your action methods might look like:
// Helper method
- (Player *)playerForTextField:(UITextField *)textField
{
NSIndexPath *indexPath = [self.tableView indexPathForCell:textField.superview];
return [_tempArray objectAtIndex:indexPath.row];
}
- (IBAction)firstNameDidChange:(UITextField *)textField
{
Player *player = [self playerForTextField:textField];
player.firstName = textField.text;
}
- (IBAction)lastNameDidChange:(UITextField *)textField
{
Player *player = [self playerForTextField:textField];
player.lastName = textField.text;
}
- (IBAction)numberDidChange:(UITextField *)textField
{
Player *player = [self playerForTextField:textField];
player.number = textField.text;
}
Also, change your _tempSet to a _tempArray because knowing the order of the players in the table is useful.

Related

tableview indexing confusion

I am creating simple reveal menu in my demo application. Right now what I am doing is I have a one MutableArray named MutableMenu
NSArray menu = [#"menu1",#"Menu2","Menu3",#"Menu4"];
MutableMenu = [NSMutableArray alloc]initwithArray:menu];
I am just displaying this mutable array in my by default tableview cell.
In cellForRowAtIndexPath method I am using static indexing like below code snippet
if(indexPath.row == 0){
[self performSegueWithIdentifier:SEGUE_TO_MANAGE_TASK sender:nil];
}
if(indexPath.row == 1){
[self performSegueWithIdentifier:SEGUE_TO_MANAGE_LEAVE sender:nil];
}
All is working great till now. Now I have a array that I want to check permission to display in tableview cell. For example first user have only 2 permissions from 4 menus. This user only able to access menu1 and menu2. Likewise different users different permissions.
This is my code to maintaining this scenario in viewDidLoad. All is working fine
BOOL permisson = NO;
NSArray *Roles = [USERDEFAULT objectForKey:PREF_ROLE_ARRAY];
for (NSString *temp in Roles) {
if ([temp isEqualToString:#"55"] || [temp isEqualToString:#"45"] || [temp isEqualToString:#"67"] ) {
permisson = YES;
if ([Mutablemenu containsObject:#"Team management"])
NSLog(#"Already Have");
else [Mutablemenu insertObject:#"Team management" atIndex:2];
}
}
if (!permisson) {
[Mutablemenu removeObject:#"Team management"];
[Mutablemenu insertObject:#"" atIndex:2];
[MutableImages removeObject:#"team.png"];
}
[_tableview reloadData];
All is working fine But when user have only 3 permission.My indexing is changed in didSelectRowatIndexPath. For Example first user have 4 permission then index is 0,1,2,3 but when user have 3 permission then index is 0,1,2.
But my question is how do I handle this situation in didSelectRowatIndexPath. Because every time indexing is changed.
As told by #RJV Kumar, you should have check actual menu clicked instead of indexPath in didSelectRowatIndexPath as:
if([menuArray[indexPath.row] isEqualToString: #"Menu1"]){
[self performSegueWithIdentifier:SEGUE_TO_HANDLE_MENU_1 sender:nil];
}
else if([menuArray[indexPath.row] isEqualToString: #"Menu2"]){
[self performSegueWithIdentifier:SEGUE_TO_HANDLE_MENU_2 sender:nil];
}
.
.
so on...
As per my understanding, you are finding the menu based on index, but you should not do like that,
instead of that maintain menuid or menuname to identify which menu the user selected. By this way, you don't have to worry about the index.
In didselectRow method, by comparing menuid or menuname, do your operation

iOS Update Reload Contents Timer

In my VC I have a UITableView. Each cell has a UITableView as one of its contents. Timer is set updating each cell every 10secs. Events are handled which also reloads the respective cell.
Method that timer calls :-
-(void) updateVisitotsLists {
NSLog(#"UPDATING VISITORS LIST ***************************");
// Call API's to get lists
[api getVisitorsList];
// Init Arrays
browsingList = [MainAppDataObject sharedAppDataObject].visitors_browsingList;
secondList = [MainAppDataObject sharedAppDataObject].visitors_secondList;
thirdList = [MainAppDataObject sharedAppDataObject].visitors_thirdList;
fourthList = [MainAppDataObject sharedAppDataObject].visitors_fourthList;
// AS these are no more useful, so make it nil & save memory
[MainAppDataObject sharedAppDataObject].visitors_browsingList = nil;
[MainAppDataObject sharedAppDataObject].visitors_secondList = nil;
[MainAppDataObject sharedAppDataObject].visitors_thirdList = nil;
[MainAppDataObject sharedAppDataObject].visitors_fourthList = nil;
// Reload all lists with latest data
[self reloadBrowsingRow];
[self reloadSecondRow];
[self reloadThirdRow];
[self reloadFourthRow];
}
Event Handler Method :-
-(void) handleNewVisitor : (NSNotification *) notification {
// UPDATE VISITOR'S LIST
Visitor *v = [notification object];
#try {
if (v != nil) {
// Add V to browsing list
[browsingList addObject:v];
// Reload browsing list
[self reloadBrowsingRow];
}
}#catch (NSException *e) {
NSLog(#"EXCEP - %#", e);
}
v = nil;
return;
}
Reloading Method -
-(void)reloadBrowsingRow {
// Browsing
VisitorsListsCell *bcell = (VisitorsListsCell*)[self.visitorlistsTv cellForRowAtIndexPath:[NSIndexPath indexPathForRow:2 inSection:0]];
[bcell.listsTableView reloadData];
[bcell updateButtonText];
[bcell setNeedsDisplay];
bcell = nil;
return;
}
The Problem :-
When updateVisitotsLists is called thru timer, the updated contents are not reflected on cell.
When event handler method calls the same [self reloadBrowsingRow]; method, the contents of the cell are updated and reflected.
Due to this despite cells contents are updated but are not reflected until the state of cell is changed - expanded or collapsed.
I tried removing timer and cell updates properly on event caught, but when timer was on and event was caught, method is called but contents are not reflected on the screen.
I feel both methods may be calling reload method at same time, hence this must be happening or what ? How can this be handled making sure that the contents of cells are updated in any respect ? Any help is highly appreciated. Thanks.
Use [tableview reloadData]; on that method.
Because Reload on tableView will reload all data, so it will be more helpful.

How can I alternate view controller and not lose data

I have two UIViewControllers, one is a UIPickerViewController, the Other a UITableViewController. Ideally the Picker should get a request from the user to add x amount of some item to the tableView. The Picker gets user inputs and assigns them to variables val1, val2, val3, where val1 is the number of items (number of rows) and val2 is the name or label for the item.
PickerViewController.m
- (IBAction)add:(id)sender
{
TableViewController *tvc = [[TableViewController alloc] init];
[tvc setValues:self.val1 :self.val2 :self.val3];
[self presentViewController:tvc animated:YES completion:nil];
}
TableViewController.m
-(void)setValues:(NSString *)newVal1 :(NSString *)newVal2 :(NSString *)newVal3
{
self.val1 = newVal1;
self.val2 = newVal2;
self.val3 = newVal3;
}
- (void)viewDidLoad
{
[super viewDidLoad];
[self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:#"UITableViewCell"];
// This is just a header which holds my "Add" button
UIView *header = self.headerView;
[self.tableView setTableHeaderView:header];
[self addNew:self.val1 :self.val2 :self.val3];
}
- (void)addNew:(NSString *)newVal1 :(NSString *)newVal2 :(NSString *)newVal3
{
if(!self.numberOfRows){
NSLog(#"Initially no of rows = %d", self.numberOfRows);
self.numberOfRows = [self.val1 intValue];
NSLog(#"Then no of rows = %d", self.numberOfRows);
}
else
{
self.numberOfRows = self.numberOfRows + [newVal1 intValue];
NSLog(#"New no rows = %d", self.numberOfRows);
}
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:self.numberOfRows inSection:0];
// Only run when called again .. not initially
if(self.run != 0){
[self.tableView beginUpdates];
[self.tableView insertRowsAtIndexPaths:#[indexPath]withRowAnimation:UITableViewRowAnimationBottom];
self.run ++;
[self.tableView endUpdates];
}
}
// "ADD" button which should go back to the picker and get new items to add to the table
- (IBAction)testAdd:(id)sender
{
PickerViewController *pvc = [[PickerViewController alloc] init];
[self presentViewController:pvc animated:YES completion:nil];
}
Now, I realize every time I call the next view controller I am creating a new instance of it, but I don't know how else to do it, I figure this is the main problem. As of right now, I expect when I leave the tableview for the picker view and return the console should log "New no of rows = x" but that doesn't happen.
I know val3 isn't used and my addNew: may not be the best, but I just need it to handle the basic logging mentioned above and I should be able to take it from there.
Been stuck on this for days
Create a property for TableViewController, and only create it the first time you present it,
- (IBAction)add:(id)sender {
if (! self.tvc) {
self.tvc = [[TableViewController alloc] init];
}
[self.tvc setValues:self.val1 :self.val2 :self.val3];
[self presentViewController:self.tvc animated:YES completion:nil];
}
It's not entirely clear from you question, whether it's this presentation or the one you have in the table view class that you're talking about. It also looks like you're doing something wrong in terms of presentation -- you're presenting the picker view from the table view controller, and also presenting the table view controller from the picker. That's not correct, you should present which ever controller you want to appear second, and that controller should use dismissViewControllerAnimated to go back, not present another controller.
In testAdd you don't need to create a new instance and present it. If you want to go back to the presentingViewController, just use dismissViewControllerAnimated .
And you will go one controller up in the stack.

Switching views without dismissing View

I am developing a small music application in which first view comprises of all the List of songs in Tableview and when user taps on any one row (song) it takes them to Second view & Plays the song their(comprising of play/Pause Buttons). Now I go back in First View from Second View using back button to select another song. And their is one button in First View for just switching views(i.e it takes to current playing song)but it plays that same song from start but not from its currentplaying . Its similar to now Playing button in Stock Music app in iOS.
Im using storyboards & Segues
-(void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
temp = indexPath.row;
[tableView deselectRowAtIndexPath:indexPath animated:NO];
[self performSegueWithIdentifier:#"Player" sender:self];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if([[segue identifier] isEqualToString:#"Player"])
{
MPMediaItem *item = [itemCollections objectAtIndex:temp];
NSString *trackInfo = [[NSString alloc] initWithFormat:#"%#- %#",[item valueForProperty:MPMediaItemPropertyTitle],[item valueForProperty:MPMediaItemPropertyArtist]];
MPMediaItemArtwork *artwork = [item valueForProperty:MPMediaItemPropertyArtwork];
NSURL *tempURL = [item valueForProperty:MPMediaItemPropertyAssetURL];
playerController = [segue destinationViewController];
playerController.fileUrl1 = tempURL;
playerController.globalVar = [NSNumber numberWithInteger:temp];
playerController.labelTitleString = trackInfo;
playerController.img = [artwork imageWithSize:CGSizeMake(100, 100)];
}
}
You could go about this in many ways -- I would suggest implementing a singleton to handle the media playback. Rather than constantly retaining that view, this singleton manager (part of your model) should provide any controllers with the needed information about the current song, album, author, etc.
Ex. In your TableViewController:
- (void)viewDidLoad
{
[self setSong:[[PlaybackManager sharedInstance] currentSong]];
if (self.song) {
//Add navigation item
}
}

Programatically changing multiple UIViews in masterDetail App

THIS QUESTION HAS BEEN RE-WRITTEN IN ORDER TO PROPERLY COMMUNICATE THE INTENTIONS OF THE AUTHOR
I have a MasterDetail App that will eventually become a way to track an amount of "players" through a fixed amount of "challenges" (60 to be exact). The challenges are the same for every player, while the status of each player on the individual challenges may vary. All of the data about the players are being stored on a Postgres Database and then fetched using the JSONModel in the LeftViewController. I am getting all of the data correctly from the DB and I have successfully put it into a NSMutableDictionary. The data in the Dictionary looks like this (this is one player):
player = (
{
id = 9;
name = "Arthur Dent";
currentChallenge = "Cooking";
timeStarted = "04 Jul 2013 1:08 PM";
challengesDone = (
{
challengeName = "Flying";
timeCompleted = "04 Jul 2013 12:08 PM";
}
);
nextChallenge = "Swimming";
freePass = (
Running,
"Climbing"
);
},
As you can see, the player has some challenges "done" ("done"), some he has doesn't need to do ("pass") on, one he is doing ("currentChallenge") and the one challenge that he will do next ("nextChallenge"). For each of these states that a player can be in, I want the "Challenges" that are in the RightViewController to be color coded so you can understand what is happening at a glance. I need the "challenges" to light up different colors, based on the status of the player, which is determined by the data that is in my NSMutableDictionary.
By creating arrays from the data in the NSDictionary I can get the colors to change by doing this:
if ([freePassArray containsObject:#"challenge three name"])
{
UIImage *image = [UIImage imageNamed:#"status_pass.png"]; //status_pass.png is my color
[challengeThreeImageView setImage:image];
}
But if I did it like this, I would have to write 240 if and else if statements to cover all possible statuses on all 60 challenges.
My arrays only contain the data of the player selected because I pass the data over like so:
*LeftViewController.m * didSelectRowAtIndexPath
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:YES];
//Re-fetch the feed from the Postgres Database when a user selects an entry
[JSONHTTPClient getJSONFromURLWithString:#"http://myurl" completion:^(NSDictionary *json, JSONModelError *err) {
NSError* error = nil;
_feed = [[PostgresFeed alloc] initWithDictionary:json error:&error];
[[NSNotificationCenter defaultCenter] postNotificationName:#"myNotification" object:nil userInfo:[[json objectForKey:#"preclear"] objectAtIndex:indexPath.row]];
}];
Player *selectedPlayer = [_players objectAtIndex:indexPath.row];
if (_delegate)
{
[_delegate selectedPlayer:selectedPlayer];
}
}
So the images wont set for all of the players at once, but they also do not clear after another player is selected.
How do I get the colors to change without writing 240 if and else if statements? and how would I get the colors to clear when I select another player?
Assuming you have all the 50 images and you know which challenge corresponds to which image I would put the status image on top of the actual challenge image:
- (void)setChallengesDone:(NSArray*)doneAr next:(NSArray*)nextAr current:(NSArray*)currentAr andPass:(NSArray*)passAr
{
// remove additions from the previous selected player
UIView *prevAdditionsView = [self.view viewWithTag:122333];
while (prevAdditionsView != nil)
{
[prevAdditionsView removeFromSuperview];
prevAdditionsView = [self.view viewWithTag:122333];
}
for (int i=0; i<[doneAr count]; i++)
{
[self addChallengeImageAdditionWithImage:#"status_done" forChallenge:[doneAr objectAtIndex:i]];
}
for (int i=0; i<[nextAr count]; i++)
{
[self addChallengeImageAdditionWithImage:#"status_next" forChallenge:[nextAr objectAtIndex:i]];
}
for (int i=0; i<[currentAr count]; i++)
{
[self addChallengeImageAdditionWithImage:#"status_current" forChallenge:[currentAr objectAtIndex:i]];
}
for (int i=0; i<[passAr count]; i++)
{
[self addChallengeImageAdditionWithImage:#"status_free" forChallenge:[passAr objectAtIndex:i]];
}
}
- (void)addChallengeImageAdditionWithImage:(NSString*)image forChallenge:(NSString*)challengeTitle
{
UIImageView *challengeImage = [challenges objectForKey:challengeTitle];
CGRect frame = challengeImage.frame;
UIImageView *statusImg = [[[UIImageView alloc] initWithImage:[UIImage imageNamed:image]] autorelease];
[statusImg setFrame:frame];
[statusImg setAlpha:0.5]; // optionally add some transparency
[statusImg setTag:122333];
[self.view addSubview:statusImg];
[self.view bringSubviewToFront:challengeImage]; // bring challenge image to front
}
The simplest way to map your challenge titles with the UIImageViews is to maintain a NSDictionary with the challenge titles as keys, and a references to their corresponding UIImageViews as values.
This is an excellent example of the model/view concept.
Each challenge is a model with all the info required (challenge name, status, timestamp and others).
Each of your 50 views is a "challenge view" that knows how to render any challenge.
The status data is in the model; the status image is in the view.
Example:
-(void) drawChallenge: (Challenge*) challenge
{
// Draw challenge name as text
switch ([challenge status])
{
case done: // draw "done" image
case next: // draw "next" image
}
}
The idea is that the challenge itself should not know about how it is rendered, and the view should not know about what the challenge contains. The view is responsible for drawing it.
This could be packed into a ChallengeView subclass of UIView. Then, create your 50 or so ChallengeViews, and call [currentView drawChallenge: currentChallenge].
Essentially, when a new challenge is added, you create a view for it, and give the view a pointer to the challenge. When the challenge view is rendered by iOS, it'll draw "its" challenge as you have described in the drawChallenge method.
If you need a primer on the Model-View-Controller pattern, this section of the Apple docs is pretty good. You can disregard the Controller role in this situation.

Resources