Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 8 years ago.
Improve this question
I have a simple Cocoa program to plot data read from a Raspberry Pi based server. After a few thousand reads, the programs memory usage pushes toward 1 GB. Each read simply gets a long integer for the time and a float value representing temperature read from a temperature sensor. I've removed everything that I can think of that might utilize memory and the problem does not change. Please tell me where my problem are.
//
// ABSAppDelegate.m
// RPi_socket_test
//
#import "ABSAppDelegate.h"
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <arpa/inet.h>
#implementation ABSAppDelegate
- (void)dealloc
{
[super dealloc];
}
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
// Insert code here to initialize your application
NSView *superview = [[self window] contentView];
NSRect fullPlotFrame = [[self window] frame];
fullPlotFrame.size.height *= 0.75;
fullPlotFrame.size.width *= 0.85;
fullPlotFrame.origin.x = [[self window] frame].size.width*0.1;
fullPlotFrame.origin.y = [[self window] frame].size.height*0.02;
thePlot = [[[RPiViewController alloc] initWithFrame:fullPlotFrame] retain];
[superview addSubview:thePlot];
[thePlot display];
[NSThread detachNewThreadSelector:#selector(getTemp) toTarget:self withObject:nil];
}
-(void)getTemp{
int sockfd = 0, n = 0;
char recvBuff[1024];
struct sockaddr_in serv_addr;
while(ok){
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
memset(recvBuff, '0',sizeof(recvBuff));
if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
NSLog(#"\n Error : Could not create socket \n");
ok = false;
}
memset(&serv_addr, '0', sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(5001);
if(inet_pton(AF_INET, "172.27.220.44", &serv_addr.sin_addr)<=0)
{
NSLog(#"\n inet_pton error occured\n");
}
if( connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0)
{
NSLog(#"\n Error : Connect Failed \n");
ok = false;
}
while ( (n = (int)read(sockfd, recvBuff, sizeof(recvBuff)-1)) > 0)
{
recvBuff[n] = '\0';
[self performSelectorOnMainThread:#selector(displayTemp:) withObject:[NSString stringWithFormat:#"%.18s", recvBuff] waitUntilDone:YES];
}
if(n < 0)
{
NSLog(#"\n Read error \n");
}
close(sockfd);
sleep(1);
[pool drain];
}
}
-(void)displayTemp:(NSString*)theData{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSPoint pointForConversion = NSPointFromString(theData);
[txtTempC setStringValue:[NSString stringWithFormat:#"%0.0f, %0.2f", pointForConversion.x, pointForConversion.y]];
long int dataSize = [thePlot SizeOfData];
if(dataSize < 1024) [txtSizeOfData setStringValue:[NSString stringWithFormat:#"%i Bytes", (int)dataSize]];
else if(dataSize < 1024*1024) [txtSizeOfData setStringValue:[NSString stringWithFormat:#"%0.3f kBytes", (float)dataSize/1024]];
else [txtSizeOfData setStringValue:[NSString stringWithFormat:#"%0.3f MBytes", (float)dataSize/(1024*1024)]];
[thePlot addDataToPlot:pointForConversion];
[thePlot display];
[[self window] display];
[pool drain];
}
-(IBAction)getTemp:(id)sender{
if(!ok){
ok = true;
[NSThread detachNewThreadSelector:#selector(getTemp) toTarget:self withObject:nil];
}
}
-(IBAction)stopClient:(id)sender{
ok = false;
}
-(void)controlTextDidChange:(NSNotification *)obj{
if([obj object] == txtMax) [thePlot setMaxRawPlotData:(int)[txtMax integerValue]];
}
#end
With view controller
//
// RPiViewController.m
// RPi_socket_test
//
#import "RPiViewController.h"
#implementation RPiViewController
- (id)initWithFrame:(NSRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code here.
fontSize = 10;
NSMutableDictionary *drawStringAttributes = [[NSMutableDictionary alloc] init];
[drawStringAttributes setValue:[NSColor blackColor] forKey:NSForegroundColorAttributeName];
NSFont *myFont = [NSFont fontWithName:#"American Typewriter" size:fontSize];
[drawStringAttributes setValue:myFont forKey:NSFontAttributeName];
NSString* strLabelWidth = [NSString stringWithFormat:#"%0.2f", 55.55];
float labelWidth = [strLabelWidth sizeWithAttributes:drawStringAttributes].width;
NSRect frameForData = [self bounds];
frameForData.origin.x += labelWidth;
frameForData.size.width -= labelWidth;
myPlot = [[[RPiView alloc] initWithFrame:frameForData] retain];
[self addSubview:myPlot];
}
return self;
}
- (void)drawRect:(NSRect)dirtyRect
{
// Drawing code here.
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSPoint minPoint = [myPlot findMinValues];
NSPoint maxPoint = [myPlot findMaxValues];
NSMutableDictionary *drawStringAttributes = [[NSMutableDictionary alloc] init];
[drawStringAttributes setValue:[NSColor blackColor] forKey:NSForegroundColorAttributeName];
NSFont *myFont = [NSFont fontWithName:#"American Typewriter" size:fontSize];
[drawStringAttributes setValue:myFont forKey:NSFontAttributeName];
NSString* MinTempLabel = [NSString stringWithFormat:#"%0.2f", minPoint.y];
NSPoint pointToDrawLabel = {0,0};
[MinTempLabel drawAtPoint:pointToDrawLabel withAttributes:drawStringAttributes];
NSString* MaxTempLabel = [NSString stringWithFormat:#"%0.2f", maxPoint.y];
pointToDrawLabel.y = [self bounds].size.height - [MaxTempLabel sizeWithAttributes:drawStringAttributes].height;
[MaxTempLabel drawAtPoint:pointToDrawLabel withAttributes:drawStringAttributes];
[pool drain];
}
-(void)addDataToPlot:(NSPoint)theDataToPlot{
[myPlot addDataToPlot:theDataToPlot];
}
-(long int)SizeOfData{
long int dataSize = [myPlot SizeOfData];
return dataSize;
}
-(int)setMaxRawPlotData:(int)theMax{
return [myPlot setMaxRawPlotData:theMax];
}
#end
And subview controller
//
// RPiView.m
// RPi_socket_test
//
#import "RPiView.h"
#implementation RPiView
- (id)initWithFrame:(NSRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code here.
maxRawPlotData = 3600;
firstRawPlotIndex = 0;
dataCount = 0;
}
return self;
}
- (void)drawRect:(NSRect)dirtyRect
{ // Drawing code here.
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
NSPoint minPoint = [self findMinValues];
NSPoint maxPoint = [self findMaxValues];
float xScale = ([self bounds].size.width)/(maxPoint.x - minPoint.x);
float yScale = [self bounds].size.height/(maxPoint.y - minPoint.y);
[[NSColor whiteColor] set];
NSRect fillArea = [self bounds];
[NSBezierPath fillRect:fillArea];
NSBezierPath *pathForPlot = [[NSBezierPath alloc] init];
if(dataCount<maxRawPlotData){
if(dataCount>1){
NSPoint p1 = myData[0];
p1.x = (p1.x-minPoint.x)*xScale;
p1.y = (p1.y-minPoint.y)*yScale;
[pathForPlot moveToPoint:p1];
}
for(int i=1; i<dataCount; i++){
NSPoint p = myData[i];
p.x = (p.x-minPoint.x)*xScale;
p.y = (p.y-minPoint.y)*yScale;
[pathForPlot lineToPoint:p];
}
}
else{
unsigned long firstPointToPlot = dataCount-maxRawPlotData;
NSPoint p1 = myData[firstPointToPlot];
xScale = [self bounds].size.width/maxRawPlotData;
minPoint.x = p1.x;
p1.x = (p1.x-minPoint.x)*xScale;
p1.y = (p1.y-minPoint.y)*yScale;
[pathForPlot moveToPoint:p1];
for(unsigned long i=firstPointToPlot; i<dataCount; i++){
NSPoint p = myData[i];
p.x = (p.x-minPoint.x)*xScale;
p.y = (p.y-minPoint.y)*yScale;
[pathForPlot lineToPoint:p];
}
}
[[NSColor blackColor] set];
[pathForPlot stroke];
[pool drain];
}
-(void)addDataToPlot:(NSPoint)theDataToPlot{
myData[dataCount] = theDataToPlot;
dataCount++;
}
-(NSPoint)findMaxValues{
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
NSPoint maxValue = {-1e9, -1e9};
for(int i=0; i<dataCount; i++){
NSPoint testValue = myData[i];
if(testValue.x > maxValue.x) maxValue.x = testValue.x;
if(testValue.y > maxValue.y) maxValue.y = testValue.y;
}
[pool drain];
return maxValue;
}
-(NSPoint)findMinValues{
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
NSPoint maxValue = {1e9, 1e9};
for(int i=0; i<dataCount; i++){
NSPoint testValue = myData[i];
if(testValue.x < maxValue.x) maxValue.x = testValue.x;
if(testValue.y < maxValue.y) maxValue.y = testValue.y;
}
[pool drain];
return maxValue;
}
-(long int)SizeOfData{
long int dataSize = 0;
for(int i=0; i<dataCount; i++){
dataSize += sizeof(NSPoint);
}
return dataSize;
}
-(int)setMaxRawPlotData:(int)theMax{
if(theMax<10) theMax = 10;
maxRawPlotData = theMax;
return (int)dataCount;
}
#end
So, after painfully turning off code section by section, I tracked the leak to the drawRect method in the subview controller. By adding NSAutoreleasePools everywhere I could and releasing the NSBezierPath, the leak was plugged.
Thanks for giving me an outlet to "think out loud" about this!
- (void)drawRect:(NSRect)dirtyRect
{ // Drawing code here.
NSAutoreleasePool* uberpool = [[NSAutoreleasePool alloc] init];
NSPoint minPoint = [self findMinValues];
NSPoint maxPoint = [self findMaxValues];
float xScale = ([self bounds].size.width)/(maxPoint.x - minPoint.x);
float yScale = [self bounds].size.height/(maxPoint.y - minPoint.y);
[[NSColor whiteColor] set];
NSRect fillArea = [self bounds];
[NSBezierPath fillRect:fillArea];
NSBezierPath *pathForPlot = [[NSBezierPath alloc] init];
if(dataCount<maxRawPlotData){
if(dataCount>1){
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
NSPoint p1 = myData[0];
p1.x = (p1.x-minPoint.x)*xScale;
p1.y = (p1.y-minPoint.y)*yScale;
[pathForPlot moveToPoint:p1];
[pool drain];
}
for(int i=1; i<dataCount; i++){
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
NSPoint p = myData[i];
p.x = (p.x-minPoint.x)*xScale;
p.y = (p.y-minPoint.y)*yScale;
[pathForPlot lineToPoint:p];
[pool drain];
}
}
else{
unsigned long firstPointToPlot = dataCount-maxRawPlotData;
NSPoint p1 = myData[firstPointToPlot];
xScale = [self bounds].size.width/maxRawPlotData;
minPoint.x = p1.x;
p1.x = (p1.x-minPoint.x)*xScale;
p1.y = (p1.y-minPoint.y)*yScale;
[pathForPlot moveToPoint:p1];
for(unsigned long i=firstPointToPlot; i<dataCount; i++){
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
NSPoint p = myData[i];
p.x = (p.x-minPoint.x)*xScale;
p.y = (p.y-minPoint.y)*yScale;
[pathForPlot lineToPoint:p];
[pool drain];
}
}
[[NSColor blackColor] set];
[pathForPlot stroke];
[pathForPlot release];
[uberpool drain];
}
Related
I'm not much of an Objective-C guy I primarily use swift and I need some help from this error
Variable 'indexOfLargestGap' may be uninitialized when used here
and
Variable 'indexOfSmallestGap' may be uninitialized when used here
I have imported the code from Github and created a bridging header and fixed a few errors but I don't know how to fix this one.
The original file from Github https://github.com/jberlana/JBCroppableView
#import "JBCroppableImageView.h"
#import "JBCroppableLayer.h"
#implementation JBCroppableImageView{
JBCroppableLayer *_pointsView;
}
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self) {
self.userInteractionEnabled = YES;
_pointsView = [[JBCroppableLayer alloc] initWithImageView:self];
[_pointsView addPoints:4];
[self addSubview:_pointsView];
UIPanGestureRecognizer *singlePan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(singlePan:)];
singlePan.maximumNumberOfTouches = 1;
[self addGestureRecognizer:singlePan];
UIPanGestureRecognizer *doublePan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(doublePan:)];
doublePan.minimumNumberOfTouches = 2;
[self addGestureRecognizer:doublePan];
}
return self;
}
-(void)singlePan:(UIPanGestureRecognizer *)gesture{
CGPoint posInStretch = [gesture locationInView:_pointsView];
if(gesture.state==UIGestureRecognizerStateBegan){
[_pointsView findPointAtLocation:posInStretch];
}
if(gesture.state==UIGestureRecognizerStateEnded){
_pointsView.activePoint.backgroundColor = _pointsView.pointColor;
_pointsView.activePoint = nil;
}
[_pointsView moveActivePointToLocation:posInStretch];
// CGPoint translation = [gesture translationInView:self.imageView];
// [gesture setTranslation:CGPointZero inView:self.imageView];
}
-(void)doublePan:(UIPanGestureRecognizer *)gesture{
NSLog(#"double pan");
CGPoint translation = [gesture translationInView:self];
CGPoint newCenter = CGPointMake(self.center.x+translation.x, self.center.y+translation.y);
[self setCenter:newCenter];
[gesture setTranslation:CGPointZero inView:self];
}
-(void)crop{
[_pointsView maskImageView:self];
[_pointsView removeFromSuperview];
}
-(void)reverseCrop{
self.layer.mask = nil;
[self addSubview:_pointsView];
}
-(UIImage *)getCroppedImage{
return [_pointsView getCroppedImageForView:self withTransparentBorders:NO];
}
-(UIImage *)getCroppedImageWithTransparentBorders:(BOOL)transparent{
return [_pointsView getCroppedImageForView:self withTransparentBorders:transparent];
}
- (void)addPoint{
self.layer.mask = nil;
NSMutableArray *oldPoints = [_pointsView.getPoints mutableCopy];
// CGPoint new = CGPointMake((first.x+last.x)/2.0f, (first.y+last.y)/2.0f);
NSInteger indexOfLargestGap; //points to here for first warning
CGFloat largestGap = 0;
for(int i=0; i< oldPoints.count-1; i++){
CGPoint first = [[oldPoints objectAtIndex:i] CGPointValue];
CGPoint last = [[oldPoints objectAtIndex:i+1] CGPointValue];
CGFloat distance = [self distanceBetween:first And:last];
if(distance>largestGap){
indexOfLargestGap = i+1;
largestGap = distance;
}
}
CGPoint veryFirst = [[oldPoints firstObject] CGPointValue];
CGPoint veryLast = [[oldPoints lastObject] CGPointValue];
CGPoint new;
if([self distanceBetween:veryFirst And:veryLast]>largestGap){
indexOfLargestGap = oldPoints.count;
new = CGPointMake((veryFirst.x+veryLast.x)/2.0f, (veryFirst.y+veryLast.y)/2.0f);
} else {
CGPoint first = [[oldPoints objectAtIndex:indexOfLargestGap-1] CGPointValue];//Variable 'indexOfLargestGap' may be uninitialized when used here
CGPoint last = [[oldPoints objectAtIndex:indexOfLargestGap] CGPointValue];
new = CGPointMake((first.x+last.x)/2.0f, (first.y+last.y)/2.0f);
}
[oldPoints insertObject:[NSValue valueWithCGPoint:new] atIndex:indexOfLargestGap];
[_pointsView removeFromSuperview];
_pointsView = [[JBCroppableLayer alloc] initWithImageView:self];
[_pointsView addPointsAt:oldPoints];
[self addSubview:_pointsView];
}
- (void)removePoint{
self.layer.mask = nil;
NSMutableArray *oldPoints = [_pointsView.getPoints mutableCopy];
if(oldPoints.count==3) return;
NSInteger indexOfSmallestGap;// points to here for second warning
CGFloat smallestGap = INFINITY;
for(int i=0; i< oldPoints.count; i++){
int firstIndex = i-1;
int lastIndex = i +1;
if(firstIndex<0){
firstIndex = (int)oldPoints.count-1+firstIndex;
} else if(firstIndex>=oldPoints.count){
firstIndex = firstIndex-(int)oldPoints.count;
}
if(lastIndex<0){
lastIndex = (int)oldPoints.count-1+lastIndex;
} else if(lastIndex>=oldPoints.count){
lastIndex = lastIndex-(int)oldPoints.count;
}
CGPoint first = [[oldPoints objectAtIndex:firstIndex] CGPointValue];
CGPoint mid = [[oldPoints objectAtIndex:i] CGPointValue];
CGPoint last = [[oldPoints objectAtIndex:lastIndex] CGPointValue];
CGFloat distance = [self distanceFrom:first to:last throuh:mid];
if(distance<smallestGap){
indexOfSmallestGap = i;
smallestGap = distance;
}
}
[oldPoints removeObjectAtIndex:indexOfSmallestGap]; //Variable 'indexOfSmallestGap' may be uninitialized when used here
[_pointsView removeFromSuperview];
_pointsView = [[JBCroppableLayer alloc] initWithImageView:self];
[_pointsView addPointsAt:[NSArray arrayWithArray:oldPoints]];
[self addSubview:_pointsView];
}
-(CGFloat)distanceBetween:(CGPoint)first And:(CGPoint)last{
CGFloat xDist = (last.x - first.x);
if(xDist<0) xDist=xDist*-1;
CGFloat yDist = (last.y - first.y);
if(yDist<0) yDist=yDist*-1;
return sqrt((xDist * xDist) + (yDist * yDist));
}
-(CGFloat)distanceFrom:(CGPoint)first to:(CGPoint)last throuh:(CGPoint)middle{
CGFloat firstToMid = [self distanceBetween:first And:middle];
CGFloat lastToMid = [self distanceBetween:middle And:last];
return firstToMid + lastToMid;
}
-(void)setFrame:(CGRect)frame{
[super setFrame:frame];
[_pointsView setFrame:CGRectMake(0, 0, frame.size.width, frame.size.height)];
[_pointsView setNeedsDisplay];
}
#end
And I'm getting this from the terminal
index 0 beyond bounds for empty array
Question:
How to achieve this animation with Spritekit?
What I've done:
Problem:
1) I can draw all four petals,but once I lift my finger to draw the circle, it will still create a line from the previous point where I lift my finger to the new touches begin point. refer to gif below:
2) How to remove the solid orange line from the view incrementally (mine is too abrupt)?
3) Need to tune the .sks file properties.
4) https://stackoverflow.com/questions/29792443/set-the-initial-state-of-skemitternode
This is my code:
#import "GameScene.h"
#interface GameScene()
#property (nonatomic) SKEmitterNode* fireEmmitter;
#property (nonatomic) SKEmitterNode* fireEmmitter2;
#end
#implementation GameScene
NSMutableArray *_wayPoints;
NSTimer* myTimer;
-(void)didMoveToView:(SKView *)view {
_wayPoints = [NSMutableArray array];
//Setup a background
self.backgroundColor = [UIColor blackColor];
//setup a fire emitter
NSString *fireEmmitterPath = [[NSBundle mainBundle] pathForResource:#"magic" ofType:#"sks"];
_fireEmmitter = [NSKeyedUnarchiver unarchiveObjectWithFile:fireEmmitterPath];
_fireEmmitter.position = CGPointMake(self.frame.size.width/2, self.frame.size.height/2 - 200);
_fireEmmitter.name = #"fireEmmitter";
_fireEmmitter.zPosition = 1;
_fireEmmitter.targetNode = self;
_fireEmmitter.particleBirthRate = 0;
[self addChild: _fireEmmitter];
//setup another fire emitter
NSString *fireEmmitterPath2 = [[NSBundle mainBundle] pathForResource:#"fireflies" ofType:#"sks"];
_fireEmmitter2 = [NSKeyedUnarchiver unarchiveObjectWithFile:fireEmmitterPath2];
_fireEmmitter2.position = CGPointMake(self.frame.size.width/2, self.frame.size.height/2);
_fireEmmitter2.name = #"fireEmmitter";
_fireEmmitter2.zPosition = 1;
_fireEmmitter2.targetNode = self;
_fireEmmitter2.particleBirthRate = 0;
[self addChild: _fireEmmitter2];
//Setup a LightNode
SKLightNode* light = [[SKLightNode alloc] init];
light.categoryBitMask = 1;
light.falloff = 1;
light.ambientColor = [UIColor whiteColor];
light.lightColor = [[UIColor alloc] initWithRed:1.0 green:1.0 blue:0.0 alpha:0.5];
light.shadowColor = [[UIColor alloc] initWithRed:0.0 green:0.0 blue:0.0 alpha:0.3];
[_fireEmmitter addChild:light];
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
CGPoint touchPoint = [[touches anyObject] locationInNode:self.scene];
CGMutablePathRef ref = CGPathCreateMutable();
CGPoint p = touchPoint;
p = [self.scene convertPointToView:p];
CGPathMoveToPoint(ref, NULL, p.x, p.y);
_fireEmmitter.position = CGPointMake(touchPoint.x, touchPoint.y);
_fireEmmitter.particleBirthRate = 2000;
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
CGPoint touchPoint = [[touches anyObject] locationInNode:self.scene];
//On Dragging make the emitter with the attached light follow the position
for (UITouch *touch in touches) {
[self addPointToMove:touchPoint];
CGPoint location = [touch locationInNode:self];
[self childNodeWithName:#"fireEmmitter"].position = CGPointMake(location.x, location.y);
}
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
_fireEmmitter.particleBirthRate = 0;
[self performSelector:#selector(userHasCompletedTheDrawing) withObject:nil afterDelay:3];
}
- (void)userHasCompletedTheDrawing{
CGMutablePathRef path = CGPathCreateMutable();
if (_wayPoints && _wayPoints.count > 0) {
CGPoint p = [(NSValue *)[_wayPoints objectAtIndex:0] CGPointValue];
//p = [self.scene convertPointToView:p];
CGPathMoveToPoint(path, nil, p.x, p.y);
_fireEmmitter2.position = CGPointMake(p.x,p.y);
_fireEmmitter2.particleBirthRate = 1000;
for (int i = 0; i < _wayPoints.count; ++i) {
p = [(NSValue *)[_wayPoints objectAtIndex:i] CGPointValue];
CGPathAddLineToPoint(path, nil, p.x, p.y);
}
SKAction *followTrack = [SKAction followPath:path asOffset:NO orientToPath:YES duration:1];
[_fireEmmitter2 runAction:followTrack completion:^{
_fireEmmitter2.particleBirthRate = 0;
[_fireEmmitter2 runAction:[SKAction waitForDuration:1] completion:^{
//_fireEmmitter2.particleBirthRate = 0;
}];
}];
}
//myTimer = [NSTimer scheduledTimerWithTimeInterval: 0.01 target: self selector: #selector(removePointToMove) userInfo: nil repeats: YES];
[self performSelector:#selector(removeAllPointToMove) withObject:nil afterDelay:1];
}
- (void)addPointToMove:(CGPoint)point {
[_wayPoints addObject:[NSValue valueWithCGPoint:point]];
}
- (void)removeAllPointToMove{
[_wayPoints removeAllObjects];
}
- (void)removePointToMove{
if ([_wayPoints count]>0) {
[_wayPoints removeObjectAtIndex:0];
}
}
- (void)drawLines {
//1
NSMutableArray *temp = [NSMutableArray array];
for(CALayer *layer in self.view.layer.sublayers) {
if([layer.name isEqualToString:#"line"]) {
[temp addObject:layer];
}
}
[temp makeObjectsPerformSelector:#selector(removeFromSuperlayer)];
//3
CAShapeLayer *lineLayer = [CAShapeLayer layer];
lineLayer.name = #"line";
lineLayer.strokeColor = [UIColor orangeColor].CGColor;
lineLayer.fillColor = nil;
lineLayer.lineWidth = 3;
lineLayer.lineJoin = kCALineJoinRound; /* The join style used when stroking the path. Options are `miter', `round'
* and `bevel'. Defaults to `miter'. */
lineLayer.zPosition = -1;
//4
CGPathRef path = [self createPathToMove];
lineLayer.path = path;
CGPathRelease(path);
[self.view.layer addSublayer:lineLayer];
}
- (CGPathRef)createPathToMove {
//1
CGMutablePathRef ref = CGPathCreateMutable();
//2
for(int i = 0; i < [_wayPoints count]; ++i) {
CGPoint p = [_wayPoints[i] CGPointValue];
p = [self.scene convertPointToView:p];
//3
if(i == 0 ) {
CGPathMoveToPoint(ref, NULL, p.x, p.y);
} else {
CGPathAddLineToPoint(ref, NULL, p.x, p.y);
}
}
return ref;
}
-(void)update:(CFTimeInterval)currentTime {
/* Called before each frame is rendered */
[self drawLines];
if ([_wayPoints count]==0) {
[myTimer invalidate];
}
}
#end
This is my .sks files properties:
Concerning your first question, you need to split your CGPathRef into multiple subpaths so that no line gets drawn between the petals and the center. Use the CGPathCloseSubpath function when you are done drawing the petals so that you can call CGPathMoveToPoint and CGPathAddLineToPoint afterwards.
I have the following class
#import <UIKit/UIKit.h>
#interface UIImageViewInfo : UIImageView
#property(nonatomic,strong) NSString* colorInfo;
#end
when creating an UIImageViewInfo in function1(post below) I get the correct result
"UIImageViewInfo: 0x1d8acd60; baseClass = UIImageView; frame = (0 0;
20 20); opaque = NO; userInteractionEnabled = NO; tag = 2000; layer =
CALayer: 0x1d8a0560>>"
but creating the something in function2(posted below) I get the -(null)
UIImageViewInfo: 0x8372c40; baseClass = UIImageView; frame = (0 0; 20
20); alpha = 0; hidden = YES; opaque = NO; userInteractionEnabled =
NO; tag = 2000; layer = CALayer: 0x8380090>> - (null)
Why is there a - (null) ??
Thanx in advance
P.S> here is more from my code
-(void)function1{
UIImageViewInfo *image;
self.solutionPinSlots = [[NSMutableArray alloc] init];
for (int i=0; i<M; i++) {
image = [[UIImageViewInfo alloc] initWithImage:[UIImage imageNamed:#"solutionPeg#2x.png"]];
image.translatesAutoresizingMaskIntoConstraints=NO;
image.hidden=YES;
image.alpha = 0;
image.colorInfo = #"clear";
image.tag = 2000*(self.brainController.slotsIndex+1) +i;
[self.solutionPinSlots addObject:image];
[self.view addSubview:(UIImageViewInfo *)self.solutionPinSlots[i]];
}
NSLog(#"solutionSlots: %#",self.solutionPinSlots);
__block int counter = 0;
[self.brainController.solution enumerateObjectsUsingBlock:^(NSString *peg, NSUInteger idx, BOOL *stop){
for (int i=0;i<M;i++) {
if ([self.brainController.slots[self.brainController.slotsIndex][i] isEqual:peg]) {
if (i==idx) {
((UIImageViewInfo *) self.solutionPinSlots[counter]).backgroundColor = [UIColor whiteColor];
((UIImageViewInfo *) self.solutionPinSlots[counter]).colorInfo=#"white";
}else{
((UIImageViewInfo *) self.solutionPinSlots[counter]).backgroundColor = [UIColor blackColor];
((UIImageViewInfo *) self.solutionPinSlots[counter]).colorInfo=#"black";}
((UIImageViewInfo *) self.solutionPinSlots[counter]).hidden=NO;
((UIImageViewInfo *) self.solutionPinSlots[counter++]).alpha=1;
}
}
}];
[self.solutionPinSlots shuffle];
[self updateSolutionPinSlots];
}
-(void)function2{
NSString *obj;
for (int idx=0; idx< M;idx++ ) {
UIImageViewInfo *image = [[UIImageViewInfo alloc] initWithImage:[UIImage imageNamed:#"solutionPeg#2x.png"]];
image.translatesAutoresizingMaskIntoConstraints=NO;
obj = (NSString *) [self.solutionPinSlots objectAtIndex:idx];
image.tag=2000*(self.brainController.slotsIndex+1)+idx;
image.hidden=NO;
image.alpha = 1;
if ([obj isEqualToString:#"clear"]) {
image.hidden=YES;
image.alpha = 0;
image.colorInfo=#"clear";
image.backgroundColor=[UIColor clearColor];
}else if ([obj isEqualToString:#"white"]){
image.colorInfo=#"white";
image.backgroundColor=[UIColor whiteColor];
}else if ([obj isEqualToString:#"black"]){
image.colorInfo=#"black";
image.backgroundColor=[UIColor blackColor];
}else{
NSLog(#"Something is Wrong!!!");
}
[self.solutionPinSlots replaceObjectAtIndex:idx withObject:image];
[self.view addSubview:image];
}
}
Never mind. It is coming from the description method of UIView, which is rather complex and dumps a bunch of information about the view. One of the conditionals is being tripped up by your configuration and causing the (null) at the end.
Safe to ignore.
There is no UIImageViewInfo class in UIKit, so the implementation must be in your project. In that implementation, there is likely a description method that is implemented something like:
- (NSString*)description {
....
return [NSString stringWithFormat:#"UIImageViewInfo:%p; %# - %#", self, [super description], someInstanceVariableThatHappensToBeNil]];
}
Only your description is likely more complex due to the sometimes does this sometimes that output.
Not that you should not prefix classes with UI when subclassing the UIKit.
It is because your NSLog call (which you do not show) is asking for a second object, and that second object is null.
So I was just coding along, then Xcode just started going whack when I hit build and gave me all these error that I have never had before
Here is the file:
import
#import <OpenGLES/EAGLDrawable.h>
#import "EAGLView.h"
#define USE_DEPTH_BUFFER 0
// A class extension to declare private methods #interface EAGLView ()
#property (nonatomic, retain) EAGLContext *context; #property (nonatomic, assign) NSTimer *animationTimer;
- (BOOL) createFramebuffer;
- (void) destroyFramebuffer;
- (void) updateScene:(float)delta;
- (void) renderScene;
#end
#implementation EAGLView
#synthesize context; #synthesize animationTimer; #synthesize animationInterval;
// You must implement this method
+ (Class)layerClass {
return [CAEAGLLayer class]; }
//The GL view is stored in the nib file. When it's unarchived it's sent -initWithCoder:
- (id)initWithCoder:(NSCoder*)coder {
if ((self = [super initWithCoder:coder])) {
// Get the layer
CAEAGLLayer *eaglLayer = (CAEAGLLayer *)self.layer;
eaglLayer.opaque = YES;
eaglLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:NO], kEAGLDrawablePropertyRetainedBacking,
kEAGLColorFormatRGBA8, kEAGLDrawablePropertyColorFormat, nil];
context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES1];
if (!context || ![EAGLContext setCurrentContext:context]) {
[self release];
return nil;
}
animationInterval = 1.0 / 60.0;
CGRect rect = [[UIScreen mainScreen] bounds];
// Set up OpenGL projection matrix glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrthof(0,
rect.size.width, 0, rect.size.height, -1, 1);
glMatrixMode(GL_MODELVIEW); glViewport(0, 0, rect.size.width,
rect.size.height);
// Initialize OpenGL states glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glDisable(GL_DEPTH_TEST);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_BLEND_SRC);
glEnableClientState(GL_VERTEX_ARRAY); glClearColor(0.0f, 0.0f,
0.0f, 1.0f);
// init [self initGame];
UIAccelerometer *accel = [UIAccelerometer sharedAccelerometer];
accel.delegate = self;
accel.updateInterval = 1.0f / 60.0f;
//[sharedSoundManager playMusicWithKey:#"song" timesToRepeat:-1];
}
return self; }
-(void) initGame{
//SET GAME STATE
//0 = Menu
//1 = Gameplay
//2 = death screen
gameState = 0;
player = [[Circle alloc] init];
score = 0;
scoreString = [NSString alloc];
scoreAdder = 1;
//[self setupScore];
menu = [[Image alloc] initWithImage:[UIImage imageNamed:#"loadImage.png"]];
bk = [[Image alloc] initWithImage:[UIImage imageNamed:#"bk.png"]];
squared = [[Image alloc] initWithImage:[UIImage imageNamed:#"squared.png"]];
gameRunning = TRUE;
gameState = 0;
squares = [[Squares alloc] init];
[squares addSquare: CGPointMake(100, 100) : 0];
//font = [[AngelCodeFont alloc]initWithFontImageNamed:#"font1.png" controlFile:#"font1.fnt"
scale: 0.0f filter: nil];
// Init sound sharedSoundManager = [SingletonSoundManager sharedSoundManager]; [sharedSoundManager loadSoundWithKey:#"menu"
fileName:#"Menu" fileExt:#"mp3" frequency: 22050];
[sharedSoundManager loadBackgroundMusicWithKey:#"music" fileName:#"bkmusic" fileExt:#"aif"];
menuMusicVariable = 1;
gameMusicVariable = 1;
}
-(void) setRunning: (bool) boolean{
gameRunning = boolean; }
-(void) runContinueCountdown{
if(gameRunning == NO){
int timer = 300;
int countdownNum;
timer --;
if(timer <= 300){
if(timer > 200){
countdownNum = 3;
}
if(timer <= 200){
if(timer > 100){
countdownNum = 2;
}
}
if(timer <= 100){
if(timer >= 1){
countdownNum = 1;
}
}
if(timer == 0 && countdownNum == 1){
[self setRunning: YES];
}
} }
- (void) mainGameLoop { CFTimeInterval time; float delta; time = CFAbsoluteTimeGetCurrent(); delta = (time - lastTime);
[self updateScene:delta]; [self renderScene]; lastTime = time;
}
- (void)updateScene:(float)delta { // Update Game Logic
if(gameRunning){
if(gameState == 0){
//MENU
if(menuMusicVariable == 1){
[sharedSoundManager stopPlayingMusic];
[sharedSoundManager playSoundWithKey:#"Menu" gain:10 pitch:10 location:Vector2fMake(0, 0) shouldLoop: TRUE];
menuMusicVariable = 0;
}
[scoreLabel setHidden: YES];
} else if(gameState == 1){
//GAMEPLAY
if(gameMusicVariable == 1){
[sharedSoundManager stopPlayingMusic];
[sharedSoundManager playMusicWithKey: #"music" timesToRepeat: -1];
gameMusicVariable = 0;
}
[scoreLabel setHidden: NO];
//game is running
if([player getAlive] == true){
score = score + scoreAdder;
} else {
score = score;
}
[player move];
[self checkSquareToCircleCollisions];
[self checkSquareToSquareCollisions];
[squares update];
} else if(gameState == 2){
//DEATH SCREEN
[sharedSoundManager stopPlayingMusic];
[scoreLabel setBounds: CGRectMake(100, 100 , 100, 40)];
}
} else {
//game is puased
} }
- (void)renderScene {
// Make sure we are renderin to the frame buffer
[EAGLContext setCurrentContext:context];
glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer);
// Clear the color buffer with the glClearColor which has been set glClear(GL_COLOR_BUFFER_BIT); //Render the game Scene
if(gameState == 0){
//MENU
[menu renderAtPoint: CGPointMake(0, 0) centerOfImage: NO];
} else if(gameState == 1){
//GAMEPLAY
[bk renderAtPoint: CGPointMake(0, 0) centerOfImage: NO];
[self drawScore];
[player draw];
[squares render];
//[font drawStringAt: CGPointMake(150, 100) text:#"HELLO FONTS"];
} else if(gameState ==2){
//DEATH SCREEN
[squared renderAtPoint: CGPointMake(0, 0) centerOfImage: NO];
} // Switch the render buffer and framebuffer so our scene is displayed on the screen
glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);
[context presentRenderbuffer:GL_RENDERBUFFER_OES]; }
- (void)layoutSubviews {
[EAGLContext setCurrentContext:context];
[self destroyFramebuffer];
[self createFramebuffer];
[self renderScene]; }
- (BOOL)createFramebuffer {
glGenFramebuffersOES(1, &viewFramebuffer);
glGenRenderbuffersOES(1, &viewRenderbuffer);
glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer);
glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);
[context renderbufferStorage:GL_RENDERBUFFER_OES fromDrawable:(CAEAGLLayer*)self.layer];
glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, viewRenderbuffer);
glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_WIDTH_OES, &backingWidth);
glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_HEIGHT_OES, &backingHeight);
if (USE_DEPTH_BUFFER) {
glGenRenderbuffersOES(1, &depthRenderbuffer);
glBindRenderbufferOES(GL_RENDERBUFFER_OES, depthRenderbuffer);
glRenderbufferStorageOES(GL_RENDERBUFFER_OES, GL_DEPTH_COMPONENT16_OES, backingWidth, backingHeight);
glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_DEPTH_ATTACHMENT_OES, GL_RENDERBUFFER_OES, depthRenderbuffer);
}
if(glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES) != GL_FRAMEBUFFER_COMPLETE_OES) {
NSLog(#"failed to make complete framebuffer object %x", glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES));
return NO;
}
return YES; }
- (void)destroyFramebuffer {
glDeleteFramebuffersOES(1, &viewFramebuffer);
viewFramebuffer = 0;
glDeleteRenderbuffersOES(1, &viewRenderbuffer);
viewRenderbuffer = 0;
if(depthRenderbuffer) {
glDeleteRenderbuffersOES(1, &depthRenderbuffer);
depthRenderbuffer = 0;
} }
- (void)startAnimation {
self.animationTimer = [NSTimer scheduledTimerWithTimeInterval:animationInterval target:self
selector:#selector(mainGameLoop) userInfo:nil repeats:YES]; }
- (void)stopAnimation {
self.animationTimer = nil; }
- (void)setAnimationTimer:(NSTimer *)newTimer {
[animationTimer invalidate];
animationTimer = newTimer; }
- (void)setAnimationInterval:(NSTimeInterval)interval {
animationInterval = interval;
if (animationTimer) {
[self stopAnimation];
[self startAnimation];
} }
-(void) setupScore{
scoreLabel = [NSString stringWithFormat:#"%d", score];
scoreLabel.frame = CGRectMake(262, 250, 100, 40);
[scoreLabel setText: scoreString];
//normally you'll want a transparent background for your label
scoreLabel.backgroundColor = [UIColor clearColor];
//you can use non-standard fonts
[scoreLabel setFont:[UIFont fontWithName:#"TimesNewRoman" size: 1.0f]];
//change the label's text color
scoreLabel.textColor = [UIColor whiteColor];
//add it to your view
scoreLabel.transform = CGAffineTransformMakeRotation(89.53);
[self addSubview:scoreLabel]; }
-(void) resetScore {
score = 0;
scoreLabel.textColor = [UIColor blackColor];
[scoreLabel release]; }
-(void)drawScore{
[scoreLabel setText: scoreString]; }
-(void) checkSquareToCircleCollisions{
NSMutableArray *array = [squares getSquares];
for(int i = 0; i < [squares getCount]; i++){
Square *s = [array objectAtIndex: i];
CGRect rect1 = [player getRect];
CGRect rect2 = [s getRect];
if (CGRectIntersectsRect(rect1, rect2)){
player.alive = NO;
gameState = 2;
}
} }
-(void) checkSquareToSquareCollisions{
NSMutableArray *array = [squares getSquares];
for(int i = 0; i < [squares getCount]; i++){
Square *s = [array objectAtIndex: 0];
Square *ss = [array objectAtIndex: i];
CGRect rect1 = [s getRect];
CGRect rect2 = [ss getRect];
if (CGRectIntersectsRect(rect1, rect2)) {
[s setDirection: [s getXDir] * -1 : [s getYDir] * -1];
[ss setDirection: [ss getXDir] * -1 : [ss getYDir] * -1];
}
} }
-(void) spawnSquares {
// FINISH METHOD
}
-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
if(gameState == 0){
//MENU
UITouch *touch = [[event allTouches] anyObject];
gameState = 1;
//[self initGame];
} else if(gameState == 1){
//GAMEPLAY
UITouch *touch = [[event allTouches] anyObject];
CGPoint touchPos = [touch locationInView:touch.view];
touchPos.y = 480 - touchPos.y;
[player setPos: touchPos];
} else if(gameState == 2){
//DEATH SCREEN
UITouch *touch = [[event allTouches] anyObject];
gameState = 0;
//[self resetScore];
[self initGame];
} }
-(void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
if(gameState == 0){
//MENU
UITouch *touch = [[event allTouches] anyObject];
//[self initGame];
} else if(gameState == 1){
//GAMEPLAY
UITouch *touch = [[event allTouches] anyObject];
CGPoint touchPos = [touch locationInView:touch.view];
touchPos.y = 480 - touchPos.y;
[player setPos: touchPos];
} else if(gameState == 2){
//DEATH SCREEN
UITouch *touch = [[event allTouches] anyObject];
}}
- (void)accelerometer:(UIAccelerometer *)accelerometer
didAccelerate:(UIAcceleration *)acceleration {
point.y = acceleration.y * 10;
point.x = acceleration.x * 10;
CGPoint pos = [player getPos];
pos = CGPointMake(pos.x + point.x, pos.y + point.y);
[player setPos: pos];
//Right - MAY HAVE TO CHANGE
if(pos.x < 0){
pos = CGPointMake(320, pos.y);
}
//left
if(pos.x < 320){
pos = CGPointMake(0, pos.y);
}
//Top
if(pos.y < 0){
pos = CGPointMake(pos.x, 460);
}
//Bottom
if(pos.x < 460){
pos = CGPointMake(pos.x, 0);
}
}
- (void)dealloc {
[self stopAnimation];
if ([EAGLContext currentContext] == context) {
[EAGLContext setCurrentContext:nil];
}
[context release];
[player release];
[menu release];
[sharedSoundManager release];
[bk release];
[squared release];
[squares release];
[scoreLabel release];
[scoreString release];
[super dealloc]; }
#end // here it says: excepted } and also its excepting #end
You are missing a brace in the runContinueCountdown at the end of the second if statement.
if(timer <= 300){
if(timer > 200){
countdownNum = 3;
}
Add a brace to the end of this if statement
I am hacking this Core Animation example:
https://github.com/joericioppo/Shape_05
I want to retrigger the animation in response to an NSThread. The NSLogs show up as expected, but the animation itself does not trigger until all sleeps have completed, even though its method has verifiably been called. How can I retrigger this animation every second?
By the way - this is just an example patch I am working on in order to understand the concept. I know that animations can be set to repeat, but this isn't what I want to do, for various reasons. Here's the code, any help would be most welcome, thanks.
#import <UIKit/UIKit.h>
#import <QuartzCore/QuartzCore.h>
#interface Shape_05ViewController : UIViewController {
CALayer *rootLayer;
CAShapeLayer *shapeLayer;
CGMutablePathRef pentagonPath;
CGMutablePathRef starPath;
int animationCount;
CABasicAnimation *animation;
}
-(void)startAnimation;
-(void) viewDidLoad;
-(void) startSequence;
-(void) enactSequence;
#end
#import "Shape_05ViewController.h"
#implementation Shape_05ViewController
- (void)loadView
{
UIView *appView = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
appView.backgroundColor = [UIColor blackColor];
self.view = appView;
[appView release];
rootLayer = [CALayer layer];
rootLayer.frame = self.view.bounds;
[self.view.layer addSublayer:rootLayer];
//Pentagon Path
pentagonPath = CGPathCreateMutable();
CGPoint center = self.view.center;
CGPathMoveToPoint(pentagonPath, nil, center.x, center.y - 75.0);
for(int i = 1; i < 5; ++i) {
CGFloat x = - 75.0 * sinf(i * 2.0 * M_PI / 5.0);
CGFloat y = - 75.0 * cosf(i * 2.0 * M_PI / 5.0);
CGPathAddLineToPoint(pentagonPath, nil,center.x + x, center.y + y);
}
CGPathCloseSubpath(pentagonPath); //This seems to be fixed in 4.0
//Star Path
starPath = CGPathCreateMutable();
center = self.view.center;
CGPathMoveToPoint(starPath, NULL, center.x, center.y + 75.0);
for(int i = 1; i < 5; ++i) {
CGFloat x = 75.0 * sinf(i * 4.0 * M_PI / 5.0);
CGFloat y = 75.0 * cosf(i * 4.0 * M_PI / 5.0);
CGPathAddLineToPoint(starPath, NULL, center.x + x, center.y + y);
};
CGPathCloseSubpath(starPath);
//Create Shape
shapeLayer = [CAShapeLayer layer];
shapeLayer.path = pentagonPath;
UIColor *fillColor = [UIColor colorWithWhite:0.9 alpha:1.0];
shapeLayer.fillColor = fillColor.CGColor;
shapeLayer.fillRule = kCAFillRuleNonZero;
[rootLayer addSublayer:shapeLayer];
}
-(void) viewDidLoad {
NSLog(#"viewDidLoad");
animationCount = 0;
[NSThread detachNewThreadSelector:#selector(startSequence) toTarget:self withObject:nil];
}
-(void) startSequence {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[self enactSequence];
//[NSThread sleepForTimeInterval:100];
[pool release];
}
-(void) enactSequence {
NSLog(#"enactSequence");
for(int i = 0; i < 4; i++) {
[NSThread sleepForTimeInterval:1];
[self performSelector:#selector(startAnimation) withObject:nil];
[rootLayer setNeedsDisplay];
NSLog(#"%i", i);
};
}
-(void)startAnimation
{
NSLog(#"startAnimation");
animation = [CABasicAnimation animationWithKeyPath:#"path"];
animation.duration = 0.25;
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
animation.repeatCount = 0;
animation.autoreverses = NO;
animation.fillMode = kCAFillModeForwards;
animation.removedOnCompletion = YES;
if( (animationCount % 2) == 0) {
animation.fromValue = (id)pentagonPath;
animation.toValue = (id)starPath;
} else {
animation.fromValue = (id)starPath;
animation.toValue = (id)pentagonPath;
};
[shapeLayer addAnimation:animation forKey:#"animatePath"];
animationCount++;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
- (void)dealloc
{
CGPathRelease(starPath);
CGPathRelease(pentagonPath);
[super dealloc];
}
#end
OK after another Google trawl:
http://forums.macrumors.com/showthread.php?t=749518&highlight=cabasicanimation
I found out about this thing called NSTimer! Another search of this site revealed some good info:
How do I use NSTimer?
And about running NSTimer in its own thread:
Keep an NSThread containing an NSTimer around indefinitely? (iPhone)
So now I can offer this updated code example, which I hope may be useful to someone one day. By the way, I didn't try it yet, but apparently the following can be used to stop the timer:
[myTimer invalidate];
myTimer = nil;
Thanks to all here at this site, not only the most beautifully designed corner of the web but also a total lifesaver!
#import <UIKit/UIKit.h>
#import <QuartzCore/QuartzCore.h>
#interface Shape_05ViewController : UIViewController {
CALayer *rootLayer;
CAShapeLayer *shapeLayer;
CGMutablePathRef pentagonPath;
CGMutablePathRef starPath;
int animationCount;
CABasicAnimation *animation;
NSTimer *repeatingTimer;
}
#property (assign, readwrite) NSTimer *repeatingTimer;
-(void) startAnimation: (NSTimer*)theTimer;
-(void) viewDidLoad;
-(void) startSequence;
#end
#import "Shape_05ViewController.h"
#implementation Shape_05ViewController
#synthesize repeatingTimer;
- (void)loadView
{
UIView *appView = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
appView.backgroundColor = [UIColor blackColor];
self.view = appView;
[appView release];
rootLayer = [CALayer layer];
rootLayer.frame = self.view.bounds;
[self.view.layer addSublayer:rootLayer];
//Pentagon Path
pentagonPath = CGPathCreateMutable();
CGPoint center = self.view.center;
CGPathMoveToPoint(pentagonPath, nil, center.x, center.y - 75.0);
for(int i = 1; i < 5; ++i) {
CGFloat x = - 75.0 * sinf(i * 2.0 * M_PI / 5.0);
CGFloat y = - 75.0 * cosf(i * 2.0 * M_PI / 5.0);
CGPathAddLineToPoint(pentagonPath, nil,center.x + x, center.y + y);
}
CGPathCloseSubpath(pentagonPath); //This seems to be fixed in 4.0
//Star Path
starPath = CGPathCreateMutable();
center = self.view.center;
CGPathMoveToPoint(starPath, NULL, center.x, center.y + 75.0);
for(int i = 1; i < 5; ++i) {
CGFloat x = 75.0 * sinf(i * 4.0 * M_PI / 5.0);
CGFloat y = 75.0 * cosf(i * 4.0 * M_PI / 5.0);
CGPathAddLineToPoint(starPath, NULL, center.x + x, center.y + y);
};
CGPathCloseSubpath(starPath);
//Create Shape
shapeLayer = [CAShapeLayer layer];
shapeLayer.path = pentagonPath;
UIColor *fillColor = [UIColor colorWithWhite:0.9 alpha:1.0];
shapeLayer.fillColor = fillColor.CGColor;
shapeLayer.fillRule = kCAFillRuleNonZero;
[rootLayer addSublayer:shapeLayer];
}
-(void) viewDidLoad {
NSLog(#"viewDidLoad");
animationCount = 0;
[NSThread detachNewThreadSelector:#selector(startSequence) toTarget:self withObject:nil];
}
-(void) startSequence {
NSLog(#"startSequence");
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1.0
target:self
selector:#selector(startAnimation:)
userInfo:nil
repeats:YES];
[[NSRunLoop currentRunLoop] run];
self.repeatingTimer = timer;
[pool release];
}
-(void)startAnimation: (NSTimer*)theTimer {
NSLog(#"startAnimation");
animation = [CABasicAnimation animationWithKeyPath:#"path"];
animation.duration = 0.25;
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
animation.repeatCount = 0;
animation.autoreverses = NO;
animation.fillMode = kCAFillModeForwards;
animation.removedOnCompletion = NO;
if( (animationCount % 2) == 0) {
animation.fromValue = (id)pentagonPath;
animation.toValue = (id)starPath;
} else {
animation.fromValue = (id)starPath;
animation.toValue = (id)pentagonPath;
};
[shapeLayer addAnimation:animation forKey:#"animatePath"];
animationCount++;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
- (void)dealloc
{
CGPathRelease(starPath);
CGPathRelease(pentagonPath);
[super dealloc];
}
#end