use subclass of CCLayer for CCScrollLayer - ios

I've created a custom class which is a subclass of CCLayer and trying to use it for CCScrollLayer
The way I do it is:
//Store the my layers to an NSMutableArray
for (AACustomClassLayer *cardLayer in levels) {
[layers addObject:cardLayer];
}
Under the hood of CCScrollLayer it crashes at:
- (void) updatePages
{
// Loop through the array and add the screens if needed.
int i = 0;
for (CCLayer *l in layers_)
{
l.anchorPoint = ccp(0,0);
l.contentSize = [CCDirector sharedDirector].winSize;
l.position = ccp( (i * (self.contentSize.width - self.pagesWidthOffset)), 0 );
if (!l.parent)
[self addChild:l];
i++;
}
}
The implementation for the AACustomClassLayer class (subclass of CCLayer) looks like:
-(id)initWithChapter:(AALevel *)level {
self = [super init];
if (self) {
self.isTouchEnabled = YES;
//Here I'm adding the CCSprite to my layer
}
return self;
}
UPDATE:
Crash log
2012-04-20 14:12:12.344 [15780:10a03] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSCFDictionary setAnchorPoint:]: unrecognized selector sent to instance 0x884ab40'
*** First throw call stack:
(0x1a75022 0x200fcd6 0x1a76cbd 0x19dbed0 0x19dbcb2 0xd013f 0xcfe2b 0x102370 0x44c15 0xbe45f 0x8a94be 0x8aa274 0x8b9183 0x8b9c38 0x8ad634 0x282def5 0x1a49195 0x19adff2 0x19ac8da 0x19abd84 0x19abc9b 0x8a9c65 0x8ab626 0xbda06 0x22e5)
terminate called throwing an exception

I've found it!
for (AACustomClassLayer *cardLayer in levels) {
cardLayer = [[AACustomClassLayer node] autorelease];
[layers addObject:cardLayer];
}

You should add a conditional check in your for loop that determines if the object you are getting from enumeration is in fact a CCLayer. Your crash log states that the anchorPoint setter was not available on some object, presumably on an object in your layers_ array since that is the code you have posted that deals with anchorPoints.
Enumeration is convenient, but you are casing all objects to CCLayer when it is possible that one of them is not. I don't know where you are adding objects to layers_ but is it possible you are adding an object that is not actually a CCLayer ?

Related

Is it possible to intercept all method class on any UIViewController subclass

Let's say I wanted to be able to intercept any method calls to a UIViewController subclass.
First of all, I swizzle the +(instancetype)alloc method and I check if the current instance isKindOfClass:[UIViewController class]. If it is I go ahead and instantiate my proxy with the target.
///swizzled Alloc
+ (instancetype)monitoredAlloc {
id obj = [self monitoredAlloc];
if([obj isKindOfClass:[UIViewController class]]) {
id proxy = [PMGProxy proxyWithObject:obj];
return proxy;
}
return [self monitoredAlloc];
}
---------------------------------------
/// Proxy class
#implementation PMGProxy
+ (instancetype)proxyWithObject:(id)obj {
PMGProxy *proxy = [self alloc];
proxy.obj = obj;
return proxy;
}
- (void)forwardInvocation:(NSInvocation *)invocation
{
[invocation setTarget:_obj];
[invocation invoke];
}
-(NSMethodSignature *)methodSignatureForSelector:(SEL)sel {
return [self.obj methodSignatureForSelector:sel];
}
- (Class)class {
return [self.obj class];
}
The problem is that I get crashes, so I would expect the implementation of my Proxy is wrong... What am I doing wrong?
Here is the exception:
*** Terminating app due to uncaught exception 'NSGenericException', reason: 'This coder requires that replaced objects be returned from initWithCoder:'
From the error it seems that the coder is only happy to accept a different class returned from initCoder: rather than earlier in the process at the alloc stage.
Might be worth looking up NSSecureCoding for more detail on the whole process.
While you’re at it, take a look at the stack track that resulted in your exception, it will give you a bit more perspective on just how deep this rabbit hole goes.

Attempting to segue from Objective-C to Swift VC

i'm trying to segue from objective-c to swift.
However when I try this using the object below I receive the following error
I don't know what i'm doing wrong, i've setup the segue on the storyboard and assigned it to the same ID, created the prepare function and the perform.
- (void)renderer:(id<SCNSceneRenderer>)renderer willRenderScene:(SCNScene *)scene atTime:(NSTimeInterval)time {
// Look for trackables, and draw on each found one.
size_t trackableCount = trackableIds.size();
for (size_t i = 0; i < trackableCount; i++) {
// NSLog(#"this is the variable value: %d", trackableIds[i]);
// Find the trackable for the given trackable ID.
ARTrackable *trackable = arController->findTrackable(trackableIds[i]);
// SCNCamera *camera = self.cameraNode.camera;
SCNNode *trackableNode = self.trackableNodes[i];
if (trackable->visible) {
if (trackableIds[i] == 0) {
NSLog(#"Starbucks");
[self performSegueWithIdentifier:#"brandSegue" sender:self];
} else if (trackableIds[i] == 1) {
NSLog(#"Dortios");
}
} else {
trackableNode.opacity = 0;
}
}
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"brandSegue"]) {
JSONViewController *destViewController = segue.destinationViewController;
}
}
EDIT - ERROR MESSAGE
2017-11-09 16:57:05.232992+0000 MyARApp[2573:1099927] *** Assertion failure in -[UIApplication _cachedSystemAnimationFenceCreatingIfNecessary:], /BuildRoot/Library/Caches/com.apple.xbs/Sources/UIKit/UIKit-3698.21.8/UIApplication.m:1707
2017-11-09 16:57:05.233169+0000 MyARApp[2573:1099927] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'accessing _cachedSystemAnimationFence requires the main thread'
*** First throw call stack:
(0x186f51d04 0x1861a0528 0x186f51bd8 0x1878e1c24 0x1905e9c4c 0x19064646c 0x190432338 0x19038fe5c 0x1903fb6a8 0x190470bf0 0x1906ffb00 0x190701434 0x190703cd8 0x19070420c 0x190703c28 0x190467ab4 0x190707ae4 0x190b38854 0x190ca6b30 0x190ca69d4 0x1906f7e18 0x1020a27cc 0x19a66c610 0x19a725a84 0x19a723e00 0x19a724d1c 0x19a5a0cc4 0x19a6717ac 0x19a671b14 0x19a671f8c 0x19a71a67c 0x19a5d24a0 0x19a6e1c90 0x1035b949c 0x1035b945c 0x1035c8110 0x1035bc9a4 0x1035c9104 0x1035d0100 0x186b7afd0 0x186b7ac20)
libc++abi.dylib: terminating with uncaught exception of type NSException
(lldb)
After discussion in Chat and fixing various issues, I'll take them one by one:
> *** Assertion failure in -[UIApplication _cachedSystemAnimationFenceCreatingIfNecessary:], /BuildRoot/Library/Caches/com.apple.xbs/Sources/UIKit/UIKit-3698.21.8/UIApplication.m:1707
> *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'accessing _cachedSystemAnimationFence requires the main thread'
That's the first issue causing a crash. This is talking about an internal method of CocoaTouch needed to be called in main thread.
The issue lies on your performSegueWithIdentifier:sender:. All UI related calls have to be done in main thread.
To fix it:
dispatch_async(dispatch_get_main_queue(), ^(){
[self performSegueWithIdentifier:#"brandSegue" sender:self];
});
Fixing this revealed a second issue:
it segues but it trigger twice, do you know why this may happen?
You are doing this:
for (size_t i = 0; i < trackableCount; i++)
{
if (somethingTest)
{
[self performSegueWithIdentifier:#"brandSegue" sender:self];
}
}
Who said that in your for loop you don't valid multiple times somethingTest?
To fix it (I'm talking about the logic, I didn't do the dispatch_async(dispatch_get_main_queue(){()} part to avoid adding noise to the algorithm).
//Declare a var before the for loop
BOOL seguedNeedsToBeDone = FALSE;
for (size_t i = 0; i < trackableCount; i++)
{
if (somethingTest)
{
seguedNeedsToBeDone = TRUE;
}
}
//Perform the segue after the for loop if needed
if (seguedNeedsToBeDone)
{
[self performSegueWithIdentifier:#"brandSegue" sender:self];
}
Next issue, passing data to the Destination ViewController:
JSONViewController *destViewController = segue.destinationViewController;
destViewController.brand = #"something";
You are mixing Swift & Objective-C, since XCode was complaining about not knowing brand being a property of JSONViewController object, you needed to add #objc before the declaration of the var. More detailed answer can be found here.
Finally, a tip to pass the data of you for loop is using the sender (it's faster in term of coding than creating another var, etc.):
//Calling the performSegue with custom value to pass
[self performSegueWithIdentifier:#"brandSegue" sender:someVarToSend];
//Passing the custom value
destViewController.brand = someVarToSend;
I guess the more safe way is to use navigationController?.pushViewController(_:animated:), instead of using segues.

NSMutablearray mutated while being enumerated Can you help me in which case this exception occurs in my code

This is an implementation section of a class named "Model". Here I recursively call call the setDictionary method upto 3 layers, which raises an exception (NSMutablearray mutated while being enumerated) this exception can be avoided if I use for loop istead of forin, I would like to understand how this error occurs...Can any one help...Please dont reply with some links that point to some definition, I already read lots of documentation and I dont understand how the exception is raised in this situation.
#implementation Model
#synthesize arraySubOptions,boolHasSub;
- (instancetype)init
{
self = [super init];
if (self) {
boolHasSub = NO;
arraySubOptions = [NSMutableArray new];
}
return self;
}
-(void)setDictionary:(NSDictionary*)dict{
boolHasSub = [[[dict objectForKey:#"key_has_sub"] nullCheck:[NSString class]] boolValue];
if (boolHasSub) {
NSArray * arrayDict = (NSArray*)[self loadDataFromDB];
if(arrayDict && (arrayDict.count>0) ){
for (NSDictionary * dict in arrayDict) {
Model * objOption = [Model new];
[objOption setDictionary:dict];
[arraySubOptions addObject:objOption]; /*This is the line that raises the exception. It raised */
}
}
}
}
#end
Your method
[self loadDataFromDB]
should be altering your arrayDict, so, when you reach
[objOption setDictionary:dict];
is entering twice in that function, modifying your arrayDict and launching this exception.
Doing it with 'for' instead of 'foreach' works because you set the limits of your loop and it doesn't reach any exception if your limit isn't out of bounds. (i > arrayDict.count)
[self loadDataFromDB] returns an array which gets deallocated when the method call ends, since the object was created inside the method. So copying the returned array did the trick.
[[self loadDataFromDB] copy]

UIPickerView in SKScene

I want to design a level select scene in my game. What I want is a very abstract design which consists of a single UIPickerView that has the width of the screen and about 200 px in height.
The following is from LevelSelect.m , which is a subclass of SKScene
-(id)initWithSize:(CGSize)size{
if (self = [super initWithSize:size]) {
/* Setup your scene here */
self.backgroundColor = [SKColor whiteColor];
for (int i = 0; i < 10; i++) {
NSString* levelString = [NSString stringWithFormat:#"Level %i",i];
[levelData setObject:levelString atIndexedSubscript:i];
}
UIPickerView *levelPickerView = [[UIPickerView alloc] initWithFrame:CGRectMake(self.scene.size.width/2, self.scene.size.height/2, self.scene.size.width, 200)];
levelPickerView.dataSource = self;
levelPickerView.delegate = self;
levelPickerView.showsSelectionIndicator = YES;
[self addChild:levelPickerView];
}
return self;
}
I implemented the UIPickerView data source and delegate methods. But when i load the scene i get the following error .
2014-07-23 22:32:41.222 Poppolo[13686:60b] -[UIPickerView setPaused:]: unrecognized selector sent to instance 0x17d90470
2014-07-23 22:32:41.225 Poppolo[13686:60b] * Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[UIPickerView setPaused:]: unrecognized selector sent to instance 0x17d90470'
* First throw call stack:
(0x2dd30f0b 0x384c7ce7 0x2dd34837 0x2dd33137 0x2dc82098 0x304618cb 0xe1f3d 0xe70fb 0x389b0d53 0x389b0d3f 0x389b36c3 0x2dcfb681 0x2dcf9f4d 0x2dc64769 0x2dc6454b 0x32bd16d3 0x305c3891 0xee221 0x389c5ab7)
libc++abi.dylib: terminating with uncaught exception of type NSException
thanks in advance.

accessing a property of an instantiated object with my custom init method

Here's my class with my custom init method:
// Piece.h
#import <Foundation/Foundation.h>
#interface Piece : CCSprite
#property (nonatomic) int pieceNumber;
+(Piece *)initWithPieceImage:(UIImage*)piece pieceName:(int)pName;
#end
// Piece.m
#import "Piece.h"
#implementation Piece
#synthesize pieceNumber = _pieceNumber;
+(id)initWithPieceImage:(UIImage *)piece pieceName:(int)pName
{
return [[[self alloc] initWithPieceImage:piece pieceName:pName] autorelease];
}
-(Piece*)initWithPieceImage:(UIImage *)piece pieceName:(int)pName
{
CCSprite *bgImage = nil;
if ( (self=[super init]) )
{
bgImage = [CCSprite spriteWithCGImage:piece.CGImage
key: [NSString stringWithFormat:#"%i",pName]];
}
return (Piece*)bgImage;
}
#end
I instantiated the Piece class like this to add it to the layer:
Piece *newPiece = [Piece initWithPieceImage:myUIImage pieceName:1];
[newPiece setPieceNumber:2]; //Error in this line
[self addChild: newPiece z:1];
However I have tried it like this and it perfectly works:
Piece *newPiece = [[Piece alloc] init];
[newPiece setPieceNumber:2];
but this is not what I want.
and here is the error I get:
[CCSprite setPieceNumber:]: unrecognized selector sent to instance 0x85f1050
Terminating app due to uncaught exception NSInvalidArgumentException, reason: -[CCSprite setPieceNumber:]: unrecognized selector sent to instance 0x85f1050
Aparently it looks like the problem is how Im trying to init my object.
I'm a newcomer to objective-c so I cant figure out what is wrong here.
any idea of what am I doing wrong here?
How can I achieve this approach and access the properties of my instantiated object with custom init method?
You have a mess in your code. In -(Piece*)initWithPieceImage:(UIImage *)piece pieceName:(int)pName you return a CCSprite object instead of a Piece. You assign self with an object but return another, of an incorrect type.
init returns the correct type (because you haven't reimplemented it), so it works, but you haven't actually initialized the image correctly.
You need to change your method like so:
-(Piece*)initWithPieceImage:(UIImage *)piece pieceName:(int)pName
{
return [super initWithCGImage:piece.CGImage key:[NSString stringWithFormat:#"%i",pName]];
}
It is because in your init method, you are for some reason creating a CCSprite object and returning that instead of the Piece object. Because of that, it will not be an object of your Piece class and will not respond to any of Piece's methods.
Instead of creating a new CCSprite object to set those properties to, you want to set those on self or super.

Resources