i am making an app that requires core data to store some information, i have managed to get the app to create the entities and add the information, however i am unable to edit it
i have a list view controller that displays a brief outline of this information and another list view controller which acts as a modal view to allow the user to edit their information.
when the user taps a list item i need the app to show the modal view controller with the selected information loaded from the core data model into the relevant text boxes and when the user clicks save, i need the changes to be saved
at the moment when the user taps the list item the information is not passed into the modal view, NSLog confirms that for me - it tells me the item i tapped in the prepareForSegue: method and is null in the modal view's viewDidLoad:
here's the code:
this is from the MedsList view controller, it displays the information
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
} //selMed declared at top of file as NSManagedObject *selMed;
selMed = [self.meds objectAtIndex:indexPath.row];
NSLog(#"SELECTED MED: %#",[selMed valueForKey:#"name"] );
UIStoryboardSegue *segueString = [NSString stringWithFormat:#"%#",#"editMeds"];
NSLog(#"%#",segueString);
NSLog(#"%# %#", #"MED NAME",[self.meds objectAtIndex:indexPath.row]);
[self performSegueWithIdentifier:#"editMeds" sender:indexPath];
}
-(void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if ([segue isEqual: #"editMeds"]) {
NSLog(#"%# %#", #"SELMED AT PREPARE FOR SEGUE: ",selMed);
UINavigationController *nav = segue.destinationViewController;
MedsEditViewController *dest = (MedsEditViewController *)nav.topViewController;
dest.med = selMed;
dest.chName = [selMed valueForKey:#"name"];//chosen med's name
}
}
}
here is MedsEdit.m
#import "MedsEditViewController.h"
#import "fibroMappAppDelegate.h"
#import <CoreData/CoreData.h>
#interface MedsEditViewController ()
{
NSManagedObjectContext *context;
}
#end
#implementation MedsEditViewController
#synthesize tbName;
#synthesize tbDose;
#synthesize tbMaxDose;
#synthesize tbType;
#synthesize med;
double dose;
double maxDose;
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(#"%# %#", #"recieved MEDICATION", self.med);
NSLog(#"%# %#", #"recieved Name", _chName);
fibroMappAppDelegate *delegate = [[UIApplication sharedApplication]delegate];
context = [delegate managedObjectContext];
tbName.text = _chName;
}
chName is declared in the .h file as a NSString
could somebody please help me work out what i have done wrong? and how to fix it..... i've spent a lot of time on this on and off in the last few months
here is a copy of the log after prepare for segue starts
2013-11-07 14:21:15.475 fibromapp[660:70b] SELECTED MED: med1
2013-11-07 14:21:15.476 fibromapp[660:70b] editMeds
2013-11-07 14:21:15.477 fibromapp[660:70b] MED NAME (entity: Medication; id: 0x8a6b0a0 ;
data: {
dose = 3;
lastTaken = nil;
maxDose = 5;
name = med1;
type = Miligrams;
})
2013-11-07 14:21:15.479 fibromapp[660:70b] Warning: Attempt to present on while a presentation is in progress!
---------------------the following is in the MedsEdit viewDidLoad-------------------------
2013-11-07 14:21:15.497 fibromapp[660:70b] recieved MEDICATION (null)
2013-11-07 14:21:15.498 fibromapp[660:70b] recieved Name (null)
[segue isEqual: #"editMeds"] should be [segue.identifier isEqualToString: #"editMeds"] for a kick off...
Have you ever tried to set the MedsEditViewController as your segue.destinationViewController?
First of all, where in the code you posted are you actually setting the med on the destination controller? I see
dest.med = selMed
but it is commented out. If you never set it, then it won't ever be available. Could you please reformat and post the complete logs? It's not clear to me if your code ever reached the SELMED AT PREPARE FOR SEGUE log.
Assuming you did not mean to comment that out, or you received an error I think it may be here - UINavigationController *nav = segue.destinationViewController - unless your MedsEditViewController is actually a UINavigationController I suspect that the segue.destinationViewController is the navigation controller, which in turn is going to present your MedsEditViewController. Double check the documentiation for UINavigationController, but off the top of my head I think you need this:
dest.topViewController.med = selMed
Related
Hi im developing an app that has a parent view that then used containers to embed other views as seen below.
For now im only working with the left and centre container which are both table views. The main view or the Project screen view is my parent controller and i want it to pass data to and from the two child controller and i know for this the best option is to use delegates. However each example i have looked at that uses delegates, created and initialises a new view controller so for example lets say the left container embeds a view using the leftviewcontroller. Each example has this line of code.
LeftViewController *sampleProtocol = [[LeftViewController alloc]init];
LeftViewController.delegate = self;
Im thinking i dont need to create a new LeftViewController since it is embeded it is already in my list of child controllers. So my queston is how would i get the controller from the list of child controllers and set the parent as the delegate. I know i it is an array and i can use objectAtIndex but how do i know the order of items in the array will not change can i not call it but a tag or identifier? Thank you for any help sorry if the question is not that clear its my first time setting up delegates.
i know for this the best option is to use delegates.
In this case, I wouldn't be so sure. I think the best option would be to have a robust model and use KVO and notifications to signal updates between view controllers.
The direct answer to your question is not too bad.
for (UIViewController *viewController in self.childViewControllers) {
if ([viewController isKindOfClass:[LeftViewController class]]) {
LeftViewController *leftViewController = (id)viewController;
leftViewController.delegate = self;
break;
}
}
I think a minor improvement on this would be to use the segue. Make sure each of the containers have a named segue. In this example, the left view controller has a segue with the identifier "Load Child LeftViewController".
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"Load Child LeftViewController"]) {
LeftViewController *leftViewController = segue.destinationViewController;
leftViewController.delefate = self;
}
}
Its always better to use NSNotificationCenter for such complex mechanism.
*** put following code in LeftController.m ***
// *** Register a Notification to recieve a Data when something happens in Center controller ***
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(receivedNotification:)
name:#"hasSomeData"
object:nil];
// *** create a method to receive Notification data ***
- (void)receivedNotification:(NSNotification *) notification {
if ([[notification name] isEqualToString:#"hasSomeData"])
{
// do your stuff here with data
NSLog(#"data %#",[notification object]);
}
}
*** when something happen in center controller post a notification to inform Left Controller ***
[[NSNotificationCenter defaultCenter] postNotificationName:#"hasSomeData" object:self];
//Secondvc.h
#protocol Sendmessage<NSObject>
#required
-(void)Object:(NSArray *)tosend;
#end
#interface Secondvc:UIViewcontroller{
id <Sendmessage> delegate;
}
#property(strong,nonatomic) id <Sendmessage> delegate;
#end
//Secondvc.m
#implementation Secondvc
#synthesize delegate;
-(void)viewDidLoad{
//Do Something here!
}
//Pass Some Value When a button event occured in Second vc
-(IBAction)Send_Data{
[self dismissViewControllerAnimated:Yes completion:nil];
[self.delegate Object:[NSArray Arraywithobjects:#"Hello",nil]];
}
#end
//FirstVc.h
#import "Secondvc.h"
#interface FirstVc<Sendmessage>
#end
//FirstVc.m
#implementation FirstVc
-(void)viewDidLoad{
Secondvc* Object=[[Secondvc alloc]init];
Object.delegate=self;
}
#pragma mark Secondvc Deklegate method implementation
-(void)Object:(NSArray *)tosend{
NSLog(#"Recieved data Form Second VC Is:\n%#",tosend);
}
#end
HTH!Enjoy Coding.
Here is my .h file
#import <UIKit/UIKit.h>
#interface PersonViewController : UIViewController
#property(strong,nonatomic) NSString *personTitle;
And here is my .m file
#interface PersonViewController ()
#property (weak, nonatomic) IBOutlet UILabel *titleView;
#end
#implementation PersonViewController
//stuff …
-(void)setPersonTitle:(NSString *)personTitle
{
[self.titleView setText:personTitle];// also self.titleView.text=personTitle
[self.titleView setNeedsDisplay];
NSLog(#"The title shoud match as %# :: %#",personTitle,self.titleView.text);
}
-(NSString *)personTitle
{
return self.titleView.text;
}
//… more stuff
#end
The logging shows that the value is (null) for self.titleView.text whereas personTitle prints the appropriate value.
I remember doing this same thing a number of times and it worked. Any ideas why it’s failing this time?
update I use storyboard to set my scenes. And I am using xcode-5 and iOS-7
update: how I call
The user clicks a button, leading to a push segue
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
NSLog(#"enter prepare for segue.");
NSIndexPath *indexPath = [self.tableView indexPathForCell:sender];
if ([segue.identifier isEqualToString:the_identifier_for_person]) {
NSLog(#"segue to person is progressing“);
if ([segue.destinationViewController isKindOfClass:[PersonViewController class]]) {
NSLog(#"segue to person destination is a match");
PersonViewController *aPerson = (PersonViewController *)segue.destinationViewController;
aPerson.personTitle=((MyItem*)self.allItems[indexPath.row]).title;
NSLog(#"segue to person is done");
}
}
}
This sounds like you forgot to wire up your UILabel in the storyboard. Can you confirm that self.titleView is not null?
View controllers create their views on demand, but can spot that only via a call to view. When the view is loaded, your outlets will be populated.
Either call view to force loading or keep the string in abeyance until you get viewDidLoad.
(aside: prior to iOS 6, views would also be released in low-memory situations so the idiomatic thing is to store the string and populate on viewDidLoad)
Having accepted another answer, I wanted to show the pattern that I actually used to solve the problem, in case someone else comes looking. This pattern is best practice (yes, I forgot it for a long moment there).
#pragma mark - update UI
-(void)setPersonTitle:(NSString *)personTitle
{
_personTitle=personTitle;
if (self.view.window) [self updateUI];//only if I am on screen; or defer to viewWillAppear
}
-(void) viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self updateUI];
}
-(void)updateUI
{
self.titleView.text=self.personTitle;
}
It is always important to update the ui when the data has changed, which is why I must make the call inside setPersonTitle. But because the IBOutlets are not yet set when I set personTitle in prepareForSegue, then I must also make the call inside viewWillAppear.
Do you actually call the -(void)setPersonTitle:(NSString *)personTitle method?
It seems that you aren't calling it correctly which would result in the title being null.
After reviewing the prepareForSeque it is clear that you are not calling the method. You are actually just changing the #property named personTitle.
In the viewDidLoad you should have it so that self.titleView.text = self.personTitle;
can't for the life of me figure this out.
so I'm creating a contact list app, very similar to the one that comes with iOS7.
a user creates a name with the GUI on a detail view controller.
user goes back to the main contact list page (master view controller)
user taps on the name/contact they just created
brings them back to the view controller, but it shows what they had entered into the text fields.
User starts with this screen:
User hits the add contact button, adds in the user data here which brings them back to the first screen:
Next, they are brought to the first screen and are able to tap on the contact they've added and should bring up all the details entered (firstname,lastname,phone number etc):
here's my code in my master view controller:
- (void) detailViewControllerWillDissapear: (DetailViewController *) dvc {
NSLog(#"test");
DirectoryEntry *person = [[DirectoryEntry alloc] init];
person.firstName = dvc.txtFirstName.text;
person.lastName = dvc.txtLastName.text;
person.phoneNumber = dvc.txtPhoneNumber.text;
person.address = dvc.txtAddress.text;
[self.entries addObject:person];
NSLog(#"index is %#", person.firstName);
NSLog(#"count of array %lu", (unsigned long)self.entries.count);
dvc.holdData = person;
[self.tableView reloadData];
}
here's my segue:
#pragma mark - segue
- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
//DirectoryEntry *person = [[DirectoryEntry alloc] init];
DetailViewController *dvc = segue.destinationViewController;
dvc.delegate = self;
if ([segue.identifier isEqualToString:(#"tappedData")]) {
NSLog(#"what is the person class have %#", dvc.holdData);
// NSLog(#"dvc.holdData = %#", person.firstName);
}
}
so I've created the public variable holdData which is meant to hold all the data which was entered from the array into the Person class. but whenever I add it, just comes up with a Null value. I am able to access the variables i.e. holdData.firstName, but just everything is null.
any ideas? really confused, thanks.
The dvc that you have in prepareForSegue is almost certainly not the same object that you had in the detailViewControllerWillDissapear method. (Try printing their addresses to prove that for yourself.) That means that either your main controller should save the Person data and provide it during the segue or you could create a separate data model that each DetailViewController would use directly.
i've been fixing this problem for a few days. but can't seem to get it..
help me out ..
let me explain my situation. Basically, i have navigation controller that contains table view controller and view controller. and i'm making simple phone book app.
And, i have a directory entry declared in extension class
#interface DetailViewController ()
#property DirectoryEntry *dirEntry;
#end
And, in table view, when you click the button it will transfer some data through segue
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
DetailViewController *detailCV = [segue destinationViewController];
if ([segue.identifier isEqualToString:#"cellToDetail"]) {
[detailCV setDirEntry: [self.pbArray objectAtIndex:[self.tableView indexPathForSelectedRow].row]];
} else {
detailCV.dirEntry = nil;
}
//designate delegate !!!
detailCV.delegate = self;
}
My Problem occurs when it execute detailCV.dirEntry = nil; it will call my setter in viewController. it says EXC_BAD_ACCESS
-(void) setDirEntry:(DirectoryEntry *) dirEntry {
self.dirEntry = dirEntry;
}
Thank you in advance..
It's not an EXC_BAD_ACCESS so much as the OS killing your app for smashing the stack. This method is recursing infinitely:
-(void) setDirEntry:(DirectoryEntry *) dirEntry {
self.dirEntry = dirEntry;
}
Your use of dot notation expands to a setter which should make this more clear.
-(void) setDirEntry:(DirectoryEntry *) dirEntry {
[self setDirEntry:dirEntry];
}
Set the instance variable directly, or let the compiler handle it. Properties in class extensions are automatically synthesized.
I've searched a lot and still couldn't find an answer to this...
I'm working on an iphone App (for college) in xcode 5.0 using storyboards.
I have a View Controller with a table view and everything works fine (data sources, delegate...). Items are stored in an array (call it "array1"). I have an ADD button which brings up a list of items which I want to add (if checked) to array1. I store the checked items in another array ("array2"). The thing is, when I pass array2 to the previous view controller I lose all data in array1 (it becomes nil).
The code I'm using to pass data from array2 to the VC is:
-(void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"updateFavoritesSegue"]) {
FavoritesViewController *targetVC = (FavoritesViewController*) segue.destinationViewController;
[targetVC updateFavorites:[self newFavoritesArray]];
}
}
The updateFavorites method is implemented as below.
-(void) updateFavorites:(NSArray *)newFavorites {
[self.favorites addObjectsFromArray:newFavorites];
[self.favoritesTableView reloadData];
}
Thank you very much for your help.
Why don't you just use some handler?
secondVC.h
#property (nonatomic, copy) void (^didFinishPickingItemsHandler)(NSArray *items);
then from the firstVC:
- (void)showSecondScreen {
MYSecondVC *secondVC = /* initialisation code here */
__weak MyFirstVC *self_ = self;
secondVC.didFinishPickingItemsHandler = ^(NSArray *items) {
/* update you data here with your array1 and received picked items */
[self.navigationController popViewControllerAnimated:YES];
};
[self.navigationController pushViewController:secondVC animated:YES];
}