Xcode classes can't "see" one another - objective-c

I was working on an Xcode project and everything was going great, until two of my classes stopped recognizing each other. Here is an excerpt:
#import "JBXViewController.h"
#interface ViewController2 : UIViewController {
JBXViewController *jbx;
}
For some reason I get the error "Unknown type name 'JBXViewController'; did you mean 'UIViewController'?" I don't understand how this is possible, since I'm importing the other class just a few lines above. Any insights would be much appreciated!

When you say they "can't see each other", I assume you mean you are also importing ViewController2.h in your JBXViewController.h. So you have one header importing another header, which imports the first header, which imports the second, which imports the first again...
Instead, use a forward reference to JBXViewController:
#class JBXViewController;
#interface ViewController2 : UIViewController {
JBXViewController *jbx;
}
And then #import "JBXViewController.h" in your implementation instead (in your ViewController2.m)

Firoze Lafeer's answer is correct, but this problem is probably a symptom of poor design in your code.
I assume that, in your app, JBXViewController is a parent view controller, and it sometimes shows a ViewController2 for some specific function. (For example, JBXViewController shows a list of records, while ViewController2 edits one of the records.) In a situation like this, ViewController2 should not know the details of JBXViewController. Instead JBXViewController should give ViewController2 the data it needs via ViewController2's properties, and if ViewController2 has to call methods on JBXViewController, they should be part of a delegate protocol.
For example, suppose JBXViewController currently has the following property, and ViewController2 accesses it:
#property (strong) JBXObject * currentObject;
You should instead have a currentObject property on ViewController2, and JBXViewController should set it before showing the view controller:
self.myViewController2.currentObject = self.currentObject;
[self.navigationController pushViewController:self.myViewController2 animated:YES];
This works for one-way communication—JBXViewController can give ViewController2 data. If data needs to flow back up to JBXViewController (other than by changing currentObject's properties), you should set up a delegate for ViewController2. For example:
#protocol ViewController2Delegate; // forward declaration
#interface ViewController2 : UIViewController
#property (weak) id <ViewController2Delegate> delegate;
...
#end
#protocol ViewController2Delegate <NSObject>
- (void)viewController2ShouldSave:(ViewController2*)viewController2;
- (BOOL)viewController2:(ViewController2*)viewController2 shouldAddSomoflange:(JBXSomoflange*)aSomoflange;
#end
Then have JBXViewController conform to the protocol:
#interface JBXViewController : UIViewController <ViewController2Delegate>
Set the delegate, either in Interface Builder or in code like so:
self.myViewController2.delegate = self;
self.myViewController2.currentObject = self.currentObject;
[self.navigationController pushViewController:self.myViewController2 animated:YES];
And implement all the methods listed in ViewController2Delegate.
Together, these changes mean three things:
ViewController2 does not need specific knowledge of how JBXViewController works. This means you no longer have to import JBXViewController.h in ViewController2.h, which solves your immediate problem.
JBXViewController is now more flexible. As long as it sets the appropriate properties in ViewController2 and implements any necessary delegate methods, you can change anything you want in JBXViewController and ViewController2 will never know or care about it.
ViewController2 is now more flexible too. You can use it from other parts of the app, or move it to another app. You can insert a screen between JBXViewController and ViewController2.
These changes aren't necessary to get the app running on your device and functioning the way you intend. But you'll have an easier time down the road if you start adopting these sorts of designs.

Related

Why is this delegate method automatically called in Objective-C?

I'm going through this book called "cocoa programming for mac os x" and I just started with delegates. This whole thing with delegates is still a little bit wacky to me but I think I just need to let it settle.
However there was this one exercise where I should implement a delegate of the main window so that if resized height is always 2xwidth.
So I got 4 files:
AppDelegate.h
AppDelegate.m
WindowDelegate.h
WindowDelegate.m
AppDelegate are just the two standard files that get created when you open a new Cocoa project. I had to look up the solution because I didn't quite know how to accomplish this task.
The solution was just to create a new cocoa class, "WindowDelegat.h/.m" and add this to it's implementation file:
- (NSSize)windowWillResize:(NSWindow *)sender toSize:(NSSize)frameSize {
NSSize newSize = frameSize;
newSize.height = newSize.width * 2;
return newSize;
}
Then I opened the interface builder, added a new object and made it my WindowDelegate. I then had to ctrl drag from the WindowDelegate to the actual window and made it the window's delegate.
Clicked run and it worked. Yay! But why?
First I thought that "windowWillResize" is just one of these callback functions that get's called as soon as the window is resized but it isn't. Normally methods get invoked because the general lifecycle of an program invokes them or because they are an #IBAction, a button or different control elements.
But "windowWillResize" is non of them. So why is it called?
EDIT: Problem solved! Thanks a lot!
Now I'm trying to connect the delegate to the window programmatically. Therefore I deleted the referencing outlet from WindowDelegate to the actual window in interface builder. It works but I just want to verify that this it the correct way how it's done:
AppDelegate.h
#import <Cocoa/Cocoa.h>
#import "WindowDelegate.h"
#interface AppDelegate : NSObject <NSApplicationDelegate>
#end
AppDelegate.m
#import "AppDelegate.h"
#interface AppDelegate ()
#property (weak) IBOutlet NSWindow *window;
#property (strong) WindowDelegate *winDeleg;
#end
#implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
// Insert code here to initialize your application
}
- (void)applicationWillTerminate:(NSNotification *)aNotification {
// Insert code here to tear down your application
}
- (void)awakeFromNib {
[_window setOpaque:NO];
NSColor *transparentColor = [NSColor colorWithDeviceRed:0.0 green:0.0 blue:0.0 alpha:0.5];
[_window setBackgroundColor:transparentColor];
NSSize initialSize = NSMakeSize(100, 200);
[_window setContentSize:initialSize];
_winDeleg = [[WindowDelegate alloc] init];
[_window setDelegate: _winDeleg];
}
#end
WindowDelegate.h
#import <Foundation/Foundation.h>
#import <Cocoa/Cocoa.h>
#interface WindowDelegate : NSObject <NSWindowDelegate>
#end
WindowDelegate.m
#import "WindowDelegate.h"
#implementation WindowDelegate
- (NSSize)windowWillResize:(NSWindow *)sender toSize:(NSSize)frameSize {
NSSize newSize = frameSize;
newSize.height = newSize.width * 2;
return newSize;
}
- (id)init {
self = [super init];
return self;
}
#end
Why does the #property of WindowDelegate need to be strong?
And isn't my winDeleg an object? Why do I have to access it through _winDeleg when it's an object. I though the underscore is used to access variables?
Thank you for your help!
Clicked run and it worked. Yay! But why?
Because instances of NSWindow have a delegate property that can point to any object that implements the NSWindowDelegate protocol, and that protocol includes the -windowWillResize:toSize: method.
Read that a few times. The reason it's important is that you can create your own object, say that it implements NSWindowDelegate, implement -windowWillResize:toSize:, and set that object as your window's delegate. Then, whenever the user resizes the window, your method will be called and can modify the proposed new size.
Normally methods get invoked because the general lifecycle of an program invokes them or because they are an #IBAction, a button or different control elements. But "windowWillResize" is non of them. So why is it called?
This really isn't so different. Think of delegates as "helper objects." They let you customize the behavior of an object without having to create a whole new subclass. The NSWindowDelegate object is essentially a contract that the NSWindow promises to follow: whenever certain things happen, such as the user resizing the window, the window will call certain methods in its delegate object, if the delegate exists and implements those methods. In the case of NSApplication, a lot of those delegate methods are application lifecycle events, like the app starting up or quitting or getting a message from the operating system. In the case of NSWindow, delegate methods correspond to interesting events that can happen to a window, like the user moving it, hiding it, showing it, maximizing it, moving it to a different screen, etc. Other classes, like text views or network connections or movie players, have their own sets of interesting events and their own delegate protocols to match.
Note that methods marked IBAction really aren't delegate methods, they're just methods that get called by objects like controls that use a target/action paradigm. The IBAction keyword lets the IDE know which methods it should present as possible actions for things like buttons. You often find actions in window controllers and view controllers, and those objects frequently act as a delegate for some other object, but the actions themselves aren't part of the delegate protocol. For example, NSTableView takes a delegate object that determines how the table will act and what's displayed in it. It often makes sense for the view controller that manages the table to be the table's delegate, and that same view controller might also manage some buttons and contain the action methods that said buttons trigger, but the actions aren't part of the NSTableViewDelegate protocol and you therefore wouldn't call them delegate methods.

How to share an NSManagedObjectContext between a NSPersistentDocument and view controller?

New to OS X programming. Started with an Xcode template for a document-based app with Core Data.
In my default Document.xib I've created a View which I'm controlling with a custom ViewController. I then created a Managed Object Context in Document.xib and created two outlets, one to Document.h:
#property (strong) IBOutlet NSManagedObjectContext *myManagedObjectContext;
and one to ViewController.h:
#property (weak) IBOutlet NSManagedObjectContext *myManagedObjectContext;
In windowControllerDidLoad in Document.m, I then added self.myManagedObjectContext = [self managedObjectContext].
Following ghostfly's advice (could not locate an NSManagedObjectModel for entity name) I added:
NSLog(#"Context: %#",self.myManagedObjectContext);
NSLog(#"PS Coord : %#",self.myManagedObjectContext.persistentStoreCoordinator);
NSLog(#"MOM : %#", self.myManagedObjectContext.persistentStoreCoordinator.managedObjectModel);
NSLog(#"Entities : %#",[[self.myManagedObjectContext.persistentStoreCoordinator.managedObjectModel entities] valueForKey:#"name"]);
to my Document.m in windowControllerDidLoad and also in my ViewController's awakeFromNib. In Document.m everything seems to work fine: all the NSLog statements look right, and I can also add Entities into my NSManagedObjectContext but in the ViewController, only the first NSLog statement works, and the rest return (null).
My question: What's going wrong here, and am I even going about this the right way?
Various other questions seem to suggest either adding an AppDelegate to MainMenu.xib, but I'm not sure how this would work in practice in a Document-based application (for example, I'd expect each Document to have a separate NSManagedObjectContext, but if I use an AppDelegate, then surely they're all the same?), or even if that's recommended because some tutorials suggest this is explicitly not how to do it (e.g. here http://franck.verrot.fr/blog/2012/01/18/best-way-to-pass-nsmanagedobjectcontext-around-in-ios-applications/). Help is much appreciated! Thanks.
The above answer would create problems when you start to use multiple documents - it would not work as you would be saving just to the last opened document. You shouldn't try to create your own managed object context, because then you have to set up all the stuff for where to store the data etc (which is done already). It's much simpler:
Your main document class should be a subclass of NSPersistentDocument. If not, then you can literally just substitute that instead of NSDocument. The NSPersistentDocument has it's own managedObjectContext and persistentStoreCoordinator that you can use with in the document (to store document related data).
This therefore means each document has it's own managedObjectContext and persistentStoreCoordinator. To access these in other classes, you should pass the reference to the document. E.g. on your view controller subclass, create an #IBOutlet to the document, attach the document in interface builder and then in the constructor, copy the document's managedobjectcontext to it's own pointer in the view controller to use.
Edit: Correct answer is to use MagicalRecord (https://github.com/magicalpanda/magicalrecord) and save yourself a bunch of time!
My previous answer below:
Okay, I think I've got this working, but I'm not sure if this is the "best" or "most idiomatic" way of doing it. I'm going to spell it out in extreme detail in case someone else stumbles across this question and is equally stumped by how all this fits together...
First, forget about the ManagedObjectContext in the .xib; I'm not sure when it's appropriate to use that.
I created an application delegate called AppDelegate. In AppDelegate.h:
#import <Cocoa/Cocoa.h>
#interface AppDelegate : NSObject <NSApplicationDelegate>
#property (retain) NSManagedObjectContext *managedObjectContext;
#property (retain) NSPersistentStoreCoordinator *persistentStoreCoordinator;
#end
Then, in MainMenu.xib, I drag in an NSObject and set its class to AppDelegate and connect the File's Owner's delegate to this object.
In Document.m, in the init method, I add:
AppDelegate *appDelegate = (AppDelegate*)[[NSApplication sharedApplication] delegate];
appDelegate.managedObjectContext = [self managedObjectContext];
appDelegate.persistentStoreCoordinator = [self managedObjectContext].persistentStoreCoordinator;
Note that I originally tried putting it in the windowControllerDidLoadNib method but this didn't work, because it seems like this is being called after my ViewController's awakeFromNib method.
Then, in my ViewController.m in awakeFromNib I can access the context by, e.g.:
AppDelegate *appDelegate = (AppDelegate*)[[NSApplication sharedApplication] delegate];
MyEntity *test = [NSEntityDescription insertNewObjectForEntityForName:#"MyEntity" inManagedObjectContext:[appDelegate managedObjectContext]];
I'll accept this answer if I don't find anything wrong with it over the next few days.
An open question is what will happen when I have multiple Documents open. Will they all share the same context?

Dismissing view using delegation not working

I am trying to use delegation, which I am new at, to dismiss a modally presented view. I am trying to get it to work along the lines of the apple documentation seen here. So far my code is as follows:
Put both views on storyboard, connect first to second view with modal segue. (the segue to view2 works fine)
create delegate inside second viewcontroller/create method to call when returned:
//inside of view2ViewController.h
#class view2ViewController;
#protocol view2ViewControllerDelegate <NSObject>
-(void)goBack:(OptionsViewController *)controller;
#end
#interface OptionsViewController : UIViewController
#property (nonatomic, weak) id <view2ViewControllerDelegate>delegate;
- (IBAction)return:(id)sender;//connected to button
#end
implement delegate in view1ViewController #interface view1ViewController : UIViewController <view2ViewControllerDelegate>
write code for delegate method goBack in view1Controller.m
-(void)goBack:(view2ViewController *)controller{
[self dismissViewControllerAnimated:YES completion:nil];}
finish by writing code for return method in view2ViewController.m
- (IBAction)return:(id)sender {
[self.delegate goBack:self];}
I'm not sure where this code is going wrong. The return method is called, but then goBack isn't. I did read the developer documentation, and thought I understood, but I guess not...
PS I change the names of all of my class/variable names on StackOverflow to be more generic, so if there is a slight discrepancy between variable name spellings, it's probably because i typed one wrong.
The best shot I can try -
Make sure you assigned the SplashViewController as the delegate of the view2ViewController.
By code you can do it like that (in the SplashViewController m file):
view2ViewController.delegate = self;
Or you can do to on Story board.
BTW
I a not sure calling your function "return" is a good idea.

Xcode (storyboards and segues): why delegates instead of references?

I have read the following tutorial regarding storyboard.
Basically the sample App created in this tutorial let the user navigate between various views and it's created using segue.
In order to navigate between views the tutorial say to create two UITableViewController and when "going" from one to another to specify a delegate:
First controller:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"AddPlayer"])
{
UINavigationController *navigationController = segue.destinationViewController;
PlayerDetailsViewController *playerDetailsViewController = [[navigationController viewControllers] objectAtIndex:0];
playerDetailsViewController.delegate = self;
}
}
Second controller:
#protocol PlayerDetailsViewControllerDelegate <NSObject>
- (void)playerDetailsViewControllerDidCancel: (PlayerDetailsViewController *)controller;
- (void)playerDetailsViewController: (PlayerDetailsViewController *)controller didAddPlayer:(Player *)player;
#end
#interface PlayerDetailsViewController : UITableViewController
#property (nonatomic, weak) id <PlayerDetailsViewControllerDelegate> delegate;
When "going back":
- (IBAction)cancel:(id)sender
{
[self.delegate playerDetailsViewControllerDidCancel:self];
}
My simple question is why this complication? Why use delegates and protocols?
I have changed the code using a "Java" style and now I'm passing to the second controller a reference to the first one, everything is working.
First controller:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
...
playerDetailsViewController.playerViewController = self;
}
Second controller:
#property (strong, readwrite) PlayerViewController *playerViewController;
So, what are the benefits to use delegates instead of simply passing references between ViewControllers?
Thanks!
Guido
Several reasons:
As Leonardo says, using references you couple the two view controllers together unnecessarily. You should just pass the data that's required and not the whole class
This is just how Objective-C apps tend to be constructed. By using a different method you'd be making your app harder to understand by seasoned developers
(You don't always need the delegate in your second class -- for example when it's display only -- so your example code is more complex than is often the case)
Related to the last point, your code is already harder than it needs to be. UIStoryboardSegue has properties pointing to the source and destination view controllers; there's no need to mess with the navigation controller.
To put in a Java manner, if you use a strong type you are tied to a single class.
Instead the delegate is in the end a class conform to a protocol.
So you could pass many class as delegates to playerDetail, as long as they are conform to the #protocol.
Is like casting and passing interface instead of concrete class in java.
You may well know the List interface and all the ArrayList, LinkedList... etc concrete implementations.
One thing I don't understand is why they get destination controller by passing trough the navigation. I always used:
MyDestinationViewController *dvc = [segue destinationViewController];
so that you can use in many situation where you do not have a navigation controller.

PreferencesViewControllerDelegate

I found this in some example code I downloaded
#import "PreferencesViewController.h"
#class MetronomeView;
#interface MetronomeViewController : UIViewController <PreferencesViewControllerDelegate> {
MetronomeView *metronomeView;
}
#property (nonatomic, assign) IBOutlet MetronomeView *metronomeView;
- (IBAction)showInfo;
#end
I tried to find information on PreferencesViewControllerDelegate, but didn't find much. What is this delegate?
It's a custom view controller delegate protocol that is created by Apple, for use in the Metronome sample project (and I imagine others). The protocol declaration can be found here, and how it's implemented can be seen here.
All it does is act as a delegate that monitors what happens to PreferencesViewController, the controller that manages the preferences view.
The protocol contains one method called preferencesViewControllerDidFinish:, which is implemented by MetronomeViewController like this. When the delegate receives a signal that the preferences view has been dismissed with the Done button, this is called to hide the view:
- (void)preferencesViewControllerDidFinish:(PreferencesViewController *)controller {
[self dismissModalViewControllerAnimated:YES];
}
A similar delegate called FlipsideViewControllerDelegate can be found in the Xcode iOS project template for a Utility Application.
This looks like a custom delegate created by whoever wrote the code you downloaded. It's not an Apple delegate, since it doesn't start with any of Apple's two-letter prefixes.