我正在使用一个插件用于可滚动图层。它有两个文件,如下所示。
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
现在在我的 HelloWorldLayer.m 初始化我喜欢以下内容:
-(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;
}
但它给出错误: - [__ NSCFString setPosition:]:无法识别的选择器发送到实例0x960e170
我搜索了这个错误,我发现它是 Fundamental Subclassing错误。, 任何人都可以指导我解决这个问题吗?
答案 0 :(得分:3)
您创建了一系列人员。它们都是NSString对象。您将该数组(在不必要地复制之后)传递到需要CCLayer对象数组的滚动图层类中。