Closing panel in one source file from a separate source file - objective-c

This is what I got so far.
- (IBAction)HUDPanelDictionaryHide:(id)sender{
extern NSPanel HUDPanelDictionary;
[HUDPanelDictionary close];
}
This obviously does NOT work.
The HUDPanelDictionary is declared in a separate .h and .m file for an .xib file. I need to close this panel from the other .h and .m file for another .xib file. Sorry I'm being so vague!
Any ideas??
Elijah

You need to #import the header containing the declaration of HUDPanelDictionary.
For example:
#import "HUDPanelDictionary.h"
#interface MyController
- (IBAction)hideDictionaryPanel:(id)sender {
[HUDPanelDictionary close];
}
#end
I would also name things differently, for example "DictionaryHUD" rather than "HUDPanelDictionary." "Panel" is redundant with "HUD," and you should care more about its intent than its position in the class hierarchy.
Another thing I would do is make DictionaryHUD an NSWindowController subclass, and have it expose a singleton shared instance rather than use a global variable to point to the panel itself. Then the code above would look like this:
#import "DictionaryController.h"
#interface MyController
- (IBAction)hideDictionaryPanel:(id)sender {
[[DictionaryController sharedDictionaryController] hideDictionaryPanel:sender];
}
#end
This puts the primary responsibility for your dictionary panel/HUD on an instance of a single controller class, to which other controllers (say one that manages your main window's toolbar) can forward their interactions. You could even put the dictionary HUD window controller in the responder chain to have it automatically handle actions like -hideDictionaryPanel: so nothing needs to do that kind of forwarding.

Related

Why are IBOutlet Properties declared in a class extension?

When I create a new single view application, the UIViewController file that is generated has an extension (I think it's an extension) in the implementation file.
//
// ViewController.m
//
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
//...
#end
And the convention seems to be that IBOutlets are declared as properties inside this extension. Why is this? What kind of behavior does this result in that is different from declaring it in the header file interface?
Properties in the header files are public, property declarations in the implementation files are private. It's up to you to choose. You can connect them directly from the interface builder into the desired file.
I can't understand your concern about extension - this code is fine. Could you explain this? (i can't comment - too less reputation, sorry)

Calling a method from another class WITHOUT creating a new instance

This is a common topic but, in my case there is one thing I don't understand that I can't find explained in the other asked questions.
Here is the gist of what I'm trying to do:
User clicks a button and something like this is called:
#implementation FirstClass
-(void)clickedButton
{
[SecondClass changeText];
}
And then in SecondClass is:
#implementation SecondClass
- (void)changeText {
[myLabel setText:#"text"];
}
So when the user clicks the button, the text property in myLabel in SecondClass changes to "text".
The only problem I have with this is calling [SecondClass changeText] on the existing instance of SecondClass. Since I'm not initializing the CCNodes programmatically (they are all automatically loaded upon running the app), I don't know where or how SecondClass is initialized. I'm using SpriteBuilder to build this project.
Any help would be appreciated. Thanks!
So, you have two instaces -- one with a button, and one with a label. I'm assuming they are both descendants of NSViewController or otherwise manage underlying views.
The problem is, you found no way to address second instance containing label from the method of first instance.
You need to define a property in first instance's class:
#property(weak) SecondClass *secondInstance;
And then in button clicked method:
-(void)clickedButton
{
[self.secondInstance changeText];
}
There is one issue left: who is responsible to set first instance's property that we defined? This depends on who did create both of them, probably just app delegate or enclosing controller, you know that better.
UPD: If both of the controllers are created by AppDelegate:
#import "FirstClass.h"
#import "SecondClass.h"
#interface AppDelegate ()
// case A - manual
#property(strong) FirstClass *firstInstance;
#property(strong) SecondClass *secondInstance;
// case B - declared in xib
//#property(weak) IBOutlet FirstClass *firstInstance;
//#property(weak) IBOutlet SecondClass *secondInstance;
#end
#implementation AppDelegate
...
- (void)applicationDidFinishLaunching:(NSNotification *)notification
{
// Create them
self.firstInstance = [[FirstClass alloc] init...];
self.secondInstance = [[SecondClass alloc] init...];
// Or maybe they are declared in MainMenu.xib, then you do not create them
// by hand, but must have outlets for both. See case B above.
// Connect them
self.firstInstance.secondInstance = self.secondInstance;
...
}
Note that class is not the same as an object (instance). Class is a named collection of methods, mostly for the instance. In Objective-C, class is not just a name, but an object too, so you can call a method on it (i.e. send an message to the class object). But here we always talk about objects (instances), so forget about classes – we hold objects via strong properties or weak outlets, depending on how they were created, and operate on objects, never on classes.
In objective C, the methods are either instance methods or class methods. As the name suggests, the instance methods require an instance of the class to work, whereas the class methods can be used with just the name of the class. What you need here is a class method. Just change the following line in your code:
#implementation SecondClass
- (id)changeText {
to
#implementation SecondClass
+ (id)changeText {
This will change the method from an instance method to a class method.

Moving functions to separate classes

I have been building an iOS app and one of my ViewControllers had become full of functions like:
CGPoint randomPoint()
{
//Code goes here
}
I now want to move them to class A (or protocol, I am not sure what will work for me), import it in the VC and call the just like before:
p=randomPoint(); //NOT A.randomPoint(), [A randomPoint] or whatever
I tried using the C++ class template, but it has problems with CGPoint, CGRect and so long.
How can I accomplish that?
If you are wondering were to put C functions like the one you are describing, the best practice is to move them into a separate .h file that has a meaningful name. For instance MyGeometry.h
Make sure you give a descriptive name to your function, such as:
static inline CGPoint CGPointMakeRandom() {
// your code
return point;
}
You could make a separate objective c class with class methods.
In the header file declare the method like this (let's say you want to call it
#import <UIKit/UIKit.h>
#interface pointHelper : UIViewController
+(CGPoint) randomPoint;
And then in the .m file
#implementation pointHelper
+(CGPoint) randomPoint{
//// implementation
}
When you want to evoke the method in another file.
#import "pointerHelper.h"
You will then be able to access the method like this...
CGPoint thePoint = [pointHelper randomPoint];
Or if you had an object of the class..
CGPoint thePoint = [[pointHelperObject class] randomPoint];
This is a better way of doing it, since it makes your code much clearer. [pointHelper randomPoint] tells you why you are evoking the method and what it is doing. You are using a class that has utilities for points, and you are using it to grab a random point. You don't need an object to evoke this method, because it is controlled abstractly by the class. Just be careful not to try to access properties of the class within the class method.

Category header imports not working

I have a workspace that has two projects in it. The first project was essentially a test and develop project where I got things working before worrying about tying everything together for real. The second project is bringing all my individually developed view controllers together in a storyboard.
On one of the view controllers I have a bunch of swipe gestures with quite a bit of UIView animation calls nicely formatted for readability and therefore taking a lot of space. I elected to move them out as a category.
The problem is that the compiler is not seeing the instance variable declarations in the main header file.
What has me pulling my hair out is that I did this in the first project and it all worked fine. So I'm carefully comparing the contents of my second project to the first and I see no differences.
Here're some file snippets to help demonstrate how/where I'm defining things, and then snippets of code in the category file that is attempting to access them:
GSBViewController.h
#interface GSBViewController : UIViewController
#property (strong, nonatomic) IBOutlet UISegmentedControl *roundPicker;
#property (strong, nonatomic) IBOutlet UIView *roundsSectionView;
GSBViewController.m
#import "GSBViewController+Swipe.h"
#interface GSBGameBuilderViewController ()
{
UIBarButtonItem *rightGatherBarButton;
NSInteger previousRound;
}
#end
#implementation GSBViewController
#synthesize roundPicker;
#synthesize roundsSectionView;
GSBViewController+Swipe.h
#import "GSBViewController.h"
#interface GSBViewController (Swipe)
- (void)establishSwipeGestures;
#end
GSBViewController+Swipe.m
#import "GSBViewController+Swipe.h"
#implementation GSBViewController (Swipe)
- (void)establishSwipeGestures
{
UISwipeGestureRecognizer *swipeLeft =
[[UISwipeGestureRecognizer alloc]
initWithTarget:self
action:#selector(roundsSectionLeft:)];
[swipeLeft setDirection:UISwipeGestureRecognizerDirectionLeft];
[swipeLeft setNumberOfTouchesRequired:1];
[roundsSectionView addGestureRecognizer:swipeLeft];
// bunch-o-code snipped -- for the time being it's actually all commented out
// as a test and because the LLVM compiler was giving up after too many errors
// and I wanted to see if there was more it would like to tell me about this first --
// and very representative -- problem.
}
#end
The complaint from the compiler is "Use of undeclared identifier 'roundsSectionView'"
If I option-click on the use of roundsSectionView in that line of code where I'm adding the gesture recognizer to it the pop-up correctly describes it as declared in GSBViewController.h
So I'm stumped.
Is there something I can do in Xcode (4.3.2 at the time of this posting :-) to let me see what the included files are? Or is there something non-file-based that is needed to tie a category into the class it's augmenting? I don't remember anything like that being necessary before. In fact, the way I generated the files for this category was through Xcode's File -> New File... Objective-C Category template. Then I just copied the contents of the old ...+Swipe.h and ...+Swipe.m files and pasted them into their respective files in the new project.
A synthesized ivar is private. The compiler won't allow you to access it anywhere execept in the #implementation block where it's created. Neither categories nor subclasses can access the ivar directly; they must use the property: [self roundsSectionView].
There's a slight possibilty that earlier Clangs didn't make synthesized ivars private. Either that or you weren't really doing exactly the same thing in the earlier project.
What #Jacques Cousteau says is correct.
Since you just defined a property and no backing ivar, the category won't be able to access it. If you use self.roundsSectionView it will use the getter method generated for the property and hence it will work.
Or you could define a backing variable in your interface
#interface GSBViewController : UIViewController
{
UIBarButtonItem *roundsSectionView;
}
In this case the categories will be able to access the variable. But not any other class.

Why is applicationWillFinishLaunching in my NSApplicationDelegate class never called?

main.m
#import <Cocoa/Cocoa.h>
int main(int argc, char *argv[])
{
return NSApplicationMain(argc, (const char **) argv);
}
CoolClass.h
#import <Cocoa/Cocoa.h>
#interface CoolClass : NSObject <NSApplicationDelegate> {
}
- (void) applicationDidFinishLaunching : (NSNotification *) aNotification;
#end
CoolClass.m
#import "CoolClass.h"
#implementation CoolClass
- (void) applicationDidFinishLaunching : (NSNotification *) aNotification {
NSLog(#"THIS SHOULD BE PRINTED TO THE DEBUG CONSOLE");
}
#end
I tried this with "applicationWillFinishLaunching" as well, but still no luck. Any help at all would be appreciated. Thanks.
What you're missing is that adopting the protocol makes objects of kind CoolClass ready to function as delegates of any NSApplication object (provided you follow through on the declaration and implement all required methods of the protocol). Declaring conformance to the protocol also prevents compiler warnings when you set instances of the class as an application's delegate.
But for a specific application object (say, the shared NSApplication object that Cocoa creates for you) to know to send messages from the protocol to a specific CoolClass object, you must set the object you want to receive those messages as the specific application object's delegate.
What this means is that some time before the messages you want to receive would be sent by the application, something needs to instantiate a CoolClass object - call it c - and tell the application, "Hey, your delegate is c over here, so send delegate messages to the little feller from now on."
What that boils down to is that these lines of code must execute before the application finishes launching:
CoolClass *c = [[CoolClass alloc] init];
[[NSApplication sharedApplication] setDelegate:c];
The easiest way to have this happen is to let Interface Builder do the work for you: let the MainMenu nib instantiate your CoolClass and also set the cool class object as the application's delegate when the nib is loaded, as others have suggested.
To do so, open MainMenu.xib. Drag a Custom Object into the xib and change its class to CoolClass in the inspector. Ctrl-drag (or right-click drag) from the application object in the xib to the CoolClass object and choose "delegate". Save, build, and run.
You should define your CoolClass as applications delegate in Interface Builder (Ctrl+Drag from App instance to your CoolClass instance
applicationDidFinishLaunching is an instance method, not a class method. That is, you'll need an instantiation of your class to receive that message. Plus, it can't be just -any- instantiation; your application needs to know about your instantiation and know that it's supposed to send delegate messages to it. The easiest and most common way to do this is...
First, you'll instantiate your CoolClass. Open your application's MainMenu.nib file in Interface Builder. Drag an "Object" (it'll look like a blue cube) out of the Library window. Select it and use the Identity tab of the Inspector to change its class from NSObject to CoolClass. Now, you have an instance of your CoolClass.
Now, you'll set that instance as the application's delegate. Control-drag from "Application" (still in Interface Builder) to your new instance of CoolClass. A window will pop up (showing outlets of Application that could be connected to your object). Choose "delegate". Now your application has an instance of your CoolClass set as its delegate, and thus, your applicationDidFinishLaunching will run.
I appreciate you may be trying to learn from scratch, but why did you not just create a new project using one of the XCode templates? It sets all this up for you to begin with. Life involves enough debugging without having to add more atop it!
To start at the beginning: your call to NSApplicationMain should be wrapped in an NSAutoreleasePool. You will be in trouble if you don't do that.