Renaissance
Renaissance

Reputation: 554

[__NSCFString setPosition:]: unrecognized selector sent to instance

I'm using one plugin for scrollable layer. It has two files as shown below.

FGScrollLayer.h

#import <Foundation/Foundation.h>
#import "cocos2d.h"

@class FGScrollLayer;
@protocol FGScrollLayerDelegate

@optional

/** Called when scroll layer begins scrolling.
 * Usefull to cancel CCTouchDispatcher standardDelegates.
 */
- (void) scrollLayerScrollingStarted:(FGScrollLayer *) sender;

/** Called at the end of moveToPage:
 */
- (void) scrollLayer: (FGScrollLayer *) sender scrolledToPageNumber: (int) page;

@end

/** Vertical scrolling layer for items.
 *
 * It is a very clean and elegant subclass of CCLayer that lets you pass-in an array
 * of layers and it will then create a smooth scroller.
 * Every sub-layer should have the same size in current version.
 *
 * @version 0.1.01
 */
@interface FGScrollLayer : CCLayer
{
    NSObject <FGScrollLayerDelegate> *delegate_;

    // The screen coord of initial point the user starts their swipe.
    CGFloat startSwipe_;

    // The coord of initial position the user starts their swipe.
    CGFloat startSwipeLayerPos_;

    // For what distance user must slide finger to start scrolling menu.
    CGFloat minimumTouchLengthToSlide_;

    // Internal state of scrollLayer (scrolling or idle).
    int state_;

    BOOL stealTouches_;

#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
    // Holds the touch that started the scroll
    UITouch *scrollTouch_;
#endif

    // Holds pages.
    NSMutableArray *layers_;

    // Holds current pages width offset.
    CGFloat pagesOffset_;

    // Holds the height of every page
    CGFloat pageHeight_;

    // Holds the width of every page
    CGFloat pageWidth_;

    // Holds the maximum upper position
    CGFloat maxVerticalPos_;

    // Holds the real responsible rect in the screen
    CGRect realBound;

    /*Decoration and slide bars*/
    // Scroll bars on the right
    CCSprite* scrollBar;
    CGFloat scrollBarPosY;

    // Scroll block that indicates the current position in whole scorll view content
    CCSprite* scrollBlock;
    CGFloat scrollBlockUpperBound;
    CGFloat scrollBlockLowerBound;

    // Decoration
    // Holds position to maintain their position fixed even in setPosition
    CCSprite* upperBound;
    CGFloat upperBoundPosY;
    CCSprite* lowerBound;
    CGFloat lowerBoundPosY;
}

@property (readwrite, assign) NSObject <FGScrollLayerDelegate> *delegate;

#pragma mark Scroll Config Properties

/** Calibration property. Minimum moving touch length that is enough
 * to cancel menu items and start scrolling a layer.
 */
@property(readwrite, assign) CGFloat minimumTouchLengthToSlide;

/** If YES - when starting scrolling FGScrollLayer will claim touches, that are
 * already claimed by others targetedTouchDelegates by calling CCTouchDispatcher#touchesCancelled
 * Usefull to have ability to scroll with touch above menus in pages.
 * If NO - scrolling will start, but no touches will be cancelled.
 * Default is YES.
 */
@property(readwrite) BOOL stealTouches;

#pragma mark Pages Control Properties

/** Offset, that can be used to let user see next/previous page. */
@property(readwrite) CGFloat pagesOffset;

/** Page height, this version requires that each page shares the same height and width */
@property(readonly) CGFloat pageHeight;
@property(readonly) CGFloat pageWidth;

/** Returns array of pages CCLayer's  */
@property(readonly) NSArray *pages;
- (void) updatePages;
-(void)updatePagesAvailability;
#pragma mark Init/Creation

/** Creates new scrollLayer with given pages & width offset.
 * @param layers NSArray of CCLayers, that will be used as pages.
 * @param pageSize indicates the size of every page, now this version requires each page 
 * share the same page size
 * @param widthOffset Length in X-coord, that describes length of possible pages
 * @param visibleRect indicates the real position and size on the screen
 * intersection. */
+(id) nodeWithLayers:(NSArray *)layers pageSize:(CGSize)pageSize pagesOffset: (int) pOffset visibleRect: (CGRect)rect;

/** Inits scrollLayer with given pages & width offset.
 * @param layers NSArray of CCLayers, that will be used as pages.
 * @param pageSize indicates the size of every page, now this version requires each page
 * share the same page size
 * @param pagesOffset Length in X-coord, that describes length of possible pages
 * @param visibleRect indicates the real position and size on the screen
 * intersection. */
-(id) initWithLayers:(NSArray *)layers pageSize:(CGSize)pageSize pagesOffset: (int) pOffset visibleRect: (CGRect)rect;

#pragma mark Misc
/**
 * Return the number of pages
 */
-(int) totalPagesCount;

#pragma mark Moving/Selecting Pages

/* Moves scrollLayer to page with given number.
 * Does nothing if number >= totalScreens or < 0.
 */
-(void) moveToPage:(int)page;

@end

FGScrollLayer.m

//
//  FGScrollLayer.m
//  Fall G
//
//  Created by Dai Xuefeng on 23/9/12.
//  Copyright 2012 Nofootbird. 
//

#import "FGScrollLayer.h"


enum
{
    kFGScrollLayerStateIdle,
    kFGScrollLayerStateSliding,
};

#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
@interface CCTouchDispatcher (targetedHandlersGetter)

- (id<NSFastEnumeration>) targetedHandlers;

@end

@implementation CCTouchDispatcher (targetedHandlersGetter)

- (id<NSFastEnumeration>) targetedHandlers
{
    return targetedHandlers;
}

@end
#endif

@implementation FGScrollLayer

@synthesize delegate = delegate_;
@synthesize minimumTouchLengthToSlide = minimumTouchLengthToSlide_;
@synthesize pagesOffset = pagesOffset_;
@synthesize pages = layers_;
@synthesize stealTouches = stealTouches_;
@synthesize pageHeight = pageHeight_;
@synthesize pageWidth = pageWidth_;

- (int) totalPagesCount
{
    return [layers_ count];
}

+(id) nodeWithLayers:(NSArray *)layers pageSize:(CGSize)pageSize pagesOffset:(int)pOffset visibleRect:(CGRect)rect{
    return [[[self alloc] initWithLayers: layers pageSize:pageSize pagesOffset:pOffset visibleRect:rect] autorelease];
}

-(id) initWithLayers:(NSArray *)layers pageSize:(CGSize)pageSize pagesOffset:(int)pOffset visibleRect:(CGRect)rect{
    if ( (self = [super init]) )
    {
        NSAssert([layers count], @"FGScrollLayer#initWithLayers:widthOffset: you must provide at least one layer!");

        // Enable Touches/Mouse.
#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
        self.isTouchEnabled = YES;
#endif

        self.stealTouches = YES;

        // Set default minimum touch length to scroll.
        self.minimumTouchLengthToSlide = 30.0f;

        // Save offset.
        self.pagesOffset = pOffset;

        // Save array of layers.
        layers_ = [[NSMutableArray alloc] initWithArray:layers copyItems:NO];

        // Save pages size for later calculation
        pageHeight_ = pageSize.height;
        pageWidth_ = pageSize.width;
        maxVerticalPos_ = pageHeight_ * [layers_ count] - rect.size.height + 5;

        realBound = rect;

        [self updatePages];
    }
    return self;
}

- (void) dealloc
{
    self.delegate = nil;

    [layers_ release];
    layers_ = nil;

    [super dealloc];
}

- (void) updatePages
{
    // Loop through the array and add the screens if needed.
    int i = 0;
    for (CCLayer *l in layers_)
    {
        l.position = ccp(realBound.origin.x,  realBound.origin.y + (realBound.size.height - i * (pageHeight_ - self.pagesOffset)));
        if (!l.parent){
            [self addChild:l];
        }

        i++;
    }
    [self updatePagesAvailability];
}

/**
 * According to current position, decide which pages are visible
 */
-(void)updatePagesAvailability{
    CGPoint currentPos = [self position];
    if (currentPos.y > 0) {
        int visibleBoundUp = currentPos.y / pageHeight_;
        visibleBoundUp = MIN([layers_ count], visibleBoundUp);
        for (int i = 0; i < visibleBoundUp; i++) {
            [[layers_ objectAtIndex:i] setVisible:NO];
        }
        if (visibleBoundUp < [layers_ count]) {
            int visibleBoundDown = (currentPos.y + realBound.size.height) / pageHeight_;
            visibleBoundDown = MIN([layers_ count] - 1, visibleBoundDown);
            for (int i = visibleBoundUp; i <= visibleBoundDown; i++) {
                [[layers_ objectAtIndex:i] setVisible:YES];
            }
            if (visibleBoundDown < [layers_ count] - 1) {
                for (int i = visibleBoundDown + 1; i <= [layers_ count] - 1; i++) {
                    [[layers_ objectAtIndex:i] setVisible:NO];
                }
            }
        }
    }
    else if (currentPos.y <= 0){
        CGFloat gapY = -currentPos.y;
        int visibleBound = (realBound.size.height - gapY) / pageHeight_;
        // index visibleBound itself should be invisible
        if (visibleBound < 0) {
            for (int i = 0; i < [layers_ count]; i++) {
                [[layers_ objectAtIndex:i] setVisible:NO];
            }
            return;
        }
        visibleBound = MIN([layers_ count] - 1, visibleBound);
        for (int i = 0; i <= visibleBound; i++) {
            [[layers_ objectAtIndex:i] setVisible:YES];
        }
        for (int i = visibleBound + 1; i < [layers_ count]; i++) {
            [[layers_ objectAtIndex:i] setVisible:NO];
        }
    }
}

-(void)setRealBound:(CGPoint)position size:(CGPoint)size{
    realBound = CGRectMake(position.x, position.y, size.x, size.y);
}

-(void)setPosition:(CGPoint)position{
    [super setPosition:position];
    [self updatePagesAvailability];
    CGFloat scrollBlockDesiredY = scrollBlockUpperBound - (scrollBlockUpperBound - scrollBlockLowerBound) * position.y / maxVerticalPos_;
    if (scrollBlockDesiredY > scrollBlockUpperBound) {
        scrollBlockDesiredY = scrollBlockUpperBound;
    }else if (scrollBlockDesiredY < scrollBlockLowerBound){
        scrollBlockDesiredY = scrollBlockLowerBound;
    }
    [scrollBlock setPosition:ccp([scrollBlock position].x, scrollBlockDesiredY - position.y)];
    [lowerBound setPosition:ccp([lowerBound position].x, lowerBoundPosY - position.y)];
    [upperBound setPosition:ccp([upperBound position].x, upperBoundPosY - position.y)];
    [scrollBar setPosition:ccp([scrollBar position].x, scrollBarPosY - position.y)];
}

#pragma mark Moving To / Selecting Pages

-(void) moveToPage:(int)page
{
    if (page < 0 || page >= [layers_ count]) {
        CCLOGERROR(@"FGScrollLayer#moveToPage: %d - wrong page number, out of bounds. ", page);
        return;
    }

    CGFloat desiredPos = page * pageHeight_;
    if (desiredPos > maxVerticalPos_) {
        desiredPos = maxVerticalPos_;
    }

    [self runAction:[CCMoveTo actionWithDuration:0.3 position:ccp([self position].x, desiredPos)]];

}

#pragma mark Touches
#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED

/** Register with more priority than CCMenu's but don't swallow touches. */
-(void) registerWithTouchDispatcher
{
#if COCOS2D_VERSION >= 0x00020000
    CCTouchDispatcher *dispatcher = [[CCDirector sharedDirector] touchDispatcher];
    int priority = kCCMenuHandlerPriority - 1;
#else
    CCTouchDispatcher *dispatcher = [CCTouchDispatcher sharedDispatcher];
    int priority = kCCMenuTouchPriority - 1;
#endif

    [dispatcher addTargetedDelegate:self priority: priority swallowsTouches:NO];
}

/** Hackish stuff - stole touches from other CCTouchDispatcher targeted delegates.
 Used to claim touch without receiving ccTouchBegan. */
- (void) claimTouch: (UITouch *) aTouch
{
#if COCOS2D_VERSION >= 0x00020000
    CCTouchDispatcher *dispatcher = [[CCDirector sharedDirector] touchDispatcher];
#else
    CCTouchDispatcher *dispatcher = [CCTouchDispatcher sharedDispatcher];
#endif

    // Enumerate through all targeted handlers.
    for ( CCTargetedTouchHandler *handler in [dispatcher targetedHandlers] )
    {
        // Only our handler should claim the touch.
        if (handler.delegate == self)
        {
            if (![handler.claimedTouches containsObject: aTouch])
            {
                [handler.claimedTouches addObject: aTouch];
            }
        }
        else
        {
            // Steal touch from other targeted delegates, if they claimed it.
            if ([handler.claimedTouches containsObject: aTouch])
            {
                if ([handler.delegate respondsToSelector:@selector(ccTouchCancelled:withEvent:)])
                {
                    [handler.delegate ccTouchCancelled: aTouch withEvent: nil];
                }
                [handler.claimedTouches removeObject: aTouch];
            }
        }
    }
}

-(void)ccTouchCancelled:(UITouch *)touch withEvent:(UIEvent *)event
{
    [scrollBar setVisible:NO];
    [scrollBlock setVisible:NO];

    if( scrollTouch_ == touch ) {
        scrollTouch_ = nil;
    }
}

// these two variables are to make a sliding effect on scroll view
static CGFloat previousTouchPointY = -1;
static CGFloat moveSpeed = 0;
-(BOOL) ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event
{
    if( scrollTouch_ == nil ) {
        scrollTouch_ = touch;
    } else {
        return NO;
    }

    CGPoint touchPoint = [touch locationInView:[touch view]];
    touchPoint = [[CCDirector sharedDirector] convertToGL:touchPoint];

    startSwipe_ = touchPoint.y;
    startSwipeLayerPos_ = [self position].y;
    state_ = kFGScrollLayerStateIdle;
    return YES;
}

- (void)ccTouchMoved:(UITouch *)touch withEvent:(UIEvent *)event
{
    if( scrollTouch_ != touch ) {
        return;
    }

    CGPoint touchPoint = [touch locationInView:[touch view]];
    touchPoint = [[CCDirector sharedDirector] convertToGL:touchPoint];


    // If finger is dragged for more distance then minimum - start sliding and cancel pressed buttons.
    // Of course only if we not already in sliding mode
    if ( (state_ != kFGScrollLayerStateSliding)
        && (fabsf(touchPoint.y-startSwipe_) >= self.minimumTouchLengthToSlide) )
    {
        state_ = kFGScrollLayerStateSliding;

        // Avoid jerk after state change.
        startSwipe_ = touchPoint.y;
        startSwipeLayerPos_ = [self position].y;
        previousTouchPointY = touchPoint.y;

        if (self.stealTouches)
        {
            [self claimTouch: touch];
        }

        if ([self.delegate respondsToSelector:@selector(scrollLayerScrollingStarted:)])
        {
            [self.delegate scrollLayerScrollingStarted: self];
        }
    }

    if (state_ == kFGScrollLayerStateSliding)
    {
        CGFloat desiredY = startSwipeLayerPos_ + touchPoint.y - startSwipe_;
        [self setPosition:ccp(0, desiredY)];

        // enable scroll bar to be visible
        [scrollBar setVisible:YES];
        [scrollBlock setVisible:YES];

        // update scrolling effect variables
        moveSpeed = touchPoint.y - previousTouchPointY;
        previousTouchPointY = touchPoint.y;
    }
}

/**
 * After touching, generate an inertia effect.
 */
- (void)moveToDesiredPos:(CGFloat)desiredY{
    CCAction* slidingAction = nil;
    if (desiredY > maxVerticalPos_) {
        slidingAction = [CCSequence actions:[CCMoveTo actionWithDuration:0.10 position:ccp([self position].x, desiredY)], [CCMoveTo actionWithDuration:0.15 position:ccp([self position].x, maxVerticalPos_)], nil];
    }
    else if (desiredY < 0){
        slidingAction = [CCSequence actions:[CCMoveTo actionWithDuration:0.10 position:ccp([self position].x, desiredY)],[CCMoveTo actionWithDuration:0.15 position:ccp([self position].x, 0)], nil];
    }
    else{
        CGFloat interPosY = (desiredY - [self position].y) * 0.7 + [self position].y;
        slidingAction = [CCSequence actions:[CCMoveTo actionWithDuration:0.15 position:ccp([self position].x, interPosY)],[CCMoveTo actionWithDuration:0.3 position:ccp([self position].x, desiredY)], nil];
    }
    [self runAction:slidingAction];
}

- (void)ccTouchEnded:(UITouch *)touch withEvent:(UIEvent *)event
{
    [scrollBar setVisible:NO];
    [scrollBlock setVisible:NO];

    if( scrollTouch_ != touch )
        return;
    scrollTouch_ = nil;

    if (ABS(moveSpeed) > 10) {
        CGFloat desiredDesY = [self position].y + moveSpeed * 5;
        [self moveToDesiredPos:desiredDesY];
    }
    else{
        if ([self position].y > maxVerticalPos_) {
            [self runAction:[CCMoveTo actionWithDuration:0.3 position:ccp([self position].x, maxVerticalPos_)]];
        }else if ([self position].y < 0){
            [self runAction:[CCMoveTo actionWithDuration:0.3 position:ccp([self position].x, 0)]];
        }
    }

    // restore scrolling effect variables to default value
    moveSpeed = 0;
    previousTouchPointY = -1;
}

#endif

@end

Now in my HelloWorldLayer.m init i do like Following:

-(id) init
{
    if( (self=[super init]) ) {
        NSMutableArray *persons = [NSMutableArray array];
        for (int i = 0; i < 10; i++) {
            [persons addObject:[NSString stringWithFormat:@"%d",i]];
        }

        NSArray *arrayOfPersons = [NSArray arrayWithArray:persons];

        //scrollNode.position = ccp(0, 0);


        scrollNode = [FGScrollLayer nodeWithLayers:arrayOfPersons pageSize:CGSizeMake(100, 20) pagesOffset:1 visibleRect:CGRectMake(30, 30, 500, 900)];




    }
    return self;
}

But it is giving error:-[__NSCFString setPosition:]: unrecognized selector sent to instance 0x960e170

I searched for this error and i came to know that it is Fundamental Subclassing error., Can anybody guide me to solve this issue?

Upvotes: 0

Views: 525

Answers (1)

CodeSmile
CodeSmile

Reputation: 64478

You create an array of persons. They're all NSString objects. You pass that array (after needlessly copying it) into the scroll layer class which expects an array of CCLayer objects.

Upvotes: 3

Related Questions