subclass NSWindowController Core Data - core-data

I have a newb question, which I have tried unsuccessfully to find answers for on the web. The task is simple: I want to create a core data document-based app but alter the values in some label objects. Using interface builder, I can build the core data model and populate it, using an array controller, table, etc. all without writing any code. So far so good. My test example is to build a core data model with Box entities that have length and width attributes. I would like a label to display the area, i.e. length*width for any geometrically challenged :).
So after browsing around, I've decided I need to create an NSWindowController subclass and use that to update the label when a box in the table is selected. Have attempted this, but have failed. Before I even hook up the label to the window controller, I have a problem. Following the template comments, I added this to Document.m:
- (void)makeWindowControllers
{
NSLog(#"Adding custom Window Controller");
MyWindowController* myWindowController = [[MyWindowController alloc] init];
[self addWindowController:myWindowController];
}
Also added this to the template MyWindowController.m:
- (id)init
{
self = [super initWithWindowNibName:#"MyWindowController"];
return self;
}
The window controller has its own NIB file from Interface builder where I put the table and label etc. The file owner is set to MyWindowController. Probably forgetting other things, but that's what I remember for now.
The log message appears at startup, but then I get an exception "this class is not key value coding-compliant for the key managedObjectContext" before the window appears. I'm guessing that I don't have the window controller hooked up to the document class properly? My other thought is that the array controller is in the window controller nib, not the document nib, so maybe it's looking in the wrong place for the managedObjectContext?

I would try a different approach: Add area to your Box entity as a read-only attribute. Auto-create a Box class with Xcode ("Create NSManagedObject Subclass" menu item in the "Editor" menu when viewing the data model), then add this to Box.h
#property (weak, readonly) NSString * area;
and this to Box.m
- (NSNumber *)area
{
return [NSNumber numberWithDouble:([[self length] doubleValue] - [[self width] doubleValue])];
}
+ (NSSet *)keyPathsForValuesAffectingArea
{
return [NSSet setWithObjects:#"length", #"width", nil];
}
If you do this you can just bind area to a label value like you do for the other Box properties. No need to subclass NSWindowController or watch for changes.

Related

Use core data to send email with string

I have set up a core data model and I would like the ability to be able to tap on an entry and have an blank email pop up with the core data contents included. here is my code for saving the data-
NSManagedObject *newDevice = [NSEntityDescription insertNewObjectForEntityForName:#"Device" inManagedObjectContext:context];
NSNumber *timetickNumber = [NSNumber numberWithInt:timeTick];
NSString *versionString = [NSString stringWithFormat:#"Minutes of %#", self.versionTextField.text];
[newDevice setValue:timetickNumber forKey:#"name"];
[newDevice setValue:versionString forKey:#"version"];
[newDevice setValue:self.companyTextField.text forKey:#"company"];
Thanks in advance!
You can't click (or tap) on a Core Data object, because they do not have any UI and do not respond to UI events. The general flow you want is probably something like:
Figure out what UI element should trigger the email action-- a button, or a table row, or whatever UI element you're using.
In the handler method for taps on that element, create the MFMailComposeViewController. Where this happens depends on what UI element you're using. If your UI element is a button, this happens in whatever method your button calls when tapped. If the UI element is a table row, you probably want to use tableView:didSelectRowAtIndexPath: in your table view's delegate.
In that same method, get a reference to the managed object you want to send. If you fetched it (or created it) earlier, you might already have an instance var that points to it. If not, you may need to fetch it here.
Before displaying the MFMailComposeViewController, configure either the message body (via setMessageBody:isHTML:) or the attachment (via addAttachmentData:mimeType:fileName:) to contain the data from your managed object's data
Display the mail compose view to the user.

iOS how to display new UIView?

New to iOS dev. Kindly correct me if i am wrong?
I have a UIWindow, and rootviewcontroller is :
#interface ViewController : UIViewController
{
IBOutlet UIButton *but;
}
#property (nonatomic, strong) UIButton *but;
-(IBAction) butButtonPressed;
#end
ie: If i make this Viewcontroller as a root view controller, the UIView that is available in the ViewController is displayed. Understood.
I have created a new class inherited from UIViewController, and along with its .xib file.
So xib file name is : view1.xib,
My Objective is to display this view when the button is pressed.
Now i have created a button and button press invokes butButtonPressed. Inside of butButtonPressed, i did the following.
myViewController *vcontroler = [[ViewController alloc] initWithNibName:#"view1" bundle:nil];
[self.view.window addSubview:vcontroler.view];
Application crashes. What am i doing wrong ? Kindly point out.
This...
[self.view.window addSubview:vcontroler.view];
...is a really bad strategy (and I seriously wish I knew where people are finding it as a way of showing views). If you create a view controller, you should use it rather than just treating it as a temporary container that you can rip views out of.
If you want vcontroler to look like a child of your first controller (and you have a UINavigationController), use pushViewController:animated:. Otherwise you can show it as modal with presentModalViewController:animated:.
If you only want to add a view to the existing display, put the view in ViewController's hierarchy and show/hide it.
If you absolutely must have it as a sub-controller of ViewController then you need to keep a reference to it and manage its lifecycle inside the owning controller.

What must be done to migrate from Storyboard back to XIBs for iOS 4.x support?

I have seen scattered information related to this subject across stackoverflow and other sites, but nothing comprehensive. I've wasted hours upon hours trying to get this to work to no avail. However, I see in this post on stackoverflow (http://stackoverflow.com/questions/7909906/extracting-nibs-xibs-from-storyboards-ios), but nothing with step by step details.
I have only a single view controller (HomeViewController) in my storyboard, and it is extended from a navigation controller.
Can someone please help me identify exactly what all needs to happen to make this work?
Please correct me, add to, etc to make these steps correct:
Create a new XIB (say, HomeViewController.xib)
Copy all items from the storyboard's view controller into this xib
Set the Class type for the view controller to be HomeViewController class.
Hook up all of the IBOutlets and IBActions.
?? - Should I make the xib contain a navigation controller which contains the UIViewController, or should I set the File's Owner to the HomeViewController class??
Add new Window property in the AppDelegate class.
Create a new MainWindow.xib. What setup needs to happen here? (a) File's Owner to UIApplication; (b) Drag new Object to the "Objects" panel and set it's class to my AppDelegate class; (c) Drag new Window to the "Objects" panel; (d) Connect the IBOutlet for the AppDelegate Window property to this new window object.
Go to the project settings (click on the project in the Project Navigation tab, select the target), and clear the "Main Storyboard" field and set the Main Interface to what? HomeViewController? Or Main Window?
Make the change in the AppDelegate class as shown below:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
//self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.
//self.window.backgroundColor = [UIColor whiteColor];
[self.window makeKeyAndVisible];
return YES;
}
This is all I know to do, but it still doesn't work. What am I missing?

Detect if UIViewController is running inside an existing UINavigationController

I'm creating a generic reusable UIViewController component that people can add to their applications. It requires a navigation bar at the top where it will add some buttons.
I can easily create a navigationBar and add the buttons, but if the developer using my component is adding the view as part of an existing navigation structure, they might end up with 2 navigation bars.
In other words, if my view is loaded with:
[self.navigationController pushViewController:controller animated:YES];
then it should not add a navigationbar and use what's already there. If the view is loaded with:
[self presentModalViewController:controller animated:YES];
then it should add its own navbar.
Without requiring the developer that uses my controller to do something like a useNavBar:YES, is there a way to do this automatically?
Something like a [self isRunningInsideANavigationController] or [self hasNavigationBar] would do.
You could use self.navigationController for that purpose. It will return nil or the navigationController.

Getting UITabBarController to work with Core Data

I've been reading this thread on Stackoverflow and have been trying to replicate the solution with no success in my own project.
My project has 4 tabs. In my app delegate I do this:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
Page1 *page1 = (Page1 *)[navController topViewController];
Page2 *page2 = (Page2 *)[navController topViewController];
Page3 *page3 = (Page3 *)[navController topViewController];
Page4 *page4 = (Page4 *)[navController topViewController];
page1.managedObjectContext = self.managedObjectContext;
page2.managedObjectContext = self.managedObjectContext;
page3.managedObjectContext = self.managedObjectContext;
page4.managedObjectContext = self.managedObjectContext;
[self.window makeKeyAndVisible];
return YES;
}
In the originating thread it says I need to create a IBOutlet to each navController for each tab I want to use Core data on.
Whilst you can assign multiple delegates for the UINavigationController the same is not true for the outlets, you can only ever supply ONE outlet for the navController.
I can get Page1 to work, but the other pages simply crash; because of the lack of an IBOutlet.
Do I really need X IBOutlets for Y Tabs or can I do it another way?
Another issue is that the originating thread the accepted answer is:
Ideally you want to pass either the
NSManagedObjectContext,
NSFetchedResultsController or the
relevant NSManagedObject "down" into
the UIViewController.
But there is no code or example of how to do this.
Ideally, I do not want to use a singelton or use the app delegate all over the place.
Any confirmation and clarification would be great.
Thanks.
Your immediate problem has nothing to do with Core Data. You are assigning the same navigation controller to each tab when you need a separate navigation controller for each tab otherwise the navigation controller's hierarchy of views will get scrambled every time you change tabs.
The pattern recommended in the question you linked to is called "dependency injection" and it is the one that Apple recommends in most cases. However, in the case of tabbars or any other complex view/view-controller hierarchy, dependency injection can get to complicated. It's a particular issue with tabbars because you don't usually load all tab view/view-controllers when the app starts but wait until each tab is selected before loading its elements.
Instead, you can use an alternative pattern that exploits the UIApplication objects singleton status. Since there is only one application object, there is only one application delegate object. That means that anywhere in the app you can make a call like this:
(MyApplicationDelegate *) appDelegate=(MyApplicationDelegate *)[[UIApplication sharedApplication] delegate];
... and always get the same application object. Then, if you have the managed object context defined as a property of the app delegate you can get the context just by:
theManagedObjectContext=appDelegate.managedObjectContext
Add these two lines to every view controller and you can always be sure of getting the app delegate's managed object context.

Resources