my app runs on iPhone device and also in simulator. Everythings seems fine, but i see a compiler warning during build. I hate to deliver code thats not completely correct so i need to get rid of this warning. The compiler warning is:
newsReaderController.m:24: warning: '-managedObjectContext' not found in protocol(s)
The Code is:
- (void)viewDidLoad {
[super viewDidLoad];
//CORE DATA
if (managedObjectContext == nil) {
managedObjectContext = [[[UIApplication sharedApplication] delegate] managedObjectContext];
}
}
The managedObjectContext for CoreData operation is set up in App Delegate. Core Data Framework is importet and the app works like a charm.
any hint for me ? I'm working with objective-C for some weeks now but there seems to be something new to learn every day :)
Since -[UIApplication delegate] returns an object of type id<UIApplicationDelegate>, the compiler is complaining that no -managedObjectContext method exists in that one protocol. It's there, and you know it's there, so you can solve this issue by casting to your delegate's specific type (MyAppDelegate or whatever it may be called), or by casting to id:
id appDelegate = (id)[[UIApplication sharedApplication] delegate];
managedObjectContext = [appDelegate managedObjectContext];
Related
I've got plenty of experience with iOS, but Cocoa has me a bit confused. I read through several Apple docs on Cocoa but there are still details that I could not find anywhere. It seems the documentation was written before the NSDocument-based Xcode template was updated to use NSViewController, so I am not clear on how exactly I should organize my application. The template creates a storyboard with an NSWindow, NSViewController.
My understanding is that I should probably subclass NSWindowController or NSWindow to have a reference to my model object, and set that in makeWindowControllers(). But if I'd like to make use of the NSViewController instead of just putting everything in the window, I would also need to access my model there somehow too. I notice there is something called a representedObject in my view controller which seems like it's meant to hold some model object (to then be cast), but it's always nil. How does this get set?
I'm finding it hard to properly formulate this question, but I guess what I'm asking is:how do I properly use NSViewController in my document-based application?
PS: I understand that NSWindowController is generally meant to managing multiple windows that act on one document, so presumably if I only need one window then I don't need an NSWindowController. However, requirements might change and having using NSWindowController may be better in the long run, right?
I haven't dived into storyboards but here is how it works:
If your app has to support 10.9 and lower create custom of subclass NSWindowController
Put code like this into NSDocument subclass
- (void)makeWindowControllers
{
CustomWindowController *controller = [[CustomWindowController alloc] init];
[self addWindowController:controller];
}
If your app has multiple windows than add them here or somewhere else (loaded on demand) but do not forget to add it to array of document windowscontroller (addWindowController:)
If you create them but you don't want to show all the windows then override
- (void)showWindows
{
[controller showWindow:nil]
}
You can anytime access you model in your window controller
- (CustomDocument *)document
{
return [self document];
}
Use bindings in your window controller (windowcontroller subclass + document in the keypath which is a property of window controller)
[self.textView bind:#"editable"
toObject:self withKeyPath:#"document.readOnly"
options:#{NSValueTransformerNameBindingOption : NSNegateBooleanTransformerName}];
In contrast to iOS most of the views are on screen so you have to rely on patterns: Delegation, Notification, Events (responder chain) and of course MVC.
10.10 Yosemite Changes:
NSViewController starting from 10.10 is automatically added to responder chain (generally target of the action is unknown | NSApp sendAction:to:from:)
and all the delegates such as viewDidLoad... familiar from iOS are finally implemented. This means that I don't see big benefit of subclassing NSWindowCotroller anymore.
NSDocument subclass is mandatory and NSViewController is sufficient.
You can anytime access you data in your view controller
- (CustomDocument *)document
{
return (CustomDocument *)[[NSDocumentController sharedDocumentController] documentForWindow:[[self view] window]];
//doesn't work if you do template approach
//NSWindowController *controller = [[[self view] window] windowController];
//CustomDocument *document = [controller document];
}
If you do like this (conforming to KVC/KVO) you can do binding as written above.
Tips:
Correctly implement UNDO for your model objects in Document e.g. or shamefully call updateChangeCount:
[[self.undoManager prepareWithInvocationTarget:self] deleteRowsAtIndexes:insertedIndexes];
Do not put code related to views/windows into your Document
Split your app into multiple NSViewControllers e.g.
- (void)prepareForSegue:(NSStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:AAPLListWindowControllerShowAddItemViewControllerSegueIdentifier]) {
AAPLListViewController *listViewController = (AAPLListViewController *)self.window.contentViewController;
AAPLAddItemViewController *addItemViewController = segue.destinationController;
addItemViewController.delegate = listViewController;
}
}
Previous code is called on windowcontroller with viewcontroller as delegate (again possible only after 10.10)
I always prefer to use multiple XIBs rather than one giant storyboard/XIB. Use following subclass of NSViewController and always inherit from it:
#import <Cocoa/Cocoa.h>
#interface MyViewController : NSViewController
#property(strong) IBOutlet NSView *viewToSubstitute;
#end
#import "MyViewController.h"
#interface MyViewController ()
#end
#implementation MyViewController
- (void)awakeFromNib
{
NSView *view = [self viewToSubstitute];
if (view) {
[self setViewToSubstitute:nil];
[[self view] setFrame:[view frame]];
[[self view] setAutoresizingMask:[view autoresizingMask]];
[[view superview] replaceSubview:view with:[self view]];
}
}
#end
Add a subclass of MyViewController to the project with XIB. Rename the XIB
Add NSViewController Object to the XIB and change its subclass name
Change the loading XIB name to name from step 1
Link view to substitute to the view you want to replace
Check example project Example Multi XIB project
Inspire yourself by shapeart or lister or TextEdit
And a real guide is to use Hopper and see how other apps are done.
PS: You can add your views/viewcontroller into responder chain manually.
PS2: If you are beginner don't over-architect. Be happy with the fact that your app works.
I'm relatively new to this myself but hopefully I can add a little insight.
You can use the view controllers much as you would in ios. You can set outlets and targets and such. For NSDocument-based apps you can use a view controller or the window controller but I think for most applications you'll end up using both with most of the logic being in the view controller. Put the logic wherever it makes the most sense. For example, if your nsdocument can have multiple window types then use the view controller for logic specific to each type and the window controller for logic that applies to all the types.
The representedObject property is primarily associated with Cocoa bindings. While I am beginning to become familiar with bindings I don't have enough background to go into detail here. But a search through the bindings programming guide might be helpful. In general bindings can take the place of a lot of data source code you would need to write on ios. When it works it's magical. When it doesn't work it's like debugging magic. It can be a challenge to see where things went wrong.
Let me add a simple copy-pastable sample for the short answer category;
In your NSDocument subclass, send self to the represented object of your view controller when you are called to makeWindowControllers:
- (void) makeWindowControllers
{
NSStoryboard* storyboard = [NSStoryboard storyboardWithName: #"My Story Board" bundle: nil];
NSWindowController* windowController = [storyboard instantiateControllerWithIdentifier: #"My Document Window Controller"];
MyViewController* myController = (id) windowController.contentViewController;
[self addWindowController: windowController];
myController.representedObject = self;
}
In you MyViewController subclass of NSViewController, overwrite setRepresentedObject to trap it's value, send it to super and then make a call to refresh your view:
- (void) setRepresentedObject: (id) representedObject
{
super.representedObject = representedObject;
[self myUpdateWindowUIFromContent];
}
Merci, bonsoir, you're done.
I have the following code in an app (which was written in 2011).
[[[[NSApp delegate] myWindowController] ...] ..];
Since upgrading to Xcode 6.1 This produces the following error:-
ARC Semantic Issue No known instance method for selector 'myWindowController'
Replacing this by the following generates no error.
id ttt = [NSApp delegate];
[[[ttt myWindowController] ...] ...];
PS Xcode seems to think the type is 'id<NSFileManagerDelegate>'
What is going on here?
I admit to being very rusty with Cocoa and Xcode. I am sure I could fix it by an appropriate cast, but this seems unnecessary, and I am attempting to understand why.
Further information
My AppDelegate.h
IBOutlet MyWindowController *myWindowController;
And AppDelegate.m
#synthesize myWindowController;
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
myWindowController = [[MyWindowController alloc] initWithWindowNibName:#"MainWindow"];
I resolved this with a cast (MyAppDelegate *).
[[[(MyAppDelegate *)[NSApp delegate] myWindowController] currentTvc] saveTableColumns]; // Xcode 6.1 error
I have concluded this is an Xcode 6.1 error as it seems to think [NSApp delegate] returns type id<NSFileManagerDelegate>
Make sure you are importing the header containing the MyWindowController #interface definition.
Cast your [NSApp delegate] with (MyWindowController *).
So your original line:
[[[[NSApp delegate] myWindowController] ...] ..];
should become:
[[[(MyAppDelegate *)[NSApp delegate] myWindowController] ...] ..];
You could also cast with (id), which is effectively what you are doing with the ttt variable, but that's cheating a little. Using the proper class when casting will give you better compiler checking as well as helping Xcode make accurate autocomplete suggestions (which is a good way to detect errors before they happen). Basically an object declared or cast as id means it can be an object of any class so any method that is defined in any class will be considered valid.
I'm having the same issue with Xcode 6.1 myself. I think the compiler changed somehow in Xcode 6.1. I wish I had an answer for why, myself. It doesn't instill a lot of confidence in Xcode 6.1.
I am having a really weird issue when testing on my 1st gen. iPad (running iOS 5).
I have a UIView that I use as a property (with retain). I nil the property in the parent view's dealloc method. Pretty basic stuff. It works perfect on my iPad 3 running iOS 6, but doesn't get released on my 1st gen.
Any ideas what might be going on?
I'm not using ARC.
If you're retaining it, you have to release it. You can't just nil the instance variable.
So if you're property looks like this:
#property (nonatomic, retain) UIView *myView;
You're dealloc would either look like this:
- (void)dealloc
{
[myView release], myView = nil;
[super dealloc];
}
Or this:
- (void)dealloc
{
[self setMyView:nil];
[super dealloc];
}
Or this:
- (void)dealloc
{
self.myView = nil;
[super dealloc];
}
And your property will properly get released--unless something else is retaining it.
So I figured this out. It seems to be a bug in the iOS 6 SDK or maybe I just don't understand it. I have a UIViewController that presents another vc via presentViewController:animated:completion: —If I dismiss the presented vc then it releases and subsequently all subviews are removed and all is well.
However, if while the presented vc is showing, I remove/destroy the parent vc, the presented vc is deallocated but, its subviews are not told to removeFromSuperview; This doesn't show up as a leak in instruments, BUT it does prevent the subviews from deallocating.
This does not happen on iOS 6, thus I suspect this is a bug in iOS 5. Everything releases/deallocates as one would expect on iOS 6.
If someone has an explanation, or a better understanding of this, I would love to reward the answer to them instead of myself.
A view controller isn't responsible for removing its view from the superview when the view controller is dealloc'ed. The view controller is just responsible for releasing its own reference to it.
For example: you can create a view controller, ask for its view, then add that view to another view and throw away the view controller. In that case, you're just using the view controller as a view builder.
I'm not sure why the behavior is different in iOS 6, but would love to know.
I have just started developing iPad apps and I'm struggling with an issue for too long now, so I've decided to go for help.
I have an application for iPad using storyboard and started as a Tabbed application using CoreData. So the issue is, my NSManagedObjectContext starts with a value but when I move to another tab, the managedObjectContext becomes null.
Don't know what to do. Some help would be greatly appreciated.
Thanks
Elkucho
I'm slightly confused...
self.window.rootViewController
should return your instance of UITabBarController, which should not respond to setManagedObjectContext: and therefore should crash.
With this in mind what you need to do it
Get the tabBarController
Cycle through the viewController's that the tabBarController manages
Pass them the managedObjectContext
for (id viewController in self.window.rootViewController.viewControllers) {
[viewController setManagedObjectContext:self.managedObjectContext];
}
Edit
I've taken a quick look.
You rarely need to subclass UITabBarController and you don't really need to in this case.
What you want to do is just get the managedObjectContext to each of the viewControllers in the tabBarController, the tabBarController itself does need to know about it.
I changed your application:didFinishLaunchingWithOptions: to the following and it worked the way it was supposed to
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
UITabBarController *tabBarController = (id)self.window.rootViewController;
for (id viewController in tabBarController.viewControllers) {
[viewController setManagedObjectContext:self.managedObjectContext];
}
}
I'm working on an app that uses a navigation controller. When I build, I get no errors or warnings. I've defined a method myMethod in FirstViewController.m file, then in viewDidLoad I call the method with [self myMethod];.
When I built and ran from the Console, I got nothing. So I put NSLog() statements like so:
-(void) viewDidLoad {
NSLog(#"Entering viewDidLoad");
[self myMethod];
NSLog(#"Called myMethod");
[super viewDidLoad];
}
The Console never returns the second NSLog statement, leading me to believe that the app crashes while calling the method.
I've tried this with the simulator set to iOS 4.0.1 as well as iOS 4.1. I'm building against the iOS 4.1 SDK. I really appreciate any help you can offer.
Update: I ran the Debugger, which seems to show the problem being in myMethod. I only make it through a few lines of code in that method before I receive "EXC_BAD_ACCESS". The lines of code are:
-(void)myMethod {
NSMutableArray *someStuff;
NSMutableArray *someMoreStuff;
stuffSections=[[NSMutableArray alloc] initWithObjects:#"Some Stuff",#"Some More Stuff",nil];
someStuff=[[NSMutableArray alloc] init];
someMoreStuff=[[NSMutableArray alloc] init];
[someStuff addObject:[[NSMutableDictionary alloc]initWithObjectsAndKeys:#"Item Name",#"name",#"Item Picture.png",#"picture",#"http://theurl.net/",#"url",#"1",#"itemCode",nil]];
But it appears that I can't get past this first attempt to add something to the someStuff array.
Looks like there is a problem in "myMethod". If I had to guess, I would say you are trying to reference an object that was auto-released and not retained.
You're missing the alloc when initializing someStuff and someMoreStuff. It should be
someStuff = [[NSMutableArray alloc] init];
someMoreStuff = [[NSMutableArray alloc] init];
Have you cross checked the two MutableArrays? Do they have been allocated successfully? You get "EXC_BAD_ACCESS" when the app got into a corrupted state that is usually due to a memory management issue. I guess the arrays are not allocated.
Try allocating this way.
NSMutableArray * someStuff = [[NSMutableArray alloc]initWithCapacity:3];
NSMutableArray * someMoreStuff = [[NSMutableArray alloc]initWithCapacity:3];
add a breakpoint there are check in the debug area and confirm whether they are being allocated successfully. If there is a memory to each of these array, then they are allocated.