Subclass does not inherit its functionality - objective-c

I am trying to create a subclass of UICollectionViewLayout, but I cant call its original functionality such as for example itemSize and I get the error saying
Property 'itemSize' not found on object 'SubClass *'
What am I missing?
My code looks like this.
.h
#import <UIKit/UIKit.h>
#interface SubClass : UICollectionViewLayout
#end
.m
#import "SubClass.h"
#implementation SubClass
- (NSArray *) layoutAttributesForElementsInRect:(CGRect)rect {
NSArray *answer = [self layoutAttributesForElementsInRect:rect];
for(int i = 1; i < [answer count]; ++i) {
UICollectionViewLayoutAttributes *currentLayoutAttributes = answer[i];
UICollectionViewLayoutAttributes *prevLayoutAttributes = answer[i - 1];
CGFloat maximumSpacing = 20.0f;
CGFloat origin = CGRectGetMaxX(prevLayoutAttributes.frame);
if(origin + maximumSpacing + currentLayoutAttributes.frame.size.width < self.collectionViewContentSize.width) {
CGRect frame = currentLayoutAttributes.frame;
frame.origin.x = origin + maximumSpacing;
currentLayoutAttributes.frame = frame;
}
}
return answer;
}
#end

You inherited from the wrong class. The itemSize property is part of UICollectionViewFlowLayout, not UICollectionViewLayout.
Also, you probably want to call [super layoutAttributesForElementsInRect:rect]. Sending that message to self will cause a stack overflow due to unbounded recursion.

Related

Create a subclass of a SKSpriteNode subclass

Let's say I want to create a bunch of different types of Spaceships. I want to setup a base spaceship class that I can use to create other spaceships with minor differences.
My base class looks like this.
// BaseSpaceship.h
#interface SpaceshipNode : SKSpriteNode
#property NSColor color;
#property CGFloat engineThrust;
+ (id)baseSpaceshipWithImageNamed:(NSString *)name;
#end
// BaseSpaceship.m
#implementation BaseSpaceship
+ (id)baseSpaceshipWithImageNamed:(NSString *)name {
BaseSpaceship *ship = [BaseSpaceship spriteNodeWithImageNamed:name];
ship.color = [NSColor redColor];
ship.engineThrust = 2.0;
return ship;
}
#end
I can create a ship in MyScene.m like this just fine.
BaseSpaceship *baseClass = [BaseSpaceship baseSpaceshipWithImageNamed:#"BaseShip"];
However, I'm not sure how to create a subclass of BaseSpaceship, for example, DestroyerSpaceship. I'm not sure if I should be using static methods or not. The examples I've seen online use static methods to instantiate SKSpriteNodes. This is what I came up with, but it's wrong.
// DestroyerSpaceship.h
#interface DestroyerSpaceship : BaseSpaceship
#property CGFloat missileThrust;
- (id)makeDestroyerSpaceship;
#end
// DestroyerSpaceship.m
#implementation DestroyerSpaceship
- (id)makeDestroyerSpaceship{
DestroyerSpaceship *ship = [DestroyerSpaceship baseSpaceshipWithImageNamed:#"DestroyerShip"];
ship.engineThrust = 2.0;
// ship doesn't have missileThrust, program crashes
ship.missileThrust = 3.0;
return ship;
}
#end
Ultimately, I want to be able to do something like this.
DestroyerSpaceship* a = [DestroyerSpaceship makeDestroyerSpaceship];
EvilSpaceship* b = [EvilSpaceship makeEvilSpaceship];
NiceSpaceship* c = [NiceSpaceship makeNiceSpaceship];
And have them all inherit basic properties and methods from BaseSpaceship.
The answer is less complex than you think. Well, the code might be a bit more complex, but once you have the structure it is most flexible. Creating the different types of spaceships will also be a lot more readable.
You can override the initializer method in the subclass. As a sidenote, use (instancetype) instead of (id) (source: instancetype # NSHipster).
As you are adding custom body sprites to the object, I would opt to subclass SKNode instead of SKSpriteNode (so #interface SpaceshipNode : SKNode instead of #interface SpaceshipNode : SKSpriteNode).
#interface SpaceshipNode : SKNode
#property SKColor * color; // Use SKColor instead of NSColor
#property CGFloat engineThrust;
#end
// ...
#implementation SpaceshipNode
- (instancetype) init {
if (self == [super init]) {
NSLog(#"A new SpaceshipNode was just init'ed.");
// set some default initial values here that all brand-new SpaceshipNodes will inherit
// perhaps create and add a basic body sprite
// SKSpriteNode * body = ...;
// [self addChild:body];
// set thrust
self.engineThrust = 2.0;
}
return self;
}
Then you can subclass and create a new type of spaceship. Awesome!
#interface DestroyerSpaceship : SpaceshipNode
#property CGFloat missileThrust;
#end
#implementation DestroyerSpaceship
- (instancetype) init {
// note that [super init] will call the SpaceshipNode's init method
if (self = [super init]) {
NSLog(#"A new DestroyerSpaceship was just init'ed.");
// add a body sprite
// SKSpriteNode * body = ...;
// [self addChild:body];
// a Destroyer is much faster than your average spaceship
self.engineThrust = 10.0;
// set class specific variables
self.missileThrust = 5.f;
}
return self;
}
Now, you can just call:
SpaceshipNode * newSpaceShip = [SpaceshipNode new]; // short for [[SpaceshipNode alloc] init];
DestroyerSpaceship * newDestroyer = [DestroyerSpaceship new];
These two lines will log the following. The last two lines are caused by the Destroyer, which first calls the SpaceshipNode init, and then the Destroyer-specific init method.
A new SpaceshipNode was just init'ed.
A new SpaceshipNode was just init'ed.
A new DestroyerSpaceship was just init'ed.
And you can even use it like this:
SpaceshipNode * newUnidentifiedVessel = [DestroyerSpaceship new];
if ([newUnidentifiedVessel isKindOfClass:[DestroyerSpaceship class]]) {
NSLog(#"We are under attack! Route power to shields!");
}
- (instancetype)makeDestroyerSpaceship{
if (self = [super baseSpaceshipWithImageNamed:#"DestroyerShip"]) {
self.engineThrust = 2.0;
self.missileThrust = 3.0;
}
return self;
}

polymorphism methods in ios

I have a doubt in implementing oops concept in objective-c.Is Pholyorphism possible in objective-c. How to implement polymorphism in objective-c.please explain with example?
Every method, including class methods, is dynamic in Objective-C.
One very basic approach would be:
Declare the base interface:
#interface MONConstantColor : NSObject
- (UIColor *)color;
#end
Define the base implementation:
#implementation MONConstantColor
- (UIColor *)color { return /* ...do/ret something appropriate */; }
#end
Then create some variations:
#interface MONRedColor : MONConstantColor
#end
#implementation MONRedColor
- (UIColor *)color { return [UIColor redColor]; }
#end
#interface MONYellowColor : MONConstantColor
#end
#implementation MONYellowColor
- (UIColor *)color { return [UIColor yellowColor]; }
#end
- (HomeWorkResult *)homeWorkResultFromHomeWorkTask:(HomeWorkTask *)task
{
if (!self.lazy) {
return [self HW_performHomeWorkTask:task];
}
StackOverflowPost *post = [StackOverflow postHomeWorkTask:task];
for (id user in post.responders) {
// Here is the pholyorphism[sic].
// First, test to see if a stack overflow user is able to do home work tasks.
if ([user respondsToSelector:#selector(homeWorkResultFromHomeWorkTask:)]) {
// Next, have the user do the home work task.
HomeWorkResult *result = [user homeWorkResultFromHomeWorkTask:task];
// If there is a result, return that result.
if (result) {
return result;
}
}
}
// Finally, if no stack overflow user does home work tasks or if there was no
// result perform the task yourself.
return [self HW_performHomeWorkTask:task];
}
The word polymorphism means having many forms
Objective-C polymorphism means that a call to a member function will cause a different function to be executed depending on the type of object that invokes the function.
Consider the example, we have a class Shape that provides the basic interface for all the shapes. Square and Rectangle are derived from the base class Shape.
We have the method printArea that is going to show about the OOP feature polymorphism.
#import <Foundation/Foundation.h>
#interface Shape : NSObject
{
CGFloat area;
}
- (void)printArea;
- (void)calculateArea;
#end
#implementation Shape
- (void)printArea{
NSLog(#"The area is %f", area);
}
- (void)calculateArea{
}
#end
#interface Square : Shape
{
CGFloat length;
}
- (id)initWithSide:(CGFloat)side;
- (void)calculateArea;
#end
#implementation Square
- (id)initWithSide:(CGFloat)side{
length = side;
return self;
}
- (void)calculateArea{
area = length * length;
}
- (void)printArea{
NSLog(#"The area of square is %f", area);
}
#end
#interface Rectangle : Shape
{
CGFloat length;
CGFloat breadth;
}
- (id)initWithLength:(CGFloat)rLength andBreadth:(CGFloat)rBreadth;
#end
#implementation Rectangle
- (id)initWithLength:(CGFloat)rLength andBreadth:(CGFloat)rBreadth{
length = rLength;
breadth = rBreadth;
return self;
}
- (void)calculateArea{
area = length * breadth;
}
#end
int main(int argc, const char * argv[])
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
Shape *square = [[Square alloc]initWithSide:10.0];
[square calculateArea];
[square printArea];
Shape *rect = [[Rectangle alloc]
initWithLength:10.0 andBreadth:5.0];
[rect calculateArea];
[rect printArea];
[pool drain];
return 0;
}
use this link as refernce
http://www.tutorialspoint.com/objective_c/objective_c_polymorphism.htm

NSViewController.view (Subviews) setTag

Each time I press a button, mainController is calling [self.view addSubview: createCustomView.view]. Everything works fine here. The problem is that I need to put a tag on each subview I create in order to retrieve them later. I've already tried this :
MainController.m
NSNumber *i;
createCustomView.view.tag = i; //readonly
And what I actually wanna do is :
int i;
[createCustomView.view setTag:i];
But the setTag method doesn't exist. My question is : Is there a way I can do this other than using identifier string which brings some problems in my case?
Thanks in advance
Here's the .h file of the Controller
#import <Foundation/Foundation.h>
#import "TransactionButtonView.h"
#class TransactionButtonController;
#interface TransactionViewController : NSViewController
{
TransactionButtonController *transactionButtonController;
}
-(IBAction)createOnPushButton:(id)sender;
-(void)recalculatePositionOnRemove:(long)tag;
#property (nonatomic,assign) TransactionButtonController *transactionButtonController;
#end
Here's the .m file of the Controller
#import "TransactionViewController.h"
#import "TransactionButtonController.h"
#import "MainController.h"
#implementation TransactionViewController
#synthesize transactionButtonController;
-(IBAction)createOnPushButton:(id)sender
{
transactionButtonController = [[TransactionButtonController alloc] initWithNibName:#"TransactionButton" bundle:nil];
NSPoint originPoint;
for (int i=1; i <= [[self.view subviews]count]; i++) {
originPoint.y = transactionButtonController.view.bounds.origin.y + self.view.bounds.size.height - transactionButtonController.view.bounds.size.height*i;
transactionButtonController.view.tag = i; // Here's the PROBLEM!!!
[[transactionButtonController view]setIdentifier:[[NSNumber numberWithInt:i]stringValue]]; //here's the not good option
}
originPoint.x = transactionButtonController.view.bounds.origin.x;
[[transactionButtonController view] setFrameOrigin:originPoint];
[self.view addSubview:transactionButtonController.view];
[transactionButtonController sendVarsToButton:#"xxx" :#"591" :5 :87456356472456];
}
-(void)recalculatePositionOnRemove:(long)tag
{
NSPoint originPoint;
for (long i = tag; i<[[self.view subviews]count]; i++) {
originPoint.y = transactionButtonController.view.bounds.origin.y +self.view.bounds.size.height - transactionButtonController.view.bounds.size.height*i;
originPoint.x = transactionButtonController.view.bounds.origin.x;
[[transactionButtonController.view viewWithTag:i+1] setFrameOrigin:originPoint];
}
}
#end
If you want to add a tag to a view do this:
theView.tag = 1;
To remove it:
[[myParentView viewWithTag:1] removeFromSuperview]

ObjC: Can't access ivar of custom class from outside

I'm attempting to make a basic game in cocos2d, and I've gotten to the point where I'm attempting to scroll the background depending on the hero sprite's position. The hero's created alongside the controls in a class called GameplayLayer, and all works fine for non-scrolling backgrounds.
The book I'm following has given me some sample code to get the scrolling working based on when my character passes the half-way point, which seems perfect, but it's not executing, I believe this is because it's creating another instance of my Hero class, and there's no link between that instance, and the one that's displayed onscreen.
I'm assuming that I can fix this by making the working instance of my hero accessible from within the background scrolling class (which is called YrrasCoastBackgroundLayer), but I'm having a brain-block and can't get it to see it. I've tried a #property, but Xcode just won't see it in the other class.
Here's the .h file from the GameplayLayer class, where the hero is created / hooked up to controls, etc.:
#import "CCLayer.h"
#import "cocos2d.h"
#import "CommonProtocols.h"
#import "SneakyJoystick.h"
#import "SneakyButton.h"
#import "SneakyJoystickSkinnedBase.h"
#import "SneakyButtonSkinnedBase.h"
#import "HeroMale.h"
#import "GameCharacter.h"
#interface GameplayLayer : CCLayer <GameplayLayerDelegate> {
CCSprite *playerSprite;
SneakyJoystick *playerJoystick;
SneakyButton *jumpButton;
CCSpriteBatchNode *sceneSpriteBatchNode;
HeroMale *heroMale;
}
#property (nonatomic, readonly) SneakyJoystick *playerJoystick;
#property (nonatomic, readonly) SneakyButton *jumpButton;
#property (nonatomic, assign) HeroMale *heroMale;
#end
The YrrasCoastBackgroundLayer.h file definitely imports GameplayLayer.h, and here's the contents of the method in YrrasCoastBackgroundLayer.m file which I want to be able to access that *heroMale ivar:
- (void) adjustLayer {
float heroXPosition = heroMale.position.x;
CGSize screenSize = [[CCDirector sharedDirector] winSize];
float halfOfTheScreen = screenSize.width / 2.0f;
CGSize levelSize = [[GameManager sharedGameManager] getDimensionsOfCurrentScene];
if ((heroXPosition > halfOfTheScreen) && (heroXPosition < (levelSize.width - halfOfTheScreen))) {
float newXPosition = halfOfTheScreen - heroXPosition;
[self setPosition:ccp(newXPosition, self.position.y)];
}
}
I'm getting an Xcode error on the float heroXPosition = heroMale.position.x line, stating that heroMale is an undeclared identifier. How can I make it usable here, and will that even solve the problem?
UPDATE:
Here's the .h file for YrrasCoastBackgroundLayer:
#import "CCLayer.h"
#import "cocos2d.h"
#import "HeroMale.h"
#import "GameplayLayer.h"
#interface YrrasCoastBackgroundLayer : CCLayer {
// Web Tutorial
CCParallaxNode *backgroundNode;
CCSprite *backgroundImage;
// Book Tutorial
CCSpriteBatchNode *sceneSpriteBatchNode;
CCParallaxNode *parallaxNode;
}
#property (nonatomic, assign) GameplayLayer *gameplayLayer;
#end
And here's the YrrasCoastBackgroundLayer.m:
#import "YrrasCoastBackgroundLayer.h"
#implementation YrrasCoastBackgroundLayer
#synthesize gameplayLayer;
- (id) init {
self = [super init];
if (self != nil) {
// Web Tutorial
backgroundNode = [CCParallaxNode node];
[self addChild:backgroundNode z: -1];
backgroundImage = [CCSprite spriteWithFile:#"yrras-coast-ipad-hd.png"];
CGPoint dustSpeed = ccp(0.1, 0.1);
[backgroundNode addChild:backgroundImage z:0 parallaxRatio:dustSpeed positionOffset:ccp(1024, [[CCDirector sharedDirector]winSize].height / 2)];
self.gameplayLayer = gameplayLayer;
[self scheduleUpdate];
}
return self;
}
- (void)update:(ccTime)deltaTime {
// Web Tutorial
// CGPoint backgroundScrollVel = ccp(-1000, 0);
// backgroundNode.position = ccpAdd(backgroundNode.position, ccpMult(backgroundScrollVel, deltaTime));
CCArray *listOfGameObjects = [sceneSpriteBatchNode children];
for (GameCharacter *tempChar in listOfGameObjects) {
[tempChar updateStateWithDeltaTime:deltaTime andListOfGameObjects:listOfGameObjects];
}
[self adjustLayer];
}
// Book Tutorial
- (void) adjustLayer {
float heroXPosition = gameplayLayer.heroMale.position.x;
CCLOG(#"heroXPosition is %f", gameplayLayer.heroMale.position.x);
CGSize screenSize = [[CCDirector sharedDirector] winSize];
float halfOfTheScreen = screenSize.width / 2.0f;
CGSize levelSize = [[GameManager sharedGameManager] getDimensionsOfCurrentScene];
if ((heroXPosition > halfOfTheScreen) && (heroXPosition < (levelSize.width - halfOfTheScreen))) {
float newXPosition = halfOfTheScreen - heroXPosition;
[self setPosition:ccp(newXPosition, self.position.y)];
}
}
#end
You need an instance of your GameplayLayer to be passed to the YrrasCoastBackgroundLayer layer.
You can declare an assignable property in YrrasCoastBackgroundLayer :
#property (nonatomic, assign) GameplayLayer gamplayLayer;
and when you initialize your YrrasCoastBackgroundLayer pass the instance of the game play layer and in your adjustLayer method do :
float heroXPosition = gameplayLayer.heroMale.position.x;
EDIT :
assuming this is how you create your scene :
MyScene *scene = [MyScene scene];
gameplayLayer = [[GameplayLayer alloc] init];
bgLayer = [[YrrasCoastBackgroundLayer alloc] init];
bgLayer.gameplayLayer = gameplayLayer; // This is where you assign the gameplay Layer to the background layer;
[scene addChild:bgLayer];
[scene addChild:gameplayLayer];
// Release both layers if you dont need them anymore here (scene addChild retains them when they are added)
[gameplayLayer release];
[bgLayer release];
I am not sure how can you access heroMale as it is not member of YrrasCoastBackgroundLayer but of GameplayLayer. You would have to have object/instance of class GameplayLayer in order for you to access heroMale. That is the reason why you are getting undeclared identifier (heroMale is not property of class YrrasCoastBackgroundLayer).
Kind regards,
Bo

i try to understand initializers in obj-c and i can't

I try to understand how to work simple init funcion and I don't know where I have made a mistake. Can somebody assist?
Rectangle.h
#interface Rectangle : NSObject
{
int width;
int height;
}
-(id)initObject;
#end
Rectangle.m
#implementation Rectangle
-(id)initObject{
if (self = [super init]) {
height = 5;
width = 7;
}
return self;
}
#end
And in ViewController.h i import Rectangle.h, declare *rect object and in .m i execute(? run?) initObject.
ViewController.h
#import <UIKit/UIKit.h>
#import "Rectangle.h"
#interface ViewController : UIViewController
{
Rectangle *rect;
}
#end
ViewController.m
-(void)viewDidLoad
{
rect = [[Rectangle alloc] initObject];
NSLog(#"%#", rect);
[super viewDidLoad];
}
initObject return me:
2011-11-21 09:43:02.625 initializers[43693:f803] <Rectangle: 0x6ab1660>
The only problem with your code that I can see is you called your initializer -initObject for no good reason. It's not taking any parameters at all, so you really should just call it -init like every other parameterless initializer in the system.
As for the log output, I imagine your confusion lies in the fact that it says <Rectangle: 0x6ab1660>. This is perfectly normal. The default implementation of -description (the method that returns this output) is the name of the class of the object followed by the object's address. In other words, -[NSObject description] is likely to be implemented something like the following:
- (NSString *)description {
return [NSString stringWithFormat:#"<%#: %p>",
NSStringFromClass([self class]),
self];
}
This means that instance variables of your object are not going to be printed. A number of built-in classes do print their instance variables when logged, but this was implemented specifically for that class and is not a generic mechanism. If you want to verify that your Rectangle object is correct, you could implement -description like so:
- (NSString *)description {
return [NSString stringWithFormat:#"<%#: %p width=%d, height=%d>",
NSStringFromClass([self class]),
self,
width,
height];
}
No error! Since your Rectangle class has no description method, calling NSLog(#"%#", rect); will return the class of the object, followed by its address in memory.
If you want to print width and height of the rectangle you may use something like:
in Rectangle.h
#interface Rectangle : NSObject
{
int width;
int height;
}
-(id)initObject;
#property int width, height;
#end
in Rectangle.m
#implementation Rectangle
#synthesize width, height;
-(id)initObject{
if (self = [super init]) {
height = 5;
width = 7;
}
return self;
}
#end
and then call
NSLog(#"width=%d, height=%d", [rect width], [rect height]);