In my iPad app, I have the following function:
+ (void)addDot:(Dot *)d {
if(dots == nil)
dots = [[NSMutableArray alloc] init];
NSLog(#"before adding, dots = %#",dots);
NSLog(#"adding dot %#",d);
[dots addObject:d];
NSLog(#"dots is now %#",dots);
}
Note that printing a dot results in the x,y coordinates separated by a space.
Everytime the subject taps the screen, a dot is drawn, and this method is called, which adds a dot. Note that dots is defined as static NSMutableArray *dots at the top of the class this function is in. Something weird is going on. Every element of the array is replaced. Look at this output from NSLog at the very beginning after tapping the screen, making 2 dots:
before adding, dots = (
)
2012-02-13 23:58:48.159 MoreMost[520:707] adding dot 418.000000 548.000000
2012-02-13 23:58:48.161 MoreMost[520:707] dots is now (
"418.000000 548.000000"
)
2012-02-13 23:58:48.748 MoreMost[520:707] before adding, dots = (
"635.000000 410.000000"
)
2012-02-13 23:58:48.749 MoreMost[520:707] adding dot 635.000000 410.000000
2012-02-13 23:58:48.750 MoreMost[520:707] dots is now (
"635.000000 410.000000",
"635.000000 410.000000"
)
See how the whole array is being replaced with the incoming element? Why is this? And no, the function is not being called anywhere else in the code. It is only called once, each time the user taps the screen to draw a dot at that location).
Note that the dots are not being used anywhere else in the program. Only this function.
Here is the implementation of Dot:
#import "Dot.h"
#implementation Dot
#synthesize tapPosition;
CGRect frame;
UIColor *dotColor;
float radius = 20;
- (id)initWithTapPosition:(CGPoint)pt color:(UIColor *)col {
if(self = [super init]) {
tapPosition = pt;
float topLeftX = pt.x - radius;
float topLeftY = pt.y - radius;
if(topLeftX + (radius*2) >= 1024)
return nil;
if(topLeftY + (radius*2) >= 728)
return nil;
if(topLeftY <= 0 || topLeftX <= 0)
return nil;
frame = CGRectMake(topLeftX, topLeftY, radius*2, radius*2);
dotColor = col;
}
return self;
}
- (id)copyWithZone:(NSZone *)zone
{
Dot *dot = [[[self class] allocWithZone:zone] initWithTapPosition:tapPosition color:dotColor];
return dot;
}
- (CGRect)getFrame {
return frame;
}
- (UIColor *)getColor {
return dotColor;
}
- (NSString *)description {
return [NSString stringWithFormat:#"%f %f",frame.origin.x,frame.origin.y];
}
#end
and the header:
#import <Foundation/Foundation.h>
#interface Dot : NSObject {
#public
CGPoint tapPosition;
}
#property (nonatomic, assign) CGPoint tapPosition;
- (CGRect)getFrame;
- (id)initWithTapPosition:(CGPoint)pt color:(UIColor *)col;
- (UIColor *)getColor;
#end
Move the
CGRect frame;
UIColor *dotColor;
CGPoint tapPosition;
float radius = 20;
from #implementation to the #interface:
#interface Dot : NSObject
{
CGRect frame;
// etc.
}
...
#end
The way you're declaring them make them actually "global variables", so all Dot instances will share the same value. Putting them in the #interface make them "instance variables" instead so each Dot can have different values.
Old answer
Note that
[dots addObject:d];
only adds a reference of the dot d. If you modify the dot in-place later e.g.
d.x = 123;
d.y = 456;
then the one in the array will see the same change also.
You need to add a copy instead, e.g.
[dots addObject:[d copy]];
// note: you need to implement -copyWithZone: with the <NSCopying> protocol
Your dots class needs to be modified so that the tapPosition is not a static variable.
.h
#interface Dot : NSObject{
#public
CGPoint tapPosition;
}
#property (nonatomic, assign) CGPoint tapPosition;
#end
.m
#implementation Dot
#synthesize tapPosition;
//...
#end
Related
I am creating this IB_Designable class. It is like a slider. See picture. Both elements are created with little stretchable UIImages.
I have this red square that is 66x66 pt. This square has to slide in X inside the gray rectangle.
I have create this class:
HEADER
#import <UIKit/UIKit.h>
IB_DESIGNABLE
#interface MyClass : UIView
// minimum and maximum slider value
#property (assign, nonatomic) IBInspectable CGFloat minimumValue;
#property (assign, nonatomic) IBInspectable CGFloat maximumValue;
#property (assign, nonatomic) IBInspectable CGFloat value;
#end
IMPLEMENTATION
#import "MyClass.h"
#interface MyClass() {
__weak IBOutlet UIView *topContainer;
CGFloat minimumThumbCoordinate;
CGFloat maximumThumbCoordinate;
}
#property (weak, nonatomic) IBOutlet UIImageView *thumb;
#end
#implementation MyClass
- (void)awakeFromNib {
[super awakeFromNib];
// topContainer contains everything
CGRect topContainerBounds = [topContainer bounds];
CGRect thumbBounds = [self.thumb bounds];
CGFloat topContainerWidth = CGRectGetWidth(topContainerBounds);
CGFloat thumbWidth = CGRectGetWidth(thumbBounds);
minimumThumbCoordinate = floorf(thumbWidth / 2.0f);
maximumThumbCoordinate = floorf(topContainerWidth - minimumThumbCoordinate);
}
-(void)setValue:(CGFloat)value {
if ((value < self.minimumValue) || (value > self.maximumValue)) return;
// normalize values
CGFloat minimumValueNormalized = self.minimumValue;
CGFloat maximumValueNormalized = self.maximumValue;
CGFloat desiredValue = value;
if ((minimumValueNormalized < 0) && (maximumValueNormalized > 0)) {
CGFloat absoluteMinimum = fabsf(self.minimumValue);
minimumValueNormalized = 0;
maximumValueNormalized += absoluteMinimum;
desiredValue += absoluteMinimum;
}
CGFloat percentage = desiredValue/maximumValueNormalized;
// find coordinate
CGFloat coordinateRange = maximumThumbCoordinate - minimumThumbCoordinate;
CGFloat relativeCoordinate = percentage * coordinateRange;
CGPoint center = CGPointMake(relativeCoordinate, self.thumb.center.y);
[self.thumb setCenter: center];
}
The problem is that the setValue method does not make the thumb move when I set the value on interface builder... any ideas?
You cannot have components of your thumbnail added in your storyboard. In this case, I suspect your thumb image view outlet is nil while it's being previewed in IB, and thus changing the value it is likely not updating any subview.
You generally get around this by adding the subviews programmatically, e.g. something like:
// MyClass.h
#import <UIKit/UIKit.h>
IB_DESIGNABLE
#interface MyClass : UIView
#property (nonatomic, strong) IBInspectable UIImage *thumbnailImage;
#property (nonatomic) IBInspectable CGFloat minimumValue;
#property (nonatomic) IBInspectable CGFloat maximumValue;
#property (nonatomic) IBInspectable CGFloat value;
#property (nonatomic, readonly) CGFloat percent;
#end
And
// MyClass.m
#import "MyClass.h"
#interface MyClass ()
#property (weak, nonatomic) UIImageView *thumbnailView;
#property (nonatomic) CGFloat thumbWidth;
#end
#implementation MyClass
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
[self configureView];
}
return self;
}
- (instancetype)initWithCoder:(NSCoder *)coder {
self = [super initWithCoder:coder];
if (self) {
[self configureView];
}
return self;
}
- (void)configureView {
UIImageView *thumb = [[UIImageView alloc] initWithImage:self.thumbnailImage];
[self addSubview:thumb];
thumb.backgroundColor = [UIColor lightGrayColor]; // in case no image has been set
thumb.clipsToBounds = true; // in case you change your `contentMode`
thumb.contentMode = UIViewContentModeScaleToFill;
_thumbnailView = thumb;
_thumbWidth = 44; // maybe you have another IBInspectable property for this...
[self layoutThumbnail];
}
- (void)setThumbnailImage:(UIImage *)image {
self.thumbnailView.image = image;
}
- (CGFloat)percent {
CGFloat range = self.maximumValue - self.minimumValue;
return range ? (self.value - self.minimumValue) / range : 0;
}
- (void)layoutSubviews {
[super layoutSubviews];
[self layoutThumbnail];
}
- (void)layoutThumbnail {
CGFloat minX = self.thumbWidth / 2;
CGFloat maxX = self.bounds.size.width - self.thumbWidth / 2;
CGRect frame = CGRectMake(self.percent * (maxX - minX) + minX - self.thumbWidth / 2, 0, self.thumbWidth, self.bounds.size.height);
self.thumbnailView.frame = frame;
}
- (void)setValue:(CGFloat)value {
if (value < self.minimumValue)
_value = self.minimumValue;
else if (value > self.maximumValue) {
_value = self.maximumValue;
} else {
_value = value;
}
[self layoutThumbnail];
}
#end
Note, I update the thumbnail frame not only when you change the value, but also upon layoutSubviews. You want to make sure that the thumbnail frame updates if your control changes size at runtime (e.g. user rotates the device and the control changes size). I also made the thumbnail image a property so you could set this in IB, too, if you wanted. But maybe you have some hardcoded/default image that you use. I also thought that the percent that we use for laying out the thumbnail might potentially be useful by the code that used that control, so I exposed that property.
Now, you're using image views, but if all you wanted were rounded corners of the main control and the thumbnail, I wouldn't use images, but rather just round the corners of the layer for the appropriate UIView objects. Also, I was unclear about your intent of certain things (e.g., I'm not sure what your topContainer is referring to ... you wouldn't generally reference views outside the view hierarchy of the designable view). And you reference the top level view being an image view, which you'd do if you wanted a background image there, too.
But those are details that aren't relevant to your broader question. Hopefully this illustrates the idea, that if you create the subviews programmatically, you can see the thumbnail move as you change your IBInspectable value.
I have seen some implementations here where people wanted to define all of the view hierarchy of the designable view with outlets. But in that case, you have a self-contained NIB for all of these subviews. If you search StackOverflow for "IBDesignable NIB", you'll see links to related answers.
I am migrating all my games to cocos2d 3.0. I used SlidingMenuGrid.m with slight modification.
SlidingMenuGrid.h:
//
// SlidingMenuGrid
//
// Created by Brandon Reynolds on 1/9/11.
#import "cocos2d.h"
#interface SlidingMenuGrid : CCLayer
{
tCCMenuState state; // State of our menu grid. (Eg. waiting, tracking touch, cancelled, etc)
CCMenuItem *selectedItem; // Menu item that was selected/active.
CGPoint padding; // Padding in between menu items.
CGPoint menuOrigin; // Origin position of the entire menu grid.
CGPoint touchOrigin; // Where the touch action began.
CGPoint touchStop; // Where the touch action stopped.
int iPageCount; // Number of pages in this grid.
int iCurrentPage; // Current page of menu items being viewed.
bool bMoving; // Is the grid currently moving?
bool bSwipeOnlyOnMenu; // Causes swiping functionality to only work when siping on top of the menu items instead of entire screen.
bool bVerticalPaging; // Disabled by default. Allows for pages to be scrolled vertically instead of horizontal.
float fMoveDelta; // Distance from origin of touch and current frame.
float fMoveDeadZone; // Amount they need to slide the grid in order to move to a new page.
float fAnimSpeed; // 0.0-1.0 value determining how slow/fast to animate the paging.
CGPoint pageIndicatorPosition;
}
//use this 5:
-(int) getTotalPage;
-(int) getCurrentPage;
-(void) moveToPage: (int) page animated: (BOOL) animated;
+(id) packWithArray: (NSMutableArray*) items posY: (int) posY indicatorPosY: (int) indiPosY;
+(id) levelWithArray: (NSMutableArray*) items cols: (int) cols rows: (int) rows leftEdge: (int) leftEdge upperEdge: (int) upperEdge lowerEdge: (int) lowerEdge indicatorPosY: (int) indiPosY;
+(id) menuWithArray:(NSMutableArray*)items cols:(int)cols rows:(int)rows position:(CGPoint)pos padding:(CGPoint)pad pageIndicatorPosition:(CGPoint)pip;
-(id) initWithArray:(NSMutableArray*)items cols:(int)cols rows:(int)rows position:(CGPoint)pos padding:(CGPoint)pad verticalPaging:(bool)vertical pageIndicatorPosition:(CGPoint)pip;
-(void) buildGrid:(int)cols rows:(int)rows;
-(void) buildGridVertical:(int)cols rows:(int)rows;
-(CCMenuItem*) GetItemWithinTouch:(UITouch*)touch;
- (CGPoint) GetPositionOfCurrentPageWithOffset:(float)offset;
- (CGPoint) GetPositionOfCurrentPage;
- (bool) IsSwipingOnMenuOnlyEnabled;
- (void) SetSwipingOnMenuOnly:(bool)bValue;
- (float) GetSwipeDeadZone;
- (void) SetSwipeDeadZone:(float)fValue;
- (bool) IsVerticallyPaged;
- (void) SetVerticalPaging:(bool)bValue;
#property (nonatomic, readwrite) CGPoint padding;
#property (nonatomic, readwrite) CGPoint menuOrigin;
#property (nonatomic, readwrite) CGPoint touchOrigin;
#property (nonatomic, readwrite) CGPoint touchStop;
#property (nonatomic, readwrite) int iPageCount;
#property (nonatomic, readwrite) int iCurrentPage;
#property (nonatomic, readwrite) bool bMoving;
#property (nonatomic, readwrite) bool bSwipeOnlyOnMenu;
#property (nonatomic, readwrite) bool bVerticalPaging;
#property (nonatomic, readwrite) float fMoveDelta;
#property (nonatomic, readwrite) float fMoveDeadZone;
#property (nonatomic, readwrite) float fAnimSpeed;
#end
SlidingMenuGrid.m
#import "SlidingMenuGrid.h"
#import "Constants.h"
#implementation SlidingMenuGrid
#synthesize padding;
#synthesize menuOrigin;
#synthesize touchOrigin;
#synthesize touchStop;
#synthesize bMoving;
#synthesize bSwipeOnlyOnMenu;
#synthesize fMoveDelta;
#synthesize fMoveDeadZone;
#synthesize iPageCount;
#synthesize iCurrentPage;
#synthesize bVerticalPaging;
#synthesize fAnimSpeed;
-(int) getTotalPage {
return iPageCount;
}
-(int) getCurrentPage {
return iCurrentPage;
}
+(id) packWithArray: (NSMutableArray*) items posY: (int) posY indicatorPosY: (int) indiPosY {
CGSize size = [CCDirector sharedDirector].winSize;
BOOL vertical = NO;
//pos, padding, pageindicatorpos
CGPoint pos = ccp(size.width / 2, posY);
CGPoint posIndicator = ccp(size.width / 2, indiPosY);
//padding no matter
return [SlidingMenuGrid menuWithArray:items cols:1 rows:1 position:pos padding:ccp(0, 0) verticalPaging:vertical pageIndicatorPosition:posIndicator];
}
+(id) levelWithArray: (NSMutableArray*) items cols: (int) cols rows: (int) rows leftEdge: (int) leftEdge upperEdge: (int) upperEdge lowerEdge: (int) lowerEdge indicatorPosY: (int) indiPosY {
CGSize screen = [CCDirector sharedDirector].winSize;
BOOL vertical = NO;
CGSize itemSize = [[items objectAtIndex:0] boundingBox].size;
//pos, padding, pageindipos
CGPoint posIndicator = ccp(screen.width / 2, indiPosY);
int posx = leftEdge + itemSize.width / 2;
int posy = screen.height - (upperEdge + itemSize.height / 2);
int padx = (screen.width - itemSize.width - 2 * leftEdge) / (cols - 1);
int pady = (screen.height - upperEdge - lowerEdge - itemSize.height) / (rows - 1);
return [SlidingMenuGrid menuWithArray:items cols:cols rows:rows position:ccp(posx, posy) padding:ccp(padx, pady) verticalPaging:vertical pageIndicatorPosition:posIndicator];
}
+(id) menuWithArray:(NSMutableArray*)items cols:(int)cols rows:(int)rows position:(CGPoint)pos padding:(CGPoint)pad pageIndicatorPosition:(CGPoint)pip
{
return [[[self alloc] initWithArray:items cols:cols rows:rows position:pos padding:pad verticalPaging:false pageIndicatorPosition:pip] autorelease];
}
+(id) menuWithArray:(NSMutableArray*)items cols:(int)cols rows:(int)rows position:(CGPoint)pos padding:(CGPoint)pad verticalPaging:(bool)vertical pageIndicatorPosition: (CGPoint) pip
{
return [[[self alloc] initWithArray:items cols:cols rows:rows position:pos padding:pad verticalPaging:vertical pageIndicatorPosition:pip] autorelease];
}
-(id) initWithArray:(NSMutableArray*)items cols:(int)cols rows:(int)rows position:(CGPoint)pos padding:(CGPoint)pad verticalPaging:(bool)vertical pageIndicatorPosition:(CGPoint)pip
{
if ((self = [super init]))
{
self.isTouchEnabled = YES;
int z = 1;
for (id item in items)
{
[self addChild:item z:z tag:z];
++z;
}
padding = pad;
iCurrentPage = 0;
bMoving = false;
bSwipeOnlyOnMenu = false;
menuOrigin = pos;
fMoveDeadZone = 10;
bVerticalPaging = vertical;
fAnimSpeed = 0.6f;
(bVerticalPaging) ? [self buildGridVertical:cols rows:rows] : [self buildGrid:cols rows:rows];
self.position = menuOrigin;
pageIndicatorPosition = pip;
}
return self;
}
-(void) dealloc
{
[super dealloc];
}
-(void) buildGrid:(int)cols rows:(int)rows
{
CGSize winSize = [[CCDirector sharedDirector] winSize];
int col = 0, row = 0;
for (CCMenuItem* item in self.children)
{
// Calculate the position of our menu item.
item.position = CGPointMake(self.position.x + col * padding.x + (iPageCount * winSize.width), self.position.y - row * padding.y);
// Increment our positions for the next item(s).
++col;
if (col == cols)
{
col = 0;
++row;
if( row == rows )
{
iPageCount++;
col = 0;
row = 0;
}
}
}
if([self children].count > rows * cols * iPageCount) iPageCount++;
}
-(void) buildGridVertical:(int)cols rows:(int)rows
{
CGSize winSize = [[CCDirector sharedDirector] winSize];
int col = 0, row = 0;
for (CCMenuItem* item in self.children)
{
// Calculate the position of our menu item.
item.position = CGPointMake(self.position.x + col * padding.x , self.position.y - row * padding.y + (iPageCount * winSize.height));
// Increment our positions for the next item(s).
++col;
if (col == cols)
{
col = 0;
++row;
if( row == rows )
{
iPageCount++;
col = 0;
row = 0;
}
}
}
if([self children].count > rows * cols * iPageCount) iPageCount++;
}
-(void) addChild:(CCMenuItem*)child z:(int)z tag:(int)aTag
{
return [super addChild:child z:z tag:aTag];
}
-(CCMenuItem*) GetItemWithinTouch:(UITouch*)touch
{
// Get the location of touch.
CGPoint touchLocation = [[CCDirector sharedDirector] convertToGL: [touch locationInView: [touch view]]];
// Parse our menu items and see if our touch exists within one.
for (CCMenuItem* item in [self children])
{
//only deal with the item
if ([item isKindOfClass:[CCMenuItem class]]) {
CGPoint local = [item convertToNodeSpace:touchLocation];
CGRect r = [item rect];
r.origin = CGPointZero;
// If the touch was within this item. Return the item.
if (CGRectContainsPoint(r, local))
{
return item;
}
}
}
// Didn't touch an item.
return nil;
}
-(void) registerWithTouchDispatcher
{
[[CCDirector sharedDirector].touchDispatcher addTargetedDelegate:self priority:kCCMenuHandlerPriority swallowsTouches:NO];
}
-(BOOL) ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event
{
// Convert and store the location the touch began at.
touchOrigin = [[CCDirector sharedDirector] convertToGL:[touch locationInView:[touch view]]];
// If we weren't in "waiting" state bail out.
if (state != kCCMenuStateWaiting)
{
return NO;
}
// Activate the menu item if we are touching one.
selectedItem = [self GetItemWithinTouch:touch];
[selectedItem selected];
// Only track touch if we are either in our menu system or dont care if they are outside of the menu grid.
if (!bSwipeOnlyOnMenu || (bSwipeOnlyOnMenu && selectedItem) )
{
state = kCCMenuStateTrackingTouch;
return YES;
}
return NO;
}
// Touch has ended. Process sliding of menu or press of menu item.
-(void) ccTouchEnded:(UITouch *)touch withEvent:(UIEvent *)event
{
// User has been sliding the menu.
if( bMoving )
{
bMoving = false;
// Do we have multiple pages?
if( iPageCount > 1 && (fMoveDeadZone < abs(fMoveDelta)))
{
// Are we going forward or backward?
bool bForward = (fMoveDelta < 0) ? true : false;
// Do we have a page available?
if(bForward && (iPageCount>iCurrentPage+1))
{
// Increment currently active page.
iCurrentPage++;
}
else if(!bForward && (iCurrentPage > 0))
{
// Decrement currently active page.
iCurrentPage--;
}
}
// Start sliding towards the current page.
[self moveToCurrentPage];
}
// User wasn't sliding menu and simply tapped the screen. Activate the menu item.
else
{
[selectedItem unselected];
[selectedItem activate];
}
// Back to waiting state.
state = kCCMenuStateWaiting;
}
-(void) moveToPage: (int) page animated:(BOOL)animated {
float interval = 0;
if (animated) {
interval = 0.3f * abs(iCurrentPage - page);
}
iCurrentPage = page;
// Perform the action
CGPoint position = [self GetPositionOfCurrentPage];
CCMoveTo* action = [CCMoveTo actionWithDuration:interval position:position];
[self runAction:action];
}
// Run the action necessary to slide the menu grid to the current page.
- (void) moveToCurrentPage
{
CGSize winSize = [[CCDirector sharedDirector] winSize];
// Perform the action
CGPoint position = [self GetPositionOfCurrentPage];
CCMoveTo* action = [CCMoveTo actionWithDuration:fAnimSpeed * 0.3f position:position];
[self runAction:action];
}
-(void) ccTouchCancelled:(UITouch *)touch withEvent:(UIEvent *)event
{
[selectedItem unselected];
state = kCCMenuStateWaiting;
}
-(void) ccTouchMoved:(UITouch *)touch withEvent:(UIEvent *)event
{
// Calculate the current touch point during the move.
touchStop = [[CCDirector sharedDirector] convertToGL:[touch locationInView:[touch view]]];
// Distance between the origin of the touch and current touch point.
fMoveDelta = (bVerticalPaging) ? (touchStop.y - touchOrigin.y) : (touchStop.x - touchOrigin.x);
// Set our position.
[self setPosition:[self GetPositionOfCurrentPageWithOffset:fMoveDelta]];
bMoving = true;
if (selectedItem) {
[selectedItem unselected];
selectedItem = nil;
}
}
- (CGPoint) GetPositionOfCurrentPage
{
CGSize winSize = [[CCDirector sharedDirector] winSize];
return (bVerticalPaging) ?
CGPointMake(menuOrigin.x,menuOrigin.y-(iCurrentPage*winSize.height))
: CGPointMake((menuOrigin.x-(iCurrentPage*winSize.width)),menuOrigin.y);
}
- (CGPoint) GetPositionOfCurrentPageWithOffset:(float)offset
{
CGSize winSize = [[CCDirector sharedDirector] winSize];
return (bVerticalPaging) ?
CGPointMake(menuOrigin.x,menuOrigin.y-(iCurrentPage*winSize.height)+offset)
: CGPointMake((menuOrigin.x-(iCurrentPage*winSize.width)+offset),menuOrigin.y);
}
// Returns whether or not we should only allow swiping on the actual grid.
- (bool) IsSwipingOnMenuOnlyEnabled
{
return bSwipeOnlyOnMenu;
}
// Sets the ability to swipe only on the menu or utilize entire screen for swiping.
- (void) SetSwipingOnMenuOnly:(bool)bValue
{
bSwipeOnlyOnMenu = bValue;
}
// Returns the swiping dead zone.
- (float) GetSwipeDeadZone
{
return fMoveDeadZone;
}
- (void) SetSwipeDeadZone:(float)fValue
{
fMoveDeadZone = fValue;
}
// Returns wheather or not vertical paging is enabled.
- (bool) IsVerticallyPaged
{
return bVerticalPaging;
}
// Sets the vertical paging flag.
- (void) SetVerticalPaging:(bool)bValue
{
bVerticalPaging = bValue;
[self buildGridVertical];
}
- (void) visit
{
[super visit];//< Will draw after glPopScene.
BOOL showPagesIndicator = YES;
ccColor4B pagesIndicatorNormalColor_ = ccc4(180, 180, 180, 255);
ccColor4B pagesIndicatorSelectedColor_ = ccc4(255, 255, 255, 255);
if (showPagesIndicator)
{
int totalScreens = iPageCount;
// Prepare Points Array
CGFloat n = (CGFloat)totalScreens; //< Total points count in CGFloat.
CGFloat pY = pageIndicatorPosition.y; //< Points y-coord in parent coord sys.
CGFloat d = ph_pad(16.0f, 16.0f * 2); //< Distance between points.
CGPoint points[totalScreens];
for (int i=0; i < totalScreens; ++i)
{
CGFloat pX = pageIndicatorPosition.x + d * ( (CGFloat)i - 0.5f*(n-1.0f) );
points[i] = ccp (pX, pY);
}
// Set GL Values
#if COCOS2D_VERSION >= 0x00020000
// ccGLEnable(CC_GL_BLEND);
ccPointSize( ph_pad(5.0 * CC_CONTENT_SCALE_FACTOR(), 5.0 * CC_CONTENT_SCALE_FACTOR() * 2) );
#define DRAW_4B_FUNC ccDrawColor4B
#else
glEnable(GL_POINT_SMOOTH);
GLboolean blendWasEnabled = glIsEnabled( GL_BLEND );
glEnable(GL_BLEND);
// save the old blending functions
int blend_src = 0;
int blend_dst = 0;
glGetIntegerv( GL_BLEND_SRC, &blend_src );
glGetIntegerv( GL_BLEND_DST, &blend_dst );
glPointSize( ph_pad(5.0 * CC_CONTENT_SCALE_FACTOR(), 5.0 * CC_CONTENT_SCALE_FACTOR() * 2) );
#define DRAW_4B_FUNC glColor4ub
#endif
ccGLBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
// glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
// Draw Gray Points
DRAW_4B_FUNC(pagesIndicatorNormalColor_.r,
pagesIndicatorNormalColor_.g,
pagesIndicatorNormalColor_.b,
pagesIndicatorNormalColor_.a);
ccDrawPoints( points, totalScreens );
// Draw White Point for Selected Page
DRAW_4B_FUNC(pagesIndicatorSelectedColor_.r,
pagesIndicatorSelectedColor_.g,
pagesIndicatorSelectedColor_.b,
pagesIndicatorSelectedColor_.a);
ccDrawPoint(points[iCurrentPage]);
// Restore GL Values
#if COCOS2D_VERSION >= 0x00020000
ccPointSize(1.0f);
#else
glPointSize(1.0f);
glDisable(GL_POINT_SMOOTH);
if (! blendWasEnabled)
glDisable(GL_BLEND);
// always restore the blending functions too
ccGLBlendFunc( blend_src, blend_dst);
// glBlendFunc( blend_src, blend_dst );
#endif
}
}
#end
The source code is from Brandon Reynolds and I have modified it a bit to better suit my game. I use this 5 methods to easily build menus for packs (single row, multi columns) and levels (multi rows, multi columns)
-(int) getTotalPage;
-(int) getCurrentPage;
-(void) moveToPage: (int) page animated: (BOOL) animated;
+(id) packWithArray: (NSMutableArray*) items posY: (int) posY indicatorPosY: (int) indiPosY;
+(id) levelWithArray: (NSMutableArray*) items cols: (int) cols rows: (int) rows leftEdge: (int) leftEdge upperEdge: (int) upperEdge lowerEdge: (int) lowerEdge indicatorPosY: (int) indiPosY;
But the open gl code is not working anymore for cocos2d 3.0 (e.g. ccGLBlendFunc etc). How can I update this class for cocos2d 3.x? Or any new implementation for similar menu grid?
Now it is better to use CCScrollView.
It supports pagination and you can easily create your level grid by just pre-creating it as a content node.
I am trying to create a UIView which is basically a number with a circular background. I am using a UIView and applying a corner radius of half the dimension for the circle. Then I am adding the number as a UILabel subview to the above view.
What I want is close to this (without the fancy border):
(This is a screenshot from the app Count Battle).
Please help me rewrite by moving the code under the proper methods (drawrect, init, layoutSubviews, or any custom method). This is the code in it's current form. I think my understanding of this thing is muddled up, and this just doesn't look right.
This is the header file:
// CircleNumView.h
#import <UIKit/UIKit.h>
#interface CircleNumView : UIView
#property (nonatomic, strong) UIColor *circleColor;
- (instancetype)initWithRadius:(CGFloat)radius
center:(CGPoint)center
text:(NSString *)text;
#end
This is the implementation file:
// CircleNumView.m
#import "CircleNumView.h"
#interface CircleNumView ()
#property (nonatomic) CGFloat radius;
#property (nonatomic, strong) NSString *text;
#end
#implementation CircleNumView
// designated initializer
- (instancetype)initWithRadius:(CGFloat)radius
center:(CGPoint)center
text:(NSString *)text
{
self = [super init];
self.radius = radius;
self.text = text;
self.frame = CGRectMake ( center.x - radius, center.y - radius, 2 * radius, 2 * radius);
self.circleColor = [UIColor whiteColor];
self = [self createView];
return self;
}
- (CircleNumView *)createView
{
CircleNumView *circularView = [[UIView alloc] initWithFrame:self.frame];
circularView.backgroundColor = self.circleColor;
UILabel *label = [[UILabel alloc] initWithFrame:circularView.bounds];
label.text = self.text;
label.textColor = [UIColor blackColor];
[circularView addSubview:label];
circularView.clipsToBounds = YES;
circularView.layer.cornerRadius = self.radius;
[self addSubview:circularView];
return circularView;
}
#end
You were doing everything pretty well until that self = [createView];
This is the implementation file that I would write:
//
// CircleNumberView.m
//
//
// Created by James Valaitis on 13/04/2014.
//
//
#import "CircleNumberView.h"
#pragma mark - Circle Number View Private Class Extension
#interface CircleNumberView ()
/** The radius of this circular view. */
#property (nonatomic, assign) CGFloat radius;
/** The number to present encapsulated as a string. */
#property (nonatomic, copy) NSString *text;
/** The label that shows the number contents of this view. */
#property (nonatomic, strong) UILabel *textLabel;
#end
#pragma mark - Circle Number View Implementation
#implementation CircleNumberView
#pragma mark - Initialisation
/**
* Initialises a circlular view with a number in the middle.
*
* #param radius The radius of the circle.
* #param center The center point of the circle in it's superview.
* #param text The number as a string to present in the middle of this view.
*
* #return An initialized object.
*/
- (instancetype)initWithRadius:(CGFloat)radius center:(CGFloat)center text:(NSString *)text
{
CGRect frame = CGRectMake(center.x - radius, center.y - radius, radius * 2, radius * 2);
if (self = [super initWithFrame:frame])
{
_radius = radius;
_text = text;
[self configureViewAndSubviews];
}
return self;
}
#pragma mark - Property Accessor Methods - Getters
/**
* The label that shows the number contents of this view.
*
* #return The label that shows the number contents of this view.
*/
- (UILabel *)textLabel
{
if (!_textLabel)
{
_textLabel = [[UILabel alloc] initWithFrame:self.bounds];
_textLabel.numberOfLines = 0;
_textLabel.textColor = [UIColor blackColor];
}
return _textLabel;
}
#pragma mark - Subview Management
/**
* Configures this view as well as it's subviews.
*/
- (void)configureViewAndSubviews
{
self.backgroundColor = [UIColor whiteColor];
self.clipsToBounds = YES;
self.layer.cornerRadius = self.radius;
self.textLabel.text = self.text;
[self addSubview:self.textLabel];
}
#end
I have what seems like a straightforward enough issue, but I just have no idea why it's working the way it is.
I have a class Shape which has a subclass of Square.
When I call Square and call its designated initialiser, in self = [super init] it calls the super class. However when the superclass calls its designated initialiser, named the same as one of the subclasses, it calls the subclass.
What I end up with is an infinite loop of the subclass calling init on the superclass and that calling the subclasses initialiser.
How can I solve this issue? Should I make sure the names of my initialisers are different enough so this can't happen?
Shape.h
#import <Foundation/Foundation.h>
#interface Shape : NSObject
#property (nonatomic) CGPoint position;
#property (nonatomic) float width;
#property (nonatomic) float height;
#property (nonatomic, readonly) float area;
- (id)initWithWidth:(float)width andHeight:(float)height andPosition:(CGPoint)position;
- (id)initWithWidth:(float)width andHeight:(float)height;
- (id)initWithWidth:(float)width;
- (id)init;
- (NSString *)drawShape;
#end
-
Shape.m
#import "Shape.h"
#implementation Shape
- (id)initWithWidth:(float)width andHeight:(float)height andPosition:(CGPoint)position
{
self = [super init];
if (self) {
self.width = width;
self.height = height;
self.position = position;
}
return self;
}
- (id)initWithWidth:(float)width andHeight:(float)height
{
return [self initWithWidth:width andHeight:height andPosition:CGPointMake(100, 100)];
}
- (id)initWithWidth:(float)width
{
return [self initWithWidth:width andHeight:1.0f andPosition:CGPointMake(100, 100)];
}
- (id)init
{
CGPoint defaultPoint = CGPointMake(100, 100);
return [self initWithWidth:1.0 andHeight:1.0 andPosition:defaultPoint];
}
- (NSString *)drawShape
{
NSString *outputShape = [NSString stringWithFormat:#"Drawing shape - (%.2f,%.2f), width - %f, height - %f", self.position.x, self.position.y, self.width, self.height];
NSLog(#"%#", outputShape);
return outputShape;
}
#end
-
Square.h
#import <Foundation/Foundation.h>
#import "Shape.h"
#interface Square : Shape
- (id)initWithWidth:(float)width andPosition:(CGPoint)position;
- (id)initWithWidth:(float)width andHeight:(float)height andPosition:(CGPoint)position;
- (id)initWithWidth:(float)width andHeight:(float)height;
- (id)initWithWidth:(float)width;
#end
-
Square.m
#import "Square.h"
#implementation Square
- (id) initWithWidth:(float)width andPosition:(CGPoint)position
{
self = [super init];
if (self) {
self.width = width;
self.height = width;
self.position = position;
}
return self;
}
// Returning the width as the width and height as you can't make a square with different sides
- (id)initWithWidth:(float)width andHeight:(float)height andPosition:(CGPoint)position
{
return [self initWithWidth:width andPosition:position];
}
- (id)initWithWidth:(float)width andHeight:(float)height
{
return [self initWithWidth:width andPosition:CGPointMake(100, 100)];
}
- (id)initWithWidth:(float)width
{
return [self initWithWidth:width andPosition:CGPointMake(100, 100)];
}
- (NSString *)drawShape
{
NSString *outputShape = [NSString stringWithFormat:#"Drawing shape - (%.2f,%.2f), width - %.2f, height - %.2f", self.position.x, self.position.y, self.width, self.height];
NSLog(#"%#", outputShape);
return outputShape;
}
#end
The thing to do here is to reimplement -[Square initWithWidth:andPosition:] like:
- (id)initWithWidth:(float)width andPosition:(CGPoint)position
{
self = [super initWithWidth:width andHeight:width andPosition:position];
return self;
}
I want to pass UIColor * as a parameter, but my program keeps crashing at [paramColour set].
I know I can pass in a string and select the UIColor from there but I'm just wondering why this doesn't work. Help would be greatly appreciated.
- (void) drawRooftopAtTopPointof:(CGPoint)paramTopPoint
colour:(UIColor *)paramColour
lineJoin:(CGLineJoin)paramLineJoin{
/* Set the color that we want to use to draw the line */
[paramColour set];
/* Get the current graphics context */
CGContextRef currentContext = UIGraphicsGetCurrentContext();
/* Set the line join */
CGContextSetLineJoin(currentContext,
paramLineJoin);
/* Set the width for the lines */
CGContextSetLineWidth(currentContext,
3.0f);
/* Start the line at this point */
CGContextMoveToPoint(currentContext,
paramTopPoint.x - 10,
paramTopPoint.y + 8);
/* And end it at this point */
CGContextAddLineToPoint(currentContext,
paramTopPoint.x,
paramTopPoint.y);
/* Extend the line to another point to
make the rooftop */
CGContextAddLineToPoint(currentContext,
paramTopPoint.x + 10,
paramTopPoint.y + 8);
/* Use the context's current color to draw the lines */
CGContextStrokePath(currentContext);
/* Draw the text in the rooftop using a black color */
[[UIColor blackColor] set];
}
Here's my drawRect in the view that triggers this drawing function.
- (void)drawRect:(CGRect)rect
{
for (int i=0; i < arrowsToDrawArray.count; i++)
{
Arrow * arrowObj = [arrowsToDrawArray objectAtIndex:i];
UIColor * colourOfArrow = [arrowObj colourOfArrow]; // colour determines whether right or wrong
CGPoint p = [arrowObj arrowPlacement];
// CGPoint p = [val CGPointValue];
[self drawRooftopAtTopPointof:p colour:colourOfArrow lineJoin:kCGLineJoinMiter];
}
}
and here's the definition of Arrow class.
#import <UIKit/UIKit.h>
#class Arrow;
#interface Arrow : NSObject
{
UIColor * colourOfArrow;
CGPoint arrowPlacement;
}
#property (nonatomic,retain) UIColor * colourOfArrow;
#property (nonatomic) CGPoint arrowPlacement;
#end
Hi thanks for the help,
I decided to do it properly, with constructor and all, here's the new code. Seems to be working now.
arrow.h
#import <UIKit/UIKit.h>
#class Arrow;
#interface Arrow : NSObject
{
UIColor * colourOfArrow;
CGPoint arrowPlacement;
}
#property (nonatomic,strong) UIColor * colourOfArrow;
#property (nonatomic) CGPoint arrowPlacement;
+ (Arrow *) createArrowWithColour :(UIColor *)paramColour andPosition :(CGPoint) xPosition;
#end
arrow.m
#import "Arrow.h"
#implementation Arrow
#synthesize colourOfArrow;
#synthesize arrowPlacement;
+ (Arrow *) createArrowWithColour :(UIColor *)paramColour andPosition :(CGPoint) xPosition
{
Arrow * tempArrow = [[Arrow alloc] init];
[tempArrow setColourOfArrow:paramColour];
[tempArrow setArrowPlacement:xPosition];
return tempArrow;
}
#end