How to perform animation in objective c - iphone

I'm trying to learn how to animate motion of the circle that I created in custom View. However, during the loop in startMovingCircle the circle doesn't show, it appears only when the loop finishes. I thought that if I call setNeedsDisplay every time it should redraw the view and the animation of moving circle should show up. Could someone tell me what am I doing wrong?
Also, I would like to ask if it is correct to program animation this way or should I use CoreAnimation and what are the benefits?
Can CoreAnimation animate shapes like circle, elipses, rectangle programmed in CoreGraphics?
MyDrawingView.m:
-(id)initWithFrame:(CGRect)frame{
self = [super initWithFrame:frame];
if (self) {
xCoordinate = 100.0;
yCoordinate = 100.0;
speed = 1.0;
}
return self;
}
- (void)drawRect:(CGRect)rect
{
NSLog(#"Drawing");
CGContextRef myContext = UIGraphicsGetCurrentContext();
CGContextBeginPath(myContext);
CGContextAddArc(myContext, xCoordinate, yCoordinate, 20.0, 0.0, 2 * M_PI, 0);
CGContextClosePath(myContext);
CGContextSetLineWidth(myContext, 1);
CGContextSetStrokeColorWithColor(myContext, [UIColor whiteColor].CGColor);
CGContextStrokePath(myContext);
}
-(void)moveCircle{
xCoordinate += speed;
yCoordinate += speed;
NSLog(#"Moving circle. X: %f, Y: %f", xCoordinate, yCoordinate);
[self setNeedsDisplay];
}
MyDrawingViewController.m:
-(void)startMovingCircle{
for(int i = 0; i < 1000; i++){
[myView moveCircle];
}
}

in -(void)moveCircle
you should create a core animation (with blocks)
and animate the center property of the view to animate.

You should add a pause in between iterations of your for() loop.

Related

Can't position SKShapeNode (rectangle) in grid precisely

Being new to the obj-c language and Sprite kit I'm having difficulties positioning a rectangle in a grid...
There's an offset in the rectangles I create - if I manage to get the code to position a rectangle in a chosen field another rectangle will be offset...
I have tried several different approches and I can get it to work using JavaFX. What am I doing wrong?
The photo below shows my problem clearly.
My code is rather simple and can be seen here:
#import "MyScene.h"
#implementation MyScene
const int ROWS = 10;
-(id)initWithSize:(CGSize)size {
if (self = [super initWithSize:size]) {
/* Setup your scene here */
self.backgroundColor = [SKColor darkGrayColor];
[self createBoardWithRows:ROWS];
[self createBoxPositionX:1 positionY:1];
[self createBoxPositionX:3 positionY:3];
[self createBoxPositionX:5 positionY:5];
}
return self;
}
-(void) createBoardWithRows: (int) rows{
for (int i = 1; i < rows; i++){
//Horisontal lines
int yPos = self.size.height/rows * i;
SKShapeNode *lineH = [SKShapeNode node];
CGMutablePathRef pathToDraw = CGPathCreateMutable();
CGPathMoveToPoint(pathToDraw, NULL, 0, yPos);
CGPathAddLineToPoint(pathToDraw, NULL, self.size.width, yPos);
lineH.path = pathToDraw;
lineH.lineWidth = 1.0;
[lineH setStrokeColor:[UIColor blackColor]];
//Vertical Lines
int xPos = self.size.width/rows * i;
SKShapeNode *lineV = [SKShapeNode node];
CGPathMoveToPoint(pathToDraw, NULL, xPos, 0);
CGPathAddLineToPoint(pathToDraw, NULL, xPos, self.size.height);
lineV.path = pathToDraw;
lineV.lineWidth = 1.0;
[lineV setStrokeColor:[UIColor blackColor]];
//Add lines
[self addChild:lineH];
[self addChild:lineV];
}
}
-(void) createBoxPositionX:(int) fieldIndexX positionY:(int) fieldIndexY{
int width = self.size.width/ROWS;
int height = self.size.height/ROWS;
int x = (width * fieldIndexX);
int y = (height * fieldIndexY);
CGRect box = CGRectMake(x, y, width, height);
SKShapeNode *shapeNode = [[SKShapeNode alloc] init];
shapeNode.path = [UIBezierPath bezierPathWithRect:box].CGPath;
shapeNode.fillColor = SKColor.yellowColor;
//Stroke settings
shapeNode.strokeColor = [SKColor clearColor];
shapeNode.lineWidth = 0;
[self addChild:shapeNode];
//Alternative rectangle
//SKSpriteNode spriteNodeWithColor:CGSize:
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
/* Called when a touch begins */
}
-(void)update:(CFTimeInterval)currentTime {
/* Called before each frame is rendered */
}
#end
It almost seems like you're not taking into account the line width of the border in each box - so when you create the yellow boxes they are displaced by the number of points equivalent to the number of "borders".
To fix this, change these two lines:
int x = (width * fieldIndexX);
int y = (height * fieldIndexY);
to:
int x = (width * fieldIndexX) + fieldIndexX;
int y = (height * fieldIndexY) + fieldIndexY;
If you are getting 1 pixel shifts into any direction it might be anchor point issue.
By default it is 0.5,0.5 - at center of the sprite.
So if your sprite has even length for example 4, the middle can fall on either 2 or 3 and you will get 1 pixel off positioning.
The fix for this is clear, set the anchor point of node to 0,0, like so:
node.anchorPoint = CGPointMake(0,0);
What this does is that the sprite is pinned to it parent with lower left corner instead of center. Since the lower left corner always starts at 0,0 pixel you will have correct positions regardless of pixel count of your nodes.
But you will have to adjust the nodes position to accomodate this change.
Hope this helps.

Draw circle on rectangle

I have UIView class and in method I want to draw first rectangles and sometimes circle
- (void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
if ([WhatToDraw isEqual:#"Fields"]) {
[self DrawField:context];
}
if ([WhatToDraw isEqual:#"Ball"]) {
[self DrawBall:context x:20 y:20];
}
}
-(void)DrawBall:(CGContextRef)context x:(float) x y:(float) y
{
UIGraphicsPushContext(context);
CGRect rect = CGRectMake(x, y, 25, 25);
CGContextClearRect(context, rect);
CGContextFillEllipseInRect(context, rect);
}
-(void)DrawField:(CGContextRef)context
{
columns = 6;
float offset = 5;
float boardWidth = self.frame.size.width;
float allOffset = (columns + 2) * offset;
float currentX = 10;
float currentWidth = (boardWidth - allOffset) / columns;
float currentHeight = currentWidth;
self.fieldsArray = [[NSMutableArray alloc] init];
//create a new dynamic button board
for (int columnIndex = 0; columnIndex<columns; columnIndex++) {
float currentY = offset;
for (int rowIndex=0; rowIndex<columns; rowIndex++) {
UIGraphicsPushContext(context);
//create new field
CGContextSetRGBFillColor(context, 1.0, 0.0, 0.0, 1.0);
CGContextBeginPath(context);
CGRect rect = CGRectMake(currentX, currentY, currentWidth, currentHeight);
CGContextAddRect(context, rect);
CGContextFillPath(context);
currentY = currentY + offset + currentHeight;
}
currentX = currentX + offset + currentWidth;
}
}
I also have method changing what to draw
-(void)Draw:(NSString*)Thing
{
self.WhatToDraw = Thing;
[self setNeedsDisplay];
}
Drawing rectangles (Fields) is ok, but when I click button to draw circle all rectangles disappear and only circle was drawn.
How can I draw circle on existing rectangle ?
The Problem
Your problem is that when a UIView redraws a region as marked by setNeedsDisplay or setNeedsDisplayInRect it will completely clear that region before executing your drawing code. This means that unless you draw both the rectangles and circle in a single drawing operation within drawRect you will never see the two both drawn in the area you choose to redraw, whether it be the entire view bounds with setNeedsDisplay or a specific area with setNeedsDisplayInRect.
The Solutions
There's no reason why you can't draw both the rectangles and circle each time within drawRect and optimise the performance of the drawing by only redrawing the regions necessary with setNeedsDisplayInRect.
Alternatively you could break up the content using CALayers and have the rectangles in one layer and the circle in another. This would allow you to leverage the animation capabilities of Core Animation. Core animation provides a simple and effective way to manipulate onscreen layers with implicit animations such as moving, resizing, changing colour etc.
my guess, the CGContextClearRect call in your DrawBall method is the responsable of rectangles disappearing... From Apple documentation: If the provided context is a window or bitmap context, Quartz effectively clears the rectangle.

Core Graphics drawing only black

I'm trying to develop a chess game on the iPhone, and have become stuck on an otherwise insignificant detail but can not seem to get past it. Can anybody see the piece that's missing?
Here's the problem. When a user clicks on a square, it should highlight in some faded color. What actually is happening though, is it just draws black, totally regardless of what color I set it to.
Here's an image of what I get. The black circle should be red, or any color.
Here, notice the UIView I'm drawing to is behind the pieces view. Doubt it matters, but I want to be complete.
Finally, the code for the layer:
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
self.backgroundColor = [UIColor clearColor];
rowSelected = 0;
colSelected = 0;
}
return self;
}
-(void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextClearRect(context, rect);
CGContextSetRGBStrokeColor(context, 255.0, 0.0, 0.0, 1.0);
if (rowSelected && colSelected)
{
int gridsize = (self.frame.size.width / 8);
int sx = (colSelected-1) * gridsize;
int sy = (rowSelected-1) * gridsize;
int ex = gridsize;
int ey = gridsize;
CGContextFillEllipseInRect(context, CGRectMake(sx,sy,ex,ey));
}
}
// Handles the start of a touch
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
int gridsize = (self.frame.size.width / 8);
// Position of touch in view
UITouch *touch = [[event allTouches] anyObject];
CGPoint loc = [touch locationInView:self.superview];
int x = loc.x / gridsize;
int y = loc.y / gridsize;
rowSelected = y + 1;
colSelected = x + 1;
[self setNeedsDisplay];
}
I have tried a number of things, using a value of 1 rather than 255, playing with the alpha, etc, but I can't get anything other than a solid black.
EDIT:
Also, here's the code of this view's superview:
#implementation ChessBoard
-(id)initWithFrame:(CGRect)frame
{
if (self != [super initWithFrame:frame])
return self;
int SQUARE_SIZE = frame.size.width / 8;
currentSet = BW;
UIImage *img = [UIImage imageNamed:#"board.png"];
background = [[UIImageView alloc] initWithImage:img];
[background setFrame:frame];
[self addSubview:background];
overlay = [[ChessBoardOverlay alloc] initWithFrame:frame];
[self addSubview:overlay];
In the function CGContextSetRGBStrokeColor color compontent values (red, green,blue, alpha) need to be between 0 and 1. Since you are assuming that 255 is the maximum for a color you need to divide all of your color component values by 255.0f.
CGContextSetRGBStrokeColor(context, 255.0f/255.0f, 0.0/255.0f, 0.0/255.0f, 1.0);
You say you're drawing to a layer... but a CALayer doesn't have a drawRect: method, it has drawInContext:
I would guess by trying to call drawRect: in your instance of a CALayer that the true context hasn't been built correctly, or it has but you're not tying in to it.
Maybe you should state your color this way:
CGContextSetRGBStrokeColor(context, 255.0/255.0f, 0.0/255.0f, 0.0/255.0f, 1.0f);
This happened to me once using:
[UIColor colorWithRed:12.0/255.0f green:78.0/255.0f blue:149.0/255.0f alpha:1.0f];
until I added the /255.0f so that may work.

how to add an animation effect of drawing a circle in cocos2d

i am working on a game to spot the difference between 2 images.
I want to add an effect when you spot it. drawing out a circle would be much better than just showing the circle suddenly. but i've never done core animation or opengl before.
i don't think preparing 100 sprites and changing the sprite frame by frame is a good idea.
here is my code: (just add a circle image to both left and right image. )
-(void) show {
CCSprite* leftCircle = [CCSprite spriteWithFile:#"circle.png"];
CCSprite* rightCircle = [CCSprite spriteWithFile:#"circle.png"];
leftCircle.scaleX = size.width / [leftCircle boundingBox].size.width;
leftCircle.scaleY = size.height / [leftCircle boundingBox].size.height;
rightCircle.scaleX = size.width / [rightCircle boundingBox].size.width;
rightCircle.scaleY = size.height / [rightCircle boundingBox].size.height;
leftCircle.anchorPoint = ccp(0, 1);
rightCircle.anchorPoint = ccp(0, 1);
leftCircle.position = leftPosition;
rightCircle.position = rightPosition;
[[GameScene sharedScene] addChild:leftCircle z: 3];
[[GameScene sharedScene] addChild:rightCircle z: 3];
shown = YES;
}
So how can i implement it? It would be great if you can provide some source code.
As a simple way i can recommend you to create a circle and put it's scale to zero. Then create a CCScale action and run it. It will give you a growing circle. Here is the code:
CCSprite *sprite = [CCSprite spriteWithFile:#"mySprite.png"];
[sprite setScale:0.01];
id scale = [CCScale actionWithDuration:0.3 scale:1];
[sprite runAction:scale];
The other action can be used is CCFadeIn.You can make your sprite invisible after creation and make it fade in:
CCSprite *sprite = [CCSprite spriteWithFile:#"mySprite.png"];
[sprite setOpacity:0];
id fade = [CCFadeIn actionWithDuration:0.3];
[sprite runAction:fade];
Also you can combine this actions:
[sprite runAction:fade];
[sprite runAction:scale];
Also you can make it big (set scale 3 for example) and transparent. And make it fade in and scale down to highlight your image
To Draw a circle via drawing effect you can make it from small parts (arcs) and then build your circle from them. I think it also will be cool if you make don't make the part visible when adding, but make it fade in. I mean something like this:
-(void) init
{
NSMutableArray *parts = [[NSMutableArray array] retain]; //store it in your class variable
parts_ = parts;
localTime_ = 0; //float localTime_ - store the local time in your layer
//create all the parts here, make them invisible and add to the layer and parts
}
-(void) update: (CCTime) dt //add update method to your layer that will be called every simulation step
{
localTime_ += dt;
const float fadeTime = 0.1;
int currentPart = localTime_ / fadeTime;
int i = 0;
for (CCSprite *part in parts)
{
//setup the opacity of each part according to localTime
if (i < currentPart) [part setOpacity:255];
else if (i == currentPart)
{
float localLocalTime = localTime - i*fadeTime;
float alpha = localLocalTime / fadeTime;
[part setOpacity:alpha];
}
++i;
}
}
It is rather simple to create the effect you want.
You can realize it by setting the strokeEnd of a CAShapeLayer.
Here are the details:
create a path you then assign to a CAShapeLayer
UIBezierPath* circlePath = [UIBezierPath bezierPathWithArcCenter:CGPointMake(100.0, 100.0) radius:80.0 startAngle:DEGREES_TO_RADIANS(270) endAngle:DEGREES_TO_RADIANS(270.01) clockwise:NO];
circle = [CAShapeLayer layer];
circle.path = circlePath.CGPath;
circle.strokeColor = [[UIColor blackColor] CGColor];
circle.fillColor = [[UIColor whiteColor] CGColor];
circle.lineWidth = 6.0;
circle.strokeEnd = 0.0;
[self.view.layer addSublayer:circle];
set the strokeEnd to implicitly animate the drawing of the circle
circle.strokeEnd = 1.0;
That's it! The circle will be automatically drawn for you ;). Here is the code from above with a GestureRecognizer to trigger the animation. Copy this to an empty project of type "Single View Application" and paste it to the ViewController.
#define DEGREES_TO_RADIANS(angle) ((angle) / 180.0 * M_PI)
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
UIBezierPath* circlePath = [UIBezierPath bezierPathWithArcCenter:CGPointMake(100.0, 100.0) radius:80.0 startAngle:DEGREES_TO_RADIANS(270) endAngle:DEGREES_TO_RADIANS(270.01) clockwise:NO];
circle = [CAShapeLayer layer];
circle.path = circlePath.CGPath;
circle.strokeColor = [[UIColor blackColor] CGColor];
circle.fillColor = [[UIColor whiteColor] CGColor];
circle.lineWidth = 6.0;
circle.strokeEnd = 0.0;
[self.view.layer addSublayer:circle];
// add a tag gesture recognizer
// add a single tap gesture recognizer
UITapGestureRecognizer* tapGR = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleTapGesture:)];
[self.view addGestureRecognizer:tapGR];
}
#pragma mark - gesture recognizer methods
//
// GestureRecognizer to handle the tap on the view
//
- (void)handleTapGesture:(UIGestureRecognizer *)gestureRecognizer {
circle.strokeEnd = 1.0;
}

UIView: rotation + movement

I'm trying to move and rotate a UIImageView. I'm not using Core Animation for this nor I can use it.
By the way, the core part of the animation is what follows:
CGRect r = self.frame;
r.origin.y -= self.gravity + spring;
r.origin.x -= 1;
self.rotation -= 0.015;
self.transform = CGAffineTransformMakeRotation(rotation);
self.frame = r;
It almost works. I does move or it does rotate in place, but when I try to move and rotate it, it completely deforms skewing.
I've tried to move and then rotate and viceversa. I also tried various values for content mode with no luck. Any help?
EDIT
useful tutorial: http://www.permadi.com/blog/2009/05/iphone-sdk-bouncing-and-rotating-ball-example/
It's hard to be sure what's going on without a picture of what you are seeing, but the following may be useful:
When you apply a rotation to a view it rotates it around (0,0), so you need to make sure you set the bounds of your view so that (0,0) is where you want the center of rotation. Suppose the image you are rotating is a 100x200 image and you want to rotate around the center of the image then you would need to say self.bounds = CGMakeRect(-50, -100, 100, 200); If you don't do this it will rotate about it's upper-left corner making sort of a spiral.
Oh, and using self.center to set the view's position seems to be more predictable than using self.frame when you have applied a rotation.
Here's some demo code that might make it easier to understand. In this case the code that does the animation is the view controller, but you should get the idea:
#define USE_SELF_CENTER 0
- (void)viewDidLoad {
[super viewDidLoad];
self.rotation = 0.0f;
self.translation = CGPointMake(self.view.bounds.size.width/2.0f, 0.0);
ObjectView* ov = [[ObjectView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 100.0f, 100.0f)];
#if USE_SELF_CENTER==1
ov.center = self.translation;
#else
ov.transform = CGAffineTransformMakeTranslation(self.translation.x, self.translation.y);
#endif
// adjust bounds for center of rotation
ov.bounds = CGRectMake(-50.0f, -50.0f, 100.0f, 100.0f);
[self.view addSubview:ov];
self.objectView = ov;
[ov release];
NSTimer *aTimer = [NSTimer timerWithTimeInterval:0.5 target:self selector:#selector(nextFrame)
userInfo:nil repeats:YES];
self.timer = aTimer;
[aTimer release];
[[NSRunLoop mainRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];
}
- (void)nextFrame
{
NSLog(#"nextFrame");
self.rotation += (5.0 / 180.0f * M_PI);
self.translation = CGPointMake(self.translation.x, self.translation.y + 2.0);
#if USE_SELF_CENTER==1
CGAffineTransform t = CGAffineTransformMakeRotation(self.rotation);
self.objectView.center = self.translation;
self.objectView.transform = t;
#else
CGAffineTransform rot = CGAffineTransformMakeRotation(self.rotation);
CGAffineTransform txy = CGAffineTransformMakeTranslation(self.translation.x, self.translation.y);
CGAffineTransform t = CGAffineTransformConcat(rot, txy);
self.objectView.transform = t;
#endif
}
The code demonstrates two ways of doing what you want. If USE_SELF_CENTER is zero then all the animation is done via transforms. If USE_SELF_CENTER is non-zero a transform is used for rotation and the translation is accomplished by setting the center of the view.
use something like this, the idea is combine both the transformation i.e.
CGAffineTransform transforms = CGAffineTransformConcat(CGAffineTransformMakeTranslation(-1, -(self.gravity + spring)),CGAffineTransformMakeRotation(rotation));
self.transform = transforms;
Hope this will work
Hello Friend please use this steps. On your XIB, take UIImageView and set Image and set its IBOutlet.
In .h file
int theta;
NSTimer *tmrAnimation;
IBOutlet UIImageView *imgViewLoader;
In .m file
- (void)viewDidLoad
{
[super viewDidLoad];
theta = 0;
tmrAnimation = [NSTimer scheduledTimerWithTimeInterval:0.01 target:self selector:#selector(animationImageViewLoader) userInfo:nil repeats:YES];
}
Add This function in your .m file.
-(void)animationImageViewLoader
{
CGFloat angle = theta * (PI / 100);
CGAffineTransform transform = CGAffineTransformMakeRotation(angle);
theta = (theta + 1) % 200;
imgViewLoader.transform = transform;
}
This code will help for Image Rotation only....
Following code helped me in achieving translation(vertical movement) and rotation together.
CGAffineTransform transform = CGAffineTransformMakeTranslation(0, -200);
transform = CGAffineTransformRotate(transform, M_PI);
[UIView transitionWithView:self
duration:0.5
options:UIViewAnimationOptionAllowAnimatedContent<br>
animations:^{
[view setTransform:transform];
}
completion:^(BOOL finished){
}];
Hope this will help someone else...