I have just started working on my first cocos2d ios app.
I am very used to creating games in Game Maker, in which everything is simpler, and would like some help on creating separate .m/.h class files that contains functions that will affect all instances of a specific CCSprite. Obviously different class files for different CCSprites are needed.
In game maker, objects have code applied to them, and when i want something to happen when an instance is created its pretty easy, by just adding code to the create event.
In xcode i can't think how to do this.
One way to go would be to subclass CCSprite. Check out this guide for more info:
http://www.cocos2d-iphone.org/wiki/doku.php/prog_guide:sprites
Separate classes may also not be necessary, consider just having different initiators. Here is an example of a CCSprite subclass that can make both minions and evil rabbits:
BadGuySprite *minion = [[BadGuySprite alloc] initAMinion];
BadGuySprite *evilRabbit = [[BadGuySprite alloc] initAEvilRabbit];
BadGuySprite.h
#import "cocosd.h"
#interface BadGuySprite: CCSprite
{
int lifebar;
}
+(id) initAMinion;
+(id) initAEvilRabbit;
#end
BadGuySprite.m
#import "BadGuySprite.h"
#implementation BadGuySprite
- (id)initAMinion{
self = [CCSprite spriteWithFile:#"minion.png"];
lifebar = 1000;
return self;
}
- (id)initAEvilRabbit{
self = [CCSprite spriteWithFile:#"rabbit.png"];
lifebar = 1;
return self;
}
#end
Related
I'm new to Xcode and objective c. I want to create a button (probably a UIBarButtonItem, for a navigation bar) with a particular appearance, which I will use repeatedly in different views. I've searched at length but can't figure out how.
Would it be appropriate to subclass UIBarButtonItem? I tried to do that, but I was quickly in over my head. Once I create the .h and .m files as a subclass of UIBarButtonItem, do I then have to instantiate a UIBarButtonItem? Do those files not automatically create a button object for me (imported from the parent class), which I can refer to as self? It seems like it would be weird to instantiate a button within its own subclass.
One thing I want to do is add the line,
button.titleLabel.lineBreakMode = NSLineBreakByWordWrapping;
but I'm at a loss as to how to create reusable button with that property.
Even if that is completely the wrong approach to creating a reusable custom button, I clearly need to improve my understanding of objects, so explanation of my misunderstandings would be much appreciated!
Please?
You can do this without subclassing - by making a category (a preferred way of doing things in Objective-C). With a category you can provide custom methods for an object without having to subclass it. You can't (easily) provide custom properties, but in your case this is not relevant.
Using a Category
This is how your category header file could look:
// UIButton+StyledButton.h
#import <UIKit/UIKit.h>
#interface UIButton (StyledButton)
- (void) styleButton;
#end
Then in the implementation file:
//
// UIButton+StyledButton.m
//
#import "UIButton+StyledButton.h"
#implementation UIButton (StyledButton)
- (void) styleButton {
//style your button properties here
self.titleLabel.lineBreakMode = NSLineBreakByWordWrapping;
}
('self' refers to the button object, which also acquires the custom methods you write in the category.)
To use it, #import "UIButton+StyledButton.h" then you can do this sort of thing...
on viewDidLoad {
[super viewDidLoad];
UIButton* myButton = [[UIButton alloc] initWithFrame:myFrame];
[myButton styleButton];
}
Using a Subclass
The subclassed equivalent would look something like this:
The header file...
// MyCustomButton.h
#import <UIKit/UIKit.h>
#interface MyCustomButton : UIButton
- (id)initWithCoder:(NSCoder *)coder;
- (id)initWithFrame:(CGRect)frame;
#end
The implementation file...
// MyCustomButton.m
#import "MyCustomButton.h"
#implementation MyCustomButton
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self styleButton];
}
return self;
}
- (id)initWithCoder:(NSCoder *)coder
{
self = [super initWithCoder:coder];
if (self) {
[self styleButton];
}
return self;
}
- (void) styleButton {
//style your button properties here
self.titleLabel.lineBreakMode = NSLineBreakByWordWrapping;
}
You provide two init methods - initWithFrame is the method to call when alloc/initing the object in code; initWithCoder is the init method called by the system if loading the object from a storyboard or xib.
To create one of your custom buttons in code, you alloc/init as you would any other object:
MyCustomButton* button = [[MyCustomButton alloc] initWithFrame:buttonFrame];
You wouldn't also alloc/init the superclass instance, this is done by the initWithFrame: method in the subclass when it calls [super initWithFrame:frame]. self refers to your custom subclass instance, but that includes all of the (public) properties and methods from it's superclass - unless you have implemented overrides in the subclass.
To use your subclassed button in a storyboard/xib, just drag out a regular button then set it's type to your custom button class in the Identity Inspector. The initWithCoder method is called automatically when the button is loaded from the storyboard/xib into a view.
update
From your comments, you seem to harbour a few confusions still, so here are some highly compressed de-obfuscating notes...
Keep away from subclassing UINavigationController unless you really know what you are doing. It's rarely necessary.
The buttons on a navController's interface are properties of it's contained viewControllers. Look up the navigationItem property of UIViewController (similarly - in the case of a UIToolbar - the View Controller has a toolbarItems property). This allows Navigation Controllers to be context-aware.
The 'viewDidLoad' in my example is assumed to be in a regular UIViewController. My example is also a category on the regular UIBUtton which has no formal relationship with UIBarButtonItem.
Try getting a UIButton category to work with a regular ViewController first before experimenting with UIBarButtonItem (which does not inherit from UIButton).
UIBarbuttonItem has no initWithFrame, because the thing that organises the bar (UINavigationBar or UIToolbar) - in this case a Navigation Controller - is responsible for it's ultimate size and positioning. The viewController governs the relative order of barButtonItems, and whether they appear on the left or the right, and the content and (some aspects of) it's appearance, but the rest is up to the NavController.
I'm the developer of an in-house program for modeling harmonics and other properties of liquid-filled shells. Right now, the whole program assumes that there's only one set of physical properties (temperature, pressure, etc.) being used at a time in calculations. I have already broken all of the calculations out into a Sphere model. I have a controller which owns a sphere. When the user changes the physical properties the controller has the sphere recalculate everything and updates all the windows which are displaying the results of sphere calculations.
Now someone has asked me to make a table displaying the frequencies of a specific harmonic over a range of temperatures and pressures. I think this is going to require a new controller that has its own sphere model because it needs to be independent of all the other windows.
Here's my question: Should my new controller be an NSWindowController subclass or should it be an NSObject subclass with a property that is an NSWindow whose text fields are bound to values in the controller (or something completely different)? I'm the only developer in the company and I learned Cocoa on my own as I wrote this program over the past four years so I'm not sure I've always followed best practices. Since I'm about to introduce a new significant functionality I'd like to make sure I'm doing it right.
Not sure this matters, but the solution has to run under OS X 10.5 because we still have some G5 machines in the organization.
If you have a controller that controls a particular window, then you should definitely use an NSWindowController subclass, if only because NSWindowController handles some of the more complex nib-loading and memory management issues for you.
Unless the Sphere model in your new window will be showing a different set of data than the one in your main controller, you don't need to create a new model for your new controller. You can just reference the sphere instance in your main controller.
Something like this:
.h:
#import <Cocoa/Cocoa.h>
#class Sphere;
#interface FrequenciesController : NSWindowController
{
Sphere* sphere;
}
- (id)initWithSphere:(Sphere*)aSphere;
#end
.m:
#import "FrequenciesController.h"
#import "Sphere.h"
#implementation FrequenciesController
- (id)initWithSphere:(Sphere*)aSphere
{
self = [super initWithWindowNibName:#"NameOfYourNib"];
if (self)
{
sphere = [aSphere retain];
}
return self;
}
- (void)dealloc
{
[sphere release];
[super dealloc];
}
#end
To create the window, you then just have to do something like this in your main controller, assuming that you have declared frequenciesController as an ivar:
- (IBAction)showFrequenciesWindow:(id)sender
{
if(!frequenciesController)
{
frequenciesController = [[FrequenciesController alloc] initWithSphere:self.sphere];
[frequenciesController showWindow:self];
}
}
I am new to iPhone programming. I want to use CATiledLayer to load an image.
I am creating the subclass of UIVIew class and in the init method of that class, I am writing,
CATiledLayer *tiledLayer = (CATiledLayer *)[self layer];
But when I am writing
tiledLayer.levelsOfDetail = 4;
It gives me the error that
[CALayer setLevelsOfDetail:]: unrecognized selector sent to instance 0xcd04450
So to check the class-type of tiledLayer,I am writing following statements :
NSString *pqr = [[NSString alloc]initWithFormat:#"%#", [tiledLayer class]];
NSLog(pqr);
But it prints CALayer instead of CATiledLayer. Why is it so?? What am I missing??
Now I am stuck here. :(
Applying a typecast to a pointer in Objective-C has no effect at runtime (it does have some effect during compile time, so this syntax is still used at times).
So, your "typecast" is not actually doing a typecast. It's just informing the compiler (in that one line of code only) that this is a CATiledLayer and not a CALayer as it has been declared. The actual object is really still a CATiledLayer.
In order to change the class of self.layer, you define a static method to return the class:
#implementation MyView
+ (Class)layerClass
{
return [CATiledLayer class];
}
#end
And now, when the layer for any MyView instance is created, it will be a CATiledLayer instead of a CALayer.
I am developing an plugin for OsiriX.
In that app i have 3-4 nib files. Also in for plugin there are files (.h & .m) called PluginFilter where method called - (long) filterImage:(NSString) menuName is present from which the plugin start execution. Now my problem is that, I have return the code to launch main window is in some other .m file and I have to call that file using the method mentioned above.
The app has multiple nib files. I have a plugin name PluginFilter called by:
- (long) filterImage:(NSString*) menuName
The plugin should open a window when called by this method. The code that defines the window controller is in another nib. When I call the filterimage method in the plugin, the window never appears.
Here is my filterImage: method.
#import "XGridInOsiriXFilter.h"
#import "MainWindowController.h"
#implementation XGridInOsiriXFilter
- (void) initPlugin
{
}
- (long) filterImage:(NSString*) menuName
{
MainWindowController *mainWindowController = [[GridSampleMainWindowController alloc] init];
[mainWindowController showWindow:self ];
[mainWindowController release];
return 0;
}
#end
Calling the method produces not warnings or errors, the window simply fails to appear.
I recognize this may be coming a little too late but I was looking for a way to do the same thing you are asking for and found it. You can use NSBundle to load the desired nib and point it to an instantiated controller. Like:
#implementation YourPluginFilter
- (void) initPlugin
{
yourWindowController = [[YourWindowController alloc] init];
NSLog(#"Initialized YourWindowController");
}
- (long) filterImage:(NSString*) menuName
{
if (yourWindowController && [NSBundle loadNibNamed:#"YourNibName" owner:yourWindowController]) {
NSLog(#"Activated yourWindowController");
return 0;
} else {
return -1;
}
}
#end
You would normally not open the app's main window from a plugin. Plugins by definition my not always be present so you should not put critical code in them. Neither would you want multiple plugins opening the same logical window.
Instead, the main window should be displayed by the app delegate as normal but the content of the window can be processed by a plugin if the plugin is available.
The main application should load and configure the main window and only call the plugin to process the contents of the window.
Even so it is technically possible to open a window from a plugin so either (1) the plugin is not being loaded and the method is not being called (insert breakpoint/log to confirm) or (2) the window controller is misconfigured so that it does not open the window. Test the controller outside the plugin to confirm it works. Better yet, move the window opening code outside the plugin.
Edit01:
From comment:
I have made some changes in the above
code as follows
- (long) filterImage:(NSString*) menuName {
MainWindowController *mainWindowController = [[GridSampleMainWindowController alloc] init:self];
[mainWindowController showWindow:self ];
[mainWindowController release];
return 0;
}
but it is showing wanring that no
-init method found. Why it is showing like this because -init method is der
in the MainWindowController.m file
Well, you have two problems here.
(1) You set define mainWindowController as of class MainWindowController but you initialize it with class GridSampleMainWindowController. If MainWindowController is a subclass of GridSampleMainWindowController this will work but will generate warnings. You should instead initialize it like
GridSampleMainWindowController *mainWindowController = [[GridSampleMainWindowController alloc] init:self];
or
MainWindowController *mainWindowController = [[MainWindowController alloc] init:self];
(2) You release the controller without any other object retaining it which will kill it. When a window controller dies it deallocates the windows it controls. This is most likely why you see nothing.
You should sort out what class you want the controller to be and then set it as a retained property of the plugin class so you can keep it an its window around.
Which init method is it complaining about? Your initPlugin does nothing and returns a void if that is the plugin's actual initialization method then the plugin will never load. It should at least look like this:
- (id) initPlugin
{
self=[super init];
return self;
}
It looks like you come from a pure C background which is great for this environment but you need to learn about the object oriented parts of the Objective-C language. You're still writing methods as if they were old school C functions and there are important and oft times subtle differences.
Sorry I missed all this yesterday. I saw "plugin" and focused on the wrong aspect of the problem.
Edit02:
No i m not talking about my initPlugin
method. I am talking about my init
method which is there in
MainWindowController.m file
- (id)init {
self = [super initWithWindowNibName:#"MainWindow"];
return self;
}
This will return an instance of the MainWindowController's super class. If you're not doing any customization you have no need to override the init method in you subclass. Just use the inherited version thusly:
MainWindowController *mainWindowController = [[MainWindowController alloc] initWithWindowNibName:#"MainWindow"];
After always running monolithic blocks of code from within my AppController object, I've just learned how to modularize my code using separate model controller objects (so much neater and less confusing to work with :-))
My simple test app has a main AppController and two model objects (Model1 and Model2). I can successfully pass data from the AppController to the models, and the models themselves can run methods and process the passed data 'internally' as they were intended to do -- but I can't get them to communicate with a darned NSTextField in the UI. Here's the relevant parts of my code:
In AppController.m #import "AppController.h"
#implementation AppController
- (IBAction)passObjectsToModelController:(id)sender
{
NSString *stringToPass = #"Hello from Model2 :-)";
int numToPass=12345;
Model2 *ObjController2 = [[Model2 alloc]initWithStuff:stringToPass:numToPass];
[ObjController2 release];
}
#end
...in Model2.h
#import
#interface Model2 : NSObject
{
IBOutlet NSTextField *passedStringField;
}
- (id)initWithStuff:(NSString*)passedString :(int)passedNum;
#end
...and finally in Model2.m
#import "Model2.h"
#implementation Model2
- (id)initWithStuff:(NSString*)passedString :(int)passedNum
{
if(self = [super init])
{
NSLog(#"now inside 'Model2' controller...");
NSLog(#"the passed string reads: %#",passedString); //••• this works •••
NSLog(#"the passed number is:%d",passedNum); //••• this works •••
[passedStringField setStringValue:passedString]; //••• WTF!!... this DOESN'T work! •••
// do something internally with passedNum here...
}
return self;
}
#end
Both model objects have outlets to the common NSTextField and I've control-dragged from both objects to the field and connected them. My AppController doesn't know about the NSTextField (and I assume, doesn't even want to know). No IB connections have been made between the controller object and model objects.
NSLog tells me that the model objects are being created, and that the passed values are making it that far... but not from there into the text field in the GUI window. I'm not getting any compiler errors or warnings. Am I missing some kind of 'setTarget:' call perhaps?
Any help/ideas would be much appreciated. Thanks :-)
Aside from the lack of MVC that mihirsm mentions, the actual problem is that you're trying to access an outlet in an -init method.
When a object is initialized, outlets are not guaranteed to be connected.
If you want to set the value of an NSTextField declared as an outlet, you should implement -awakeFromNib, which is called when the nib has been loaded and all outlets are guaranteed to be live.
in Model1.h:
#interface Model1 : NSObject
{
IBOutlet NSTextField* passedStringField;
NSString* modelString;
}
- (id)initWithString:(NSString*)passedString number:(int)passedNum;
#end
in Model1.m:
#implementation Model1
- (id)initWithString:(NSString*)passedString number:(int)passedNum
{
if(self = [super init])
{
//copy the string to our ivar
modelString = [passedString copy];
}
return self;
}
//awakeFromNib is called when our outlet is live
- (void)awakeFromNib
{
[passedStringField setStringValue:modelString];
}
//don't forget to release the string, because we created it using -copy
- (void)dealloc
{
[modelString release];
}
#end
The Controller sits between the Model and the View. The Model should not communicate with the View.
It should be the job of the Controller to pass any incoming values from the View to the Model. The Model then processes the data and sends back to the Controller which then updates the View with the new data.
So, in your code you should only have one IBOutlet for the TexField declared in the AppController.
Given all this, I am not exactly sure why the TextField is not being updated. From the given code looks like it should. Maybe multople IBOutlets are causing some issue? Can you try with only one Model having the IBOutlet?