Best way to save/load stroke data of the sample GLPaint app? - objective-c

I noticed that by default the sample app GLPaint comes with a recorded data in binary and it loads up on start, it is what is used to display the "Shake me" text.
I'm creating a similar painting app and I was just wondering what is the best way to actually record these strokes and load them up next time.
Currently I tried saving the location of every vertex using Core Data, then upon start it reads and renders all the points again, however I found this too slow.
Is there a better/more efficient method of doing this? Can the entire viewBuffer be saved as binary and then loaded back up?

If you look at the Recording.data you will notice that each line is its own array. To capture the ink and play it back you need to have an array of arrays. For purposes of this demo - declare a mutable array - writRay
#synthesize writRay;
//init in code
writRay = [[NSMutableArray alloc]init];
Capture the ink
// Handles the continuation of a touch.
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
CGRect bounds = [self bounds];
UITouch* touch = [[event touchesForView:self] anyObject];
// Convert touch point from UIView referential to OpenGL one (upside-down flip)
if (firstTouch) {
firstTouch = NO;
previousLocation = [touch previousLocationInView:self];
previousLocation.y = bounds.size.height - previousLocation.y;
/******************* create a new array for this stroke's points **************/
[writRay addObject:[[NSMutableArray alloc]init]];
/***** add 1st point *********/
[[writRay objectAtIndex:[writRay count] -1]addObject:[NSValue valueWithCGPoint:previousLocation]];
} else {
location = [touch locationInView:self];
location.y = bounds.size.height - location.y;
previousLocation = [touch previousLocationInView:self];
previousLocation.y = bounds.size.height - previousLocation.y;
/********* add additional points *********/
[[writRay objectAtIndex:[writRay count] -1]addObject:[NSValue valueWithCGPoint:previousLocation]];
}
// Render the stroke
[self renderLineFromPoint:previousLocation toPoint:location];
}
Playback the ink.
- (void)playRay{
if(writRay != NULL){
for(int l = 0; l < [writRay count]; l++){
//replays my writRay -1 because of location point
for(int p = 0; p < [[writRay objectAtIndex:l]count] -1; p ++){
[self renderLineFromPoint:[[[writRay objectAtIndex:l]objectAtIndex:p]CGPointValue] toPoint:[[[writRay objectAtIndex:l]objectAtIndex:p + 1]CGPointValue]];
}
}
}
}
For best effect shake the screen to clear and call playRay from changeBrushColor in the AppController.

Related

Background Sprite Image keeps being centred on screen

I'm trying to implement a scrollable background into my current GameScene. This is supposed to be done via Gesture Recognition, which I'm already using for Taps and moving other scene objects.
Unlike pretty much every other result returned by my Google searches, I don't want an infinitely scrolling background. It just needs to move with your finger, and stay where it's been moved.
The Problem:
I can move the background SKSpriteNode in my scene, but as soon as I try to move it again it snaps to the center and your scrolling effectively becomes useless. It keeps resetting itself.
Here's what I've got so far for moving my Sprites:
-(void)selectTouchedNode:(CGPoint)location
{
SKSpriteNode *node = (SKSpriteNode *)[self nodeAtPoint:location];
if ([self.selectedNode isEqual:node]){
if (![self.selectedNode isEqual:self.background]){
self.selectedNode = NULL;
}
}
if ([node isKindOfClass:[SKLabelNode class]]){
self.selectedNode = node.parent;
} else {
self.selectedNode = node;
}
NSLog(#"Node Selected: %# | Position: %f, %f",node.name,node.position.x,node.position.y);
}
- (void)respondToPan:(UIPanGestureRecognizer *)recognizer {
if (recognizer.state == UIGestureRecognizerStateBegan) {
// Get Touch Location in the View
CGPoint touchLocation = [recognizer locationInView:recognizer.view];
// Convert that Touch Location
touchLocation = [self convertPointFromView:touchLocation];
// Select Node at said Location.
[self selectTouchedNode:touchLocation];
} else if (recognizer.state == UIGestureRecognizerStateChanged) {
// Get the translation being performed on the sprite.
CGPoint translation = [recognizer translationInView:recognizer.view];
// Copy to another CGPoint
translation = CGPointMake(translation.x, -translation.y);
// Translate the currently selected object
[self translateMotion:recognizer Translation:translation];
// Reset translation to zero.
[recognizer setTranslation:CGPointZero inView:recognizer.view];
} else if (recognizer.state == UIGestureRecognizerStateEnded) {
// Fetch Current Location in View
CGPoint touchLocation = [recognizer locationInView:recognizer.view];
// Convert to location in game.
CGPoint correctLocation = [self convertPointFromView:touchLocation];
// If the selected node is the background node
if ([self.selectedNode isEqual:self.background]) {
NSLog(#"Scrolling the background: Node is: %#",self.selectedNode.name);
// Set up a scroll duration
float scrollDuration = 0.2;
// Get the new position based on what is allowed by the function
CGPoint newPos = [self backgroundPanPos:correctLocation];
NSLog(#"New Position: %f, %f",newPos.x,newPos.y);
// Remove all Actions from the background
[_selectedNode removeAllActions];
// Move the background to the new position with defined duration.
SKAction *moveTo = [SKAction moveTo:newPos duration:scrollDuration];
// SetTimingMode for a smoother transition
[moveTo setTimingMode:SKActionTimingEaseOut];
// Run the action
[_selectedNode runAction:moveTo];
} else {
// Otherwise, just put the damn node where the touch occured.
self.selectedNode.position = correctLocation;
}
}
}
// NEW PLAN: Kill myself
- (void)translateMotion:(UIGestureRecognizer *)recognizer Translation:(CGPoint)translation {
// Fetch Location being touched
CGPoint touchLocation = [recognizer locationInView:recognizer.view];
// Convert to place in View
CGPoint location = [self convertPointFromView:touchLocation];
// Set node to that location
self.selectedNode.position = location;
}
- (CGPoint)backgroundPanPos:(CGPoint)newPos {
// Create a new point based on the touched location
CGPoint correctedPos = newPos;
return correctedPos;
}
What do I know so far?
I've tried printing the positions before the scrolling, when it ends, and when it gets initiated again.
Results are that the background does move positions, and once you try to move it again it starts at those new Coordinates, the screen has just repositioned itself over the centre of the sprite.
Supporting Illustration:
I'm not sure I 100% understand the situation you are describing, but I believe that it might be related to the anchor point of your background sprite node.
in your method:
- (void)translateMotion:(UIGestureRecognizer *)recognizer Translation:(CGPoint)translation
you have the line:
self.selectedNode.position = location;
Since your background sprite's anchor point is set to its center by default, any time you move a new touch, it will snap the background sprite's center to the location of your finger.
In other words, background.sprite.position sets the coordinates for the background sprite's anchor point (which by default is the center of the sprite), and in this case any time you set a new position, it is moving the center to that position.
The solution in this case would be to shift the anchor point of the background sprite to be directly under the touch each time, so you are changing the position of the background relative to the point of the background the touch started on.
It's a little hard to explain, so here's some sample code to show you:
1) Create a new project in Xcode using the Sprite Kit Game template
2) Replace the contents of GameScene.m with the following:
#import "GameScene.h"
#interface GameScene ()
#property (nonatomic, strong) SKSpriteNode *sprite;
#end
#implementation GameScene
-(void)didMoveToView:(SKView *)view {
/* Setup your scene here */
self.sprite = [SKSpriteNode spriteNodeWithImageNamed:#"Spaceship"];
self.sprite.position = CGPointMake(CGRectGetMidX(self.frame),
CGRectGetMidY(self.frame));
[self addChild:self.sprite];
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
/* Called when a touch begins */
UITouch *touch = [touches anyObject];
CGPoint nodelocation = [touch locationInNode:self.sprite];
//[self adjustAnchorPointForSprite:self.sprite toLocation:nodelocation];
CGPoint location = [touch locationInNode:self];
self.sprite.position = location;
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInNode:self];
self.sprite.position = location;
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
}
-(void)adjustAnchorPointForSprite:(SKSpriteNode *)sprite toLocation:(CGPoint)location {
// Remember the sprite's current position
CGPoint originalPosition = sprite.position;
// Convert the coordinates of the passed-in location to be relative to the bottom left of the sprite (instead of its current anchor point)
CGPoint adjustedNodeLocation = CGPointMake(sprite.anchorPoint.x * sprite.size.width + location.x, sprite.anchorPoint.y * sprite.size.height + location.y);
// Move the anchor point of the sprite to match the passed-in location
sprite.anchorPoint = CGPointMake(adjustedNodeLocation.x / self.sprite.size.width, adjustedNodeLocation.y / self.sprite.size.height);
// Undo any change of position caused by moving the anchor point
self.sprite.position = CGPointMake(sprite.position.x - (sprite.position.x - originalPosition.x), sprite.position.y - (sprite.position.y - originalPosition.y));
}
-(void)update:(CFTimeInterval)currentTime {
/* Called before each frame is rendered */
}
#end
3) Run the project in the sim or on a device and click / touch the top tip of the space ship and start dragging.
4) Notice that the spaceship snaps its center point to where your touch is. If you drag and release it to a new location, and the touch the screen again, it snaps its center back to your finger.
5) Now uncomment the line:
[self adjustAnchorPointForSprite:self.sprite toLocation:nodelocation];
and run the project again.
6) See how you can now drag the ship where you want it, and when you touch it later, it stays in place and follows your finger from the point you touched it. This is because the anchor point is now being adjusted to the point under the touch each time a new touch begins.
Hopefully this gives you a solution you can use in your game as well.

How do I detect a touch event on a moving UIImageView?

When researching "How do I detect a touch event on a moving UIImageView?" I've come across several answers and I tried to implement them to my app. Nothing I've come across seems to work. I'll explain what I'm trying to do then post my code. Any thoughts, suggestions, comments or answers are appreciated!
My app has several cards floating across the screen from left to right. These cards are various colors and the object of the game is the drag the cards down to their similarly colored corresponding container. If the user doesn't touch and drag the cards fast enough, the cards will simply drift off the screen and points will be lost. The more cards contained in the correct containers, the better the score.
I've written code using core animation to have my cards float from the left to right. This works. However when attempting to touch a card and drag it toward it's container, it isn't correctly detecting that I'm touching the UIImageView of the card.
To test if my I'm properly implementing the code to move a card, I've also written some code allows movement for a non-moving card. In this case my touch is being detected and acting accordingly.
Why can I only interact with stationary cards? After researching this quite a bit it seems that the code:
options:UIViewAnimationOptionAllowUserInteraction
is the key ingredient to get my moving UIImages to be detected. However I tried this doesn't seem to have any effect.
I another key thing that I may be doing wrong is not properly utilizing the correct presentation layer. I've added code like this to my project and I also only works on non-moving objects:
UITouch *t = [touches anyObject];
UIView *myTouchedView = [t view];
CGPoint thePoint = [t locationInView:self.view];
if([_card.layer.presentationLayer hitTest:thePoint])
{
NSLog(#"You touched a Card!");
}
else{
NSLog(#"backgound touched");
}
After trying these types of things I'm getting stuck. Here is my code to understand this a bit more completely:
#import "RBViewController.h"
#import <QuartzCore/QuartzCore.h>
#interface RBViewController ()
#property (nonatomic, strong) UIImageView *card;
#end
#implementation RBViewController
- (void)viewDidLoad
{
srand(time (NULL)); // will be used for random colors, drift speeds, and locations of cards
[super viewDidLoad];
[self setOutFirstCardSet]; // this sends out 4 floating cards across the screen
// the following creates a non-moving image that I can move.
_card = [[UIImageView alloc] initWithFrame:CGRectMake(400,400,100,100)];
_card.image = [UIImage imageNamed:#"goodguyPINK.png"];
_card.userInteractionEnabled = YES;
[self.view addSubview:_card];
}
the following method sends out cards from a random location on the left side of the screen and uses core animation to drift the card across the screen. Notice the color of the card and the speed of the drift will be randomly generated as well.
-(void) setOutFirstCardSet
{
for(int i=1; i < 5; i++) // sends out 4 shapes
{
CGRect cardFramei;
int startingLocation = rand() % 325;
CGRect cardOrigini = CGRectMake(-100,startingLocation + 37, 92, 87);
cardFramei.size = CGSizeMake(92, 87);
CGPoint origini;
origini.y = startingLocation + 37;
origini.x = 1200;
cardFramei.origin = origini;
_card.userInteractionEnabled = YES;
_card = [[UIImageView alloc] initWithFrame:cardOrigini];
int randomColor = rand() % 7;
if(randomColor == 0)
{
_card.image = [UIImage imageNamed:#"goodguy.png"];
}
else if (randomColor == 1)
{
_card.image = [UIImage imageNamed:#"goodguyPINK.png"];
}
else if (randomColor == 2)
{
_card.image = [UIImage imageNamed:#"goodGuyPURPLE.png"];
}
else if (randomColor == 3)
{
_card.image = [UIImage imageNamed:#"goodGuyORANGE.png"];
}
else if (randomColor == 4)
{
_card.image = [UIImage imageNamed:#"goodGuyLightPINK.png"];
}
else if (randomColor == 5)
{
_card.image = [UIImage imageNamed:#"goodGuyBLUE.png"];
}
else if (randomColor == 6)
{
_card.image = [UIImage imageNamed:#"goodGuyGREEN.png"];
}
_card.userInteractionEnabled = YES; // this is also written in my viewDidLoad method
[[_card.layer presentationLayer] hitTest:origini]; // not really sure what this does
[self.view addSubview:_card];
int randomSpeed = rand() % 20;
int randomDelay = rand() % 2;
[UIView animateWithDuration:randomSpeed + 10
delay: randomDelay + 4
options:UIViewAnimationOptionAllowUserInteraction // here is the method that I thought would allow me to interact with the moving cards. Not sure why I can't
animations: ^{
_card.frame = cardFramei;
}
completion:NULL];
}
}
notice the following method is where I put CALayer and hit test information. I'm not sure if I'm doing this correctly.
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *t = [touches anyObject];
UIView *myTouchedView = [t view];
CGPoint thePoint = [t locationInView:self.view];
thePoint = [self.view.layer convertPoint:thePoint toLayer:self.view.layer.superlayer];
CALayer *theLayer = [self.view.layer hitTest:thePoint];
if([_card.layer.presentationLayer hitTest:thePoint])
{
NSLog(#"You touched a Shape!"); // This only logs when I touch a non-moving shape
}
else{
NSLog(#"backgound touched"); // this logs when I touch the background or an moving shape.
}
if(myTouchedView == _card)
{
NSLog(#"Touched a card");
_boolHasCard = YES;
}
else
{
NSLog(#"Didn't touch a card");
_boolHasCard = NO;
}
}
I want the following method to work on moving shapes. It only works on non-moving shapes. Many answers say to have the touch ask which class the card is from. As of now all my cards on of the same class (the viewController class). When trying to have the cards be their own class, I was having trouble having that view appear on my main background controller. Must I have various cards be from different classes for this to work, or can I have it work without needing to do so?
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [[event allTouches] anyObject];
if([touch view]==self.card)
{
CGPoint location = [touch locationInView:self.view];
self.card.center=location;
}
}
This next method resets the movement of a card if the user starts moving it and then lifts up on it.
-(void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
if(_boolHasCard == YES)
{
[UIView animateWithDuration:3
delay: 0
options:UIViewAnimationOptionAllowUserInteraction
animations: ^{
CGRect newCardOrigin = CGRectMake(1200,_card.center.y - 92/2, 92, 87);
_card.frame = newCardOrigin;
}
completion:NULL];
}
}
#end
The short answer is, you can't.
Core Animation does not actually move the objects along the animation path. They move the presentation layer of the object's layer.
The moment the animation begins, the system thinks the object is at it's destination.
There is no way around this if you want to use Core Animation.
You have a couple of choices.
You can set up a CADisplayLink on your view controller and roll your own animation, where you move the center of your views by a small amount on each call to the display link. This might lead to poor performance and jerky animation if you're animating a lot of objects however.
You can add a gesture recognizer to the parent view that contains all your animations, and then use layer hit testing on the paren't view's presentation view to figure out which animating layer got tapped, then fetch that layer's delegate, which will be the view you are animating. I have a project on github that shows how to do this second technique. It only detects taps on a single moving view, but it will show you the basics: Core Animation demo project on github.
(up-votes always appreciated if you find this post helpful)
It looks to me that your problem is really with just an incomplete understanding of how to convert a point between coordinate spaces. This code works exactly as expected:
- (void)viewDidLoad
{
[super viewDidLoad];
CGPoint endPoint = CGPointMake([[self view] bounds].size.width,
[[self view] bounds].size.height);
CABasicAnimation *animation = [CABasicAnimation
animationWithKeyPath:#"position"];
animation.fromValue = [NSValue valueWithCGPoint:[[_imageView layer] position]];
animation.toValue = [NSValue valueWithCGPoint:endPoint];
animation.duration = 30.0f;
[[_imageView layer] addAnimation:animation forKey:#"position"];
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *t = [touches anyObject];
CGPoint thePoint = [t locationInView:self.view];
thePoint = [[_imageView layer] convertPoint:thePoint
toLayer:[[self view] layer]];
if([[_imageView layer].presentationLayer hitTest:thePoint])
{
NSLog(#"You touched a Shape!");
}
else{
NSLog(#"backgound touched");
}
}
Notice the line in particular:
thePoint = [[_imageView layer] convertPoint:thePoint
toLayer:[[self view] layer]];
When I tap on the layer image view while it's animating, I get "You touched a Shape!" in the console window and I get "background touched" when I tap around it. That's what you're wanting right?
Here's a sample project on Github
UPDATE
To help with your follow up question in the comments, I've written the touchesBegan code a little differently. Imagine that you've add all of your image views to an array (cleverly named imageViews) when you create them. You would alter your code to look something like this:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *t = [touches anyObject];
CGPoint thePoint = [t locationInView:self.view];
for (UIImageView *imageView in [self imageViews]) {
thePoint = [[imageView layer] convertPoint:thePoint
toLayer:[[self view] layer]];
if([[imageView layer].presentationLayer hitTest:thePoint]) {
NSLog(#"Found it!!");
break; // No need to keep iterating, we've found it
} else{
NSLog(#"Not this one!");
}
}
}
I'm not sure how expensive this is, so you may have to profile it, but it should do what you're expecting.

How can I have the same sprite in multiple locations dynamically?

How can I have the same sprite in multiple locations dynamically? I have already seen the other question, but, you can only do that with three sprites. I want to have a dynamic number of sprites. My objective is that I am trying to make, instead of shooting only one bullet, I want it to shoot three or more. I have all of the math done, but, I need to draw the three sprites in a for-loop. Here is what I have so far.
- (void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch * touch = [touches anyObject];
CGPoint pointOne = [touch locationInView:[touch view]];
CGSize size = [[CCDirector sharedDirector] winSize];
CGPoint position = turret.position;
CGFloat degrees = angleBetweenLinesInDegrees(position, pointOne);
turret.rotation = degrees;
pointOne.y = size.height-pointOne.y;
CCSprite *projectile = [CCSprite spriteWithFile:#"projectile.png"];
projectile.position = turret.position;
// Determine offset of location to projectile
int angle = angleBetweenLinesInDegrees(position, pointOne);
int startAngle = angle-15;
int shots = 3;
NSMutableArray *projectiles = [[NSMutableArray alloc] initWithCapacity:shots];
// Ok to add now - we've double checked position
for(int i = 0;i<shots;i++) {
[self addChild:projectile z:1];
int angleToShoot = angle;
int x = size.width;
int y = x*tan(angle);
CGPoint realDest = ccp(x,y);
projectile.tag = 2;
if (paused==0 ) {
[_projectiles addObject:projectile];
// Move projectile to actual endpoint
[projectile runAction:
[CCSequence actions:
[CCMoveTo actionWithDuration:1 position:realDest],
[CCCallBlockN actionWithBlock:^(CCNode *node) {
[_projectiles removeObject:node];
[node removeFromParentAndCleanup:YES];
}],
nil]];
}
}
}
This gives me the error: 'NSInternalInconsistencyException', reason: 'child already added. It can't be added again'
you need to create 3 different sprite and add all 3 of them as a child.
usually for doing stuff like this is better to use a CCBatchNode (take a look to the cocos doc).
With a batchnode you get all the childs be drawn in 1 draw call with the only constrain that all the childs of the batchnode needs to have the texture on the same spriteSheet (or in your case if they have the same "filename")
for just 3 projectiles you wont have performance problems but its the correct way to design it, if you will need to have dozens of projectiles on screen without using a batchnode the game wont run smooth.
to recap:
create a ccbatchnode,
add the batchnode as a child of self (i suppose its ur layer or main node)
create 3 sprites and add them as a child of the batchnode

Need CCRenderTexture to render faster ios

I'm making a drawing app, and I'm having the users draw with CCRenderTexture. It basically keeps rendering a picture of a black circle to simulate drawing. When I move my finger slowly, it works really well since the circles come together to form a line. However, when I move my finger quickly, it ends up just being a bunch of circles that aren't connected (http://postimage.org/image/wvj3w632n/). My question is how I get the render texture to render the image faster or have it fill in the blanks for me.
Also, I'm not completely sold on this method, but it's what I've found while looking around. Feel free to suggest whatever you think would be better. I was originally using ccdrawline but it really killed my performance. Thanks!
The gaps between start point and the end points need to be sorted out. I am pasting code that might help you to resolve the situation you showed in the link.
in .h file
CCRenderTexture *target;
CCSprite* brush;
in the init method of .m file
target = [[CCRenderTexture renderTextureWithWidth:size.width height:size.height] retain];
[target setPosition:ccp(size.width/2, size.height/2)];
[self addChild:target z:1];
brush = [[CCSprite spriteWithFile:#"brush_i3.png"] retain];
add the touches method I am showing the touchesMoved code.
-(void)ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint start = [touch locationInView: [touch view]];
start = [[CCDirector sharedDirector] convertToGL: start];
CGPoint end = [touch previousLocationInView:[touch view]];
end = [[CCDirector sharedDirector] convertToGL:end];
printf("\n x= %f \t y= %f",start.x,start.y);
float distance = ccpDistance(start, end);
if (distance > 1)
{
int d = (int)distance;
for (int i = 0; i < d; i++)
{
float difx = end.x - start.x;
float dify = end.y - start.y;
float delta = (float)i / distance;
[brush setPosition:ccp(start.x + (difx * delta), start.y + (dify * delta))];
[target begin];
[brush setColor:ccc3(0, 255, 0)];
brush.opacity = 5;
[brush visit];
[target end];
}
}
}
Hopefully it would work for you.
Its not that CCRenderTexture draws too slow its that the event only fires so often. You do need to fill in the gaps between the touch points you receive.
There is a great tutorial here about it which you may have already seen, http://www.learn-cocos2d.com/2011/12/how-to-use-ccrendertexture-motion-blur-screenshots-drawing-sketches/#sketching

Need help with moving UIImageViews in a circle via touch

I'm designing an app that involves linear graphs. What I have right now is a line with three dots: one in the center for moving the whole line, and the other two, lower and higher on the line, for rotating it. The problem that I have is figuring out how to rotate the other two dots when the user touches on either of them.
I haven't actually done much with the rotation dots yet, but here's my code so far:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch* touch = [touches anyObject];
CGPoint point = [touch locationInView:graph];
if (CGRectContainsPoint(moveDot.frame, point)) {
moveDotIsTouched = YES;
}
if (CGRectContainsPoint(rotateDot1.frame, point)) {
rotateDot1IsTouched = YES;
}
if (CGRectContainsPoint(rotateDot2.frame, point)) {
rotateDot2IsTouched = YES;
}
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch* touch = [touches anyObject];
CGPoint point = [touch locationInView:graph];
if (moveDotIsTouched) {
if ((point.y > 0) && (point.y < 704))
{
if (point.y < 64) {rotateDot1.hidden = YES;}
else {rotateDot1.hidden = NO;}
if (point.y > 640) {rotateDot2.hidden = YES;}
else {rotateDot2.hidden = NO;}
moveDot.center = CGPointMake(moveDot.center.x, point.y);
rotateDot1.center = CGPointMake(moveDot.center.x+64, moveDot.center.y-64);
rotateDot2.center = CGPointMake(moveDot.center.x-64, moveDot.center.y+64);
graph.base = -(point.y)/32 + 11;
if (graph.base < 0) {[functionField setText:[NSString stringWithFormat:#"y = %.2fx %.2f", graph.slope, graph.base]];}
else if (graph.base > 0) {[functionField setText:[NSString stringWithFormat:#"y = %.2fx + %.2f", graph.slope, graph.base]];}
else {[functionField setText:[NSString stringWithFormat:#"y = %.2fx", graph.slope]];}
[yintField setText:[NSString stringWithFormat:#"%.2f", graph.base]];
[self changeTable];
}
}
[graph setNeedsDisplay];
}
The numbers involving the positioning of the dots are measured in pixels. The only way I can really think of rotating the outside dots is by calculating a radius and making sure the dots are within the radius, but I'm wondering if there isn't something simpler than that. Does anyone know a simple way to move a UIImageView in a circle?
KTOneFingerRotationGestureRecognizer is a custom UIGestureRecognizer for doing one finger rotations in iOS apps. It tracks finger movement around a central point. I don't know if this is exactly what you want, but it may point you in the right direction.