Only increase height or width using pinch gesture [duplicate] - objective-c

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Using UIPinchGestureRecognizer to scale uiviews in single direction
My code is below:
UIPinchGestureRecognizer *croperViewGessture = [[UIPinchGestureRecognizer alloc]initWithTarget:self action:#selector(croperViewScale:)];
croperViewGessture.delegate=self;
[croperView addGestureRecognizer:croperViewGessture];
-(void)CanvasScale:(id)sender
{
if([(UIPinchGestureRecognizer *)sender state]==UIGestureRecognizerStateBegan)
{
if ([sender numberOfTouches] == 2) {
_pntOrig[0] = [(UIPinchGestureRecognizer *)sender locationOfTouch:0 inView:cropedAngle];
_pntOrig[1] = [(UIPinchGestureRecognizer *)sender locationOfTouch:1 inView:cropedAngle];
} else {
_pntOrig[0] = [(UIPinchGestureRecognizer *)sender locationInView:cropedAngle];
_pntOrig[1] = _pntOrig[0];
}
_lenOrigX = fabs(_pntOrig[1].x - _pntOrig[0].x);
_lenOrigY = fabs(_pntOrig[1].y - _pntOrig[0].y);
_xScale = 1.0;
_yScale = 1.0;
}
if ([(UIPinchGestureRecognizer *)sender state] == UIGestureRecognizerStateChanged) {
if ([sender numberOfTouches] == 2) {
CGPoint pntNew[2];
pntNew[0] = [(UIPinchGestureRecognizer *)sender locationOfTouch:0 inView:cropedAngle];
pntNew[1] = [(UIPinchGestureRecognizer *)sender locationOfTouch:1 inView:cropedAngle];
CGFloat lenX = fabs(pntNew[1].x - pntNew[0].x);
CGFloat lenY = fabs(pntNew[1].y - pntNew[0].y);
CGFloat dX = fabs(lenX - _lenOrigX);
CGFloat dY = fabs(lenY - _lenOrigY);
CGFloat tot = dX + dY;
CGFloat pX = dX / tot;
CGFloat pY = dY / tot;
CGFloat scale = [(UIPinchGestureRecognizer *)sender scale];
CGFloat dscale = scale - 1.0;
_xScale = dscale * pX + 1;
_yScale = dscale * pY + 1;
}
}
CGAffineTransform transform = cropedAngle.transform;
CGAffineTransform newTarnsform = CGAffineTransformScale(transform, _lenOrigX, _lenOrigY);
[cropedAngle setTransform:newTarnsform];
}
But problem is that when I do Zoomin OR Zoomout then view spread on all over the screen and after it disable Please view my code and tell me what is wrong .
Please help me in this issue
i am Thankfull in advance.

I wrote my own custom extension to UIPinchGestureRecognizer to provide an xScale and a yScale, in addition to the normal scale. This is a drop in replacement for UIPinchGestureRecognizer. You now have the option of looking at the normal scale or the new xScale and yScale. Use these values accordingly to scale your view based on how the user does the pinch gesture.
RMPinchGestureRecognizer.h
#import <Foundation/Foundation.h>
#import <UIKit/UIGestureRecognizerSubclass.h>
#interface RMPinchGestureRecognizer : UIPinchGestureRecognizer {
CGPoint _pntOrig[2];
CGFloat _lenOrigX;
CGFloat _lenOrigY;
CGFloat _xScale;
CGFloat _yScale;
}
#property (nonatomic, readonly) CGFloat xScale;
#property (nonatomic, readonly) CGFloat yScale;
#end
RMPinchGestureRecognizer.m
#import "RMPinchGestureRecognizer.h"
#implementation RMPinchGestureRecognizer
#synthesize xScale = _xScale;
#synthesize yScale = _yScale;
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesMoved:touches withEvent:event];
if (self.state == UIGestureRecognizerStateChanged) {
if ([self numberOfTouches] == 2) {
CGPoint pntNew[2];
pntNew[0] = [self locationOfTouch:0 inView:self.view];
pntNew[1] = [self locationOfTouch:1 inView:self.view];
CGFloat lenX = fabs(pntNew[1].x - pntNew[0].x);
CGFloat lenY = fabs(pntNew[1].y - pntNew[0].y);
CGFloat dX = fabs(lenX - _lenOrigX);
CGFloat dY = fabs(lenY - _lenOrigY);
CGFloat tot = dX + dY;
CGFloat pX = dX / tot;
CGFloat pY = dY / tot;
CGFloat scale = [self scale];
CGFloat dscale = scale - 1.0;
_xScale = dscale * pX + 1;
_yScale = dscale * pY + 1;
}
}
}
- (void)setState:(UIGestureRecognizerState)state {
if (state == UIGestureRecognizerStateBegan) {
if ([self numberOfTouches] == 2) {
_pntOrig[0] = [self locationOfTouch:0 inView:self.view];
_pntOrig[1] = [self locationOfTouch:1 inView:self.view];
} else {
_pntOrig[0] = [self locationInView:self.view];
_pntOrig[1] = _pntOrig[0];
}
_lenOrigX = fabs(_pntOrig[1].x - _pntOrig[0].x);
_lenOrigY = fabs(_pntOrig[1].y - _pntOrig[0].y);
_xScale = 1.0;
_yScale = 1.0;
}
[super setState:state];
}
#end

Take a look HERE
Your line CGAffineTransformScale(transform, scaleTemp,scaleTemp); uses the same scaleTemp variable to modify both the x and y values of the transform.

Related

Custom Slider In SpriteKit

I'm trying to create a horizontal slider like the one shown below in SpriteKit (in Objective C for Mac OS).
I'm certainly doing something wrong because the "knob" of the slider never moves left, it only moves right and I'm not sure what the issue is. I'm using the mouseDragged: method to handle everything. Here's the code below:
Slider.m
#import "Slider.h"
#interface Slider()
#property CGSize dimensions;
#property SKSpriteNode *background, *foreground, *knob;
#end
#implementation Slider
-(instancetype) initWithDimensions:(CGSize)dimensions Percentage:(double)percentage {
if (self = [super init]) {
_dimensions = dimensions;
_percentage = percentage;
[self initBackgroundSprite];
[self initForegroundSprite];
[self initKnob];
self.userInteractionEnabled = YES;
}
return self;
}
-(void) initBackgroundSprite {
_background = [SKSpriteNode spriteNodeWithImageNamed:#"sliderBG"];
_background.centerRect = CGRectMake(6.0/13.0, 5.0/11.0, 1.0/13.0, 1.0/11.0);
[_background setAnchorPoint:CGPointMake(0, 0.5)];
double xScale = _dimensions.width / _background.frame.size.width;
double yScale = _dimensions.height / _background.frame.size.height;
[_background setXScale:xScale];
[_background setYScale:yScale];
[self addChild:_background];
}
-(void) initForegroundSprite {
_foreground = [SKSpriteNode spriteNodeWithImageNamed:#"sliderFG"];
_foreground.centerRect = CGRectMake(6.0/13.0, 5.0/11.0, 1.0/13.0, 1.0/11.0);
[_foreground setAnchorPoint:CGPointMake(0, 0.5)];
double xScale = _dimensions.width*_percentage / _foreground.frame.size.width;
double yScale = _dimensions.height / _foreground.frame.size.height;
[_foreground setXScale:xScale];
[_foreground setYScale:yScale];
[self addChild:_foreground];
}
-(void) initKnob {
_knob = [SKSpriteNode spriteNodeWithImageNamed:#"sliderKnob"];
_knob.centerRect = CGRectMake(6.0/13.0, 5.0/11.0, 1.0/13.0, 1.0/11.0);
[_knob setAnchorPoint:CGPointMake(0, 0.5)];
double scaleFactor = 2 / (_knob.frame.size.height / _background.frame.size.height);
NSLog(#"%f, %f", _knob.frame.size.height, _background.frame.size.height);
[_knob setScale:scaleFactor];
[_knob setZPosition:2];
[_knob setName:#"knob"];
[self addChild:_knob];
}
-(void) mouseDragged:(NSEvent *)event {
CGPoint location = [event locationInNode:self];
NSArray *nodes = [self nodesAtPoint:location];
for (SKNode *node in nodes) {
if ([node isKindOfClass:[SKSpriteNode class]]) {
SKSpriteNode *sprite = (SKSpriteNode*)node;
if ([sprite.name isEqualToString:#"knob"]) {
[self updateKnobPositionWithLocation:location];
}
}
}
}
-(void) updateKnobPositionWithLocation:(CGPoint)location {
double x = location.x;
double y = _knob.position.y; //don't want the y-pos to change
double bgX = _background.position.x; //x pos of slider
double width = _background.frame.size.width; //width of slider
if (x > bgX + width)//if knob goes beyond width of slider, restrict to width
x = bgX + width;
else if (x < bgX)
x = bgX;
[_knob setPosition:CGPointMake(x, y)];
}
Here's a video to illustrate the behaviour:
https://drive.google.com/open?id=0B8Zfr1yQCdf-OG5kZFRWNFgxd1k
I've solved all of the issues with the slider class. Thanks for the help everyone. In case anyone is interested, here's the full code:
Slider.h
#import <SpriteKit/SpriteKit.h>
#interface Slider : SKSpriteNode
#property double percentage;
-(instancetype) initWithDimensions:(CGSize)dimensions Percentage:(double)percentage;
#end
Slider.m
#import "Slider.h"
#interface Slider()
#property CGSize dimensions;
#property SKSpriteNode *background, *foreground, *knob;
#property double backgroundWidth, foregroundWidth;
#property bool isBeingUsed;
#property SKLabelNode *percentageLabel;
#end
#implementation Slider
//creates a slider with certain dimensions and at a specific starting percentage clamped [0, 1]
-(instancetype) initWithDimensions:(CGSize)dimensions Percentage:(double)percentage {
if (self = [super init]) {
_dimensions = dimensions;
_percentage = percentage;
_isBeingUsed = NO;
[self initBackgroundSprite];
[self initForegroundSprite];
[self initKnob];
[self initPercentageLabel];
self.userInteractionEnabled = YES;
}
return self;
}
//sprite initialization
-(void) initBackgroundSprite {
_background = [SKSpriteNode spriteNodeWithImageNamed:#"sliderBG"];
_background.centerRect = CGRectMake(6.0/13.0, 5.0/11.0, 1.0/13.0, 1.0/11.0);
_backgroundWidth = _background.frame.size.width;
[_background setAnchorPoint:CGPointMake(0, 0.5)];
double xScale = _dimensions.width / _backgroundWidth;
double yScale = _dimensions.height / _background.frame.size.height;
[_background setXScale:xScale];
[_background setYScale:yScale];
[self addChild:_background];
}
-(void) initForegroundSprite {
_foreground = [SKSpriteNode spriteNodeWithImageNamed:#"sliderFG"];
_foreground.centerRect = CGRectMake(6.0/13.0, 5.0/11.0, 1.0/13.0, 1.0/11.0);
[_foreground setAnchorPoint:CGPointMake(0, 0.5)];
_foregroundWidth = _foreground.frame.size.width;
double xScale = _dimensions.width*_percentage / _foregroundWidth;
double yScale = _dimensions.height / _foreground.frame.size.height;
[_foreground setXScale:xScale];
[_foreground setYScale:yScale];
[self addChild:_foreground];
}
-(void) initKnob {
_knob = [SKSpriteNode spriteNodeWithImageNamed:#"sliderKnob"];
_knob.centerRect = CGRectMake(6.0/13.0, 5.0/11.0, 1.0/13.0, 1.0/11.0);
[_knob setAnchorPoint:CGPointMake(0.5, 0.5)];
double scaleFactor = 2 / (_knob.frame.size.height / _background.frame.size.height);
[_knob setScale:scaleFactor];
[_knob setPosition:CGPointMake(_foreground.frame.size.width, -_knob.frame.size.height*0.05)];
[_knob setZPosition:2];
[_knob setName:#"knob"];
[self addChild:_knob];
}
-(void) initPercentageLabel {
_percentageLabel = [SKLabelNode labelNodeWithFontNamed:#"Hiragino Kaku Gothic Std"];
[_percentageLabel setText:[NSString stringWithFormat:#"%.0f%%", _percentage*100]];
[_percentageLabel setFontSize:15];
double x = _dimensions.width * 1.05;
double y = _knob.frame.size.height*0.05;
[_percentageLabel setPosition:CGPointMake(x, y)];
[_percentageLabel setHorizontalAlignmentMode:SKLabelHorizontalAlignmentModeLeft];
[_percentageLabel setVerticalAlignmentMode:SKLabelVerticalAlignmentModeCenter];
[self addChild:_percentageLabel];
}
-(void) mouseDragged:(NSEvent *)event {
CGPoint location = [event locationInNode:self];
_isBeingUsed = YES;
[self updateKnobPositionWithLocation:location];
}
-(void) mouseDown:(NSEvent *)event {
[super mouseDown:event];
CGPoint location = [event locationInNode:self];
_isBeingUsed = YES;
[self updateKnobPositionWithLocation:location];
}
-(void) mouseUp:(NSEvent *)event {
[super mouseUp:event];
_isBeingUsed = NO;
}
-(void) updateKnobPositionWithLocation:(CGPoint)location {
if (_isBeingUsed) {
double x = location.x;
double y = _knob.position.y; //don't want the y-pos to change
double bgX = _background.position.x; //x pos of slider
double width = _background.frame.size.width; //width of slider
if (x > bgX + width)//if knob goes beyond width of slider, restrict to width
x = bgX + width;
else if (x < bgX)
x = bgX;
[_knob setPosition:CGPointMake(x, y)];
[self updateForegroundPercentage];
//NSLog(#"(%f, %f)", _knob.position.x, _knob.position.y);
}
}
-(void) updateForegroundPercentage {
_percentage = _knob.position.x / _background.frame.size.width;
double xScale = _dimensions.width*_percentage / _foregroundWidth;
[_foreground setXScale:xScale];
[_percentageLabel setText:[NSString stringWithFormat:#"%.0f%%", _percentage*100]];
}
#end

SKPhysicsJoints are all fairly wobbly and elastic

So, I have an object composed of several physics bodies with 10 or so joints holding it together (pin and fixed). It works well but I am surprised at how wobbly the joints are. I'd imagine "fixed" to be just as fixed as if I merged them into one physics body, but it's substantially wobbly to the point that it is unusable. Also with pins, the points on each physics body seem to have quite some play apart from each other when force is applied to connecting objects. This is the case all the time, and both bodies involved are not restricted in motion in any way.
I would love to have the joints more rigid and less elastic. Has anyone been able to find a solution or reason for this? adding several more joints does not seem to make it any more rigid.
I faced a similar elasticity bug with a rope simulation and could finally come up with a workaround.
Here's my rope interface:
#import <SpriteKit/SpriteKit.h>
#interface ALRope : NSObject
#property(nonatomic, readonly) NSArray *ropeRings;
#property(nonatomic) int ringCount;
#property(nonatomic) CGFloat ringScale;
#property(nonatomic) CGFloat ringsDistance;
#property(nonatomic) CGFloat jointsFrictionTorque;
#property(nonatomic) CGFloat ringsZPosition;
#property(nonatomic) CGPoint startRingPosition;
#property(nonatomic) CGFloat ringFriction;
#property(nonatomic) CGFloat ringRestitution;
#property(nonatomic) CGFloat ringMass;
#property(nonatomic) BOOL shouldEnableJointsAngleLimits;
#property(nonatomic) CGFloat jointsLowerAngleLimit;
#property(nonatomic) CGFloat jointsUpperAngleLimit;
-(instancetype)initWithRingTexture:(SKTexture *)ringTexture;
-(void)buildRopeWithScene:(SKScene *)scene;
-(void)adjustRingPositions;
-(SKSpriteNode *)startRing;
-(SKSpriteNode *)lastRing;
#end
Rope Implementation:
#import "ALRope.h"
#implementation ALRope
{
SKTexture *_ringTexture;
NSMutableArray *_ropeRings;
}
static CGFloat const RINGS_DISTANCE_DEFAULT = 0;
static CGFloat const JOINTS_FRICTION_TORQUE_DEFAULT = 0;
static CGFloat const RING_SCALE_DEFAULT = 1;
static int const RING_COUNT_DEFAULT = 30;
static CGFloat const RINGS_Z_POSITION_DEFAULT = 1;
static BOOL const SHOULD_ENABLE_JOINTS_ANGLE_LIMITS_DEFAULT = NO;
static CGFloat const JOINT_LOWER_ANGLE_LIMIT_DEFAULT = -M_PI / 3;
static CGFloat const JOINT_UPPER_ANGLE_LIMIT_DEFAULT = M_PI / 3;
static CGFloat const RING_FRICTION_DEFAULT = 0;
static CGFloat const RING_RESTITUTION_DEFAULT = 0;
static CGFloat const RING_MASS_DEFAULT = -1;
-(instancetype)initWithRingTexture:(SKTexture *)ringTexture
{
if(self = [super init]) {
_ringTexture = ringTexture;
//apply defaults
_startRingPosition = CGPointMake(0, 0);
_ringsDistance = RINGS_DISTANCE_DEFAULT;
_jointsFrictionTorque = JOINTS_FRICTION_TORQUE_DEFAULT;
_ringScale = RING_SCALE_DEFAULT;
_ringCount = RING_COUNT_DEFAULT;
_ringsZPosition = RINGS_Z_POSITION_DEFAULT;
_shouldEnableJointsAngleLimits = SHOULD_ENABLE_JOINTS_ANGLE_LIMITS_DEFAULT;
_jointsLowerAngleLimit = JOINT_LOWER_ANGLE_LIMIT_DEFAULT;
_jointsUpperAngleLimit = JOINT_UPPER_ANGLE_LIMIT_DEFAULT;
_ringFriction = RING_FRICTION_DEFAULT;
_ringRestitution = RING_RESTITUTION_DEFAULT;
_ringMass = RING_MASS_DEFAULT;
}
return self;
}
-(void)buildRopeWithScene:(SKScene *)scene
{
_ropeRings = [NSMutableArray new];
SKSpriteNode *firstRing = [self addRopeRingWithPosition:_startRingPosition underScene:scene];
SKSpriteNode *lastRing = firstRing;
CGPoint position;
for (int i = 1; i < _ringCount; i++) {
position = CGPointMake(lastRing.position.x, lastRing.position.y - lastRing.size.height - _ringsDistance);
lastRing = [self addRopeRingWithPosition:position underScene:scene];
}
[self addJointsWithScene:scene];
}
-(SKSpriteNode *)addRopeRingWithPosition:(CGPoint)position underScene:(SKScene *)scene
{
SKSpriteNode *ring = [SKSpriteNode spriteNodeWithTexture:_ringTexture];
ring.xScale = ring.yScale = _ringScale;
ring.position = position;
ring.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:ring.size.height / 2];
ring.physicsBody.allowsRotation = YES;
ring.physicsBody.friction = _ringFriction;
ring.physicsBody.restitution = _ringRestitution;
if(_ringMass > 0) {
ring.physicsBody.mass = _ringMass;
}
[scene addChild:ring];
[_ropeRings addObject:ring];
return ring;
}
-(void)addJointsWithScene:(SKScene *)scene
{
for (int i = 1; i < _ropeRings.count; i++) {
SKSpriteNode *nodeA = [_ropeRings objectAtIndex:i-1];
SKSpriteNode *nodeB = [_ropeRings objectAtIndex:i];
SKPhysicsJointPin *joint = [SKPhysicsJointPin jointWithBodyA:nodeA.physicsBody
bodyB:nodeB.physicsBody
anchor:CGPointMake(nodeA.position.x,
nodeA.position.y - (nodeA.size.height + _ringsDistance) / 2)];
joint.frictionTorque = _jointsFrictionTorque;
joint.shouldEnableLimits = _shouldEnableJointsAngleLimits;
if(_shouldEnableJointsAngleLimits) {
joint.lowerAngleLimit = _jointsLowerAngleLimit;
joint.upperAngleLimit = _jointsUpperAngleLimit;
}
[scene.physicsWorld addJoint:joint];
}
}
//workaround for elastic effect should be called from didSimulatePhysics
-(void)adjustRingPositions
{
//based on zRotations of all rings and the position of start ring adjust the rest of the rings positions starting from top to bottom
for (int i = 1; i < _ropeRings.count; i++) {
SKSpriteNode *nodeA = [_ropeRings objectAtIndex:i-1];
SKSpriteNode *nodeB = [_ropeRings objectAtIndex:i];
CGFloat thetaA = nodeA.zRotation - M_PI / 2,
thetaB = nodeB.zRotation + M_PI / 2,
jointRadius = (_ringsDistance + nodeA.size.height) / 2,
xJoint = jointRadius * cosf(thetaA) + nodeA.position.x,
yJoint = jointRadius * sinf(thetaA) + nodeA.position.y,
theta = thetaB - M_PI,
xB = jointRadius * cosf(theta) + xJoint,
yB = jointRadius * sinf(theta) + yJoint;
nodeB.position = CGPointMake(xB, yB);
}
}
-(SKSpriteNode *)startRing
{
return _ropeRings[0];
}
-(SKSpriteNode *)lastRing
{
return [_ropeRings lastObject];
}
#end
Scene code to showcase how to use the Rope:
#import "ALRopeDemoScene.h"
#import "ALRope.h"
#implementation ALRopeDemoScene
{
__weak SKSpriteNode *_branch;
CGPoint _touchLastPosition;
BOOL _branchIsMoving;
ALRope *_rope;
}
-(id)initWithSize:(CGSize)size {
if (self = [super initWithSize:size]) {
/* Setup your scene here */
self.backgroundColor = [SKColor colorWithRed:0.2 green:0.5 blue:0.6 alpha:1.0];
[self buildScene];
}
return self;
}
-(void) buildScene {
SKSpriteNode *branch = [SKSpriteNode spriteNodeWithImageNamed:#"Branch"];
_branch = branch;
branch.position = CGPointMake(CGRectGetMaxX(self.frame) - branch.size.width / 2,
CGRectGetMidY(self.frame) + 200);
branch.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(2, 10)];
branch.physicsBody.dynamic = NO;
[self addChild:branch];
_rope = [[ALRope alloc] initWithRingTexture:[SKTexture textureWithImageNamed:#"rope_ring"]];
//configure rope params if needed
// _rope.ringCount = ...;//default is 30
// _rope.ringScale = ...;//default is 1
// _rope.ringsDistance = ...;//default is 0
// _rope.jointsFrictionTorque = ...;//default is 0
// _rope.ringsZPosition = ...;//default is 1
// _rope.ringFriction = ...;//default is 0
// _rope.ringRestitution = ...;//default is 0
// _rope.ringMass = ...;//ignored unless mass > 0; default -1
// _rope.shouldEnableJointsAngleLimits = ...;//default is NO
// _rope.jointsLowerAngleLimit = ...;//default is -M_PI/3
// _rope.jointsUpperAngleLimit = ...;//default is M_PI/3
_rope.startRingPosition = CGPointMake(branch.position.x - 100, branch.position.y);//default is (0, 0)
[_rope buildRopeWithScene:self];
//attach rope to branch
SKSpriteNode *startRing = [_rope startRing];
CGPoint jointAnchor = CGPointMake(startRing.position.x, startRing.position.y + startRing.size.height / 2);
SKPhysicsJointPin *joint = [SKPhysicsJointPin jointWithBodyA:branch.physicsBody bodyB:startRing.physicsBody anchor:jointAnchor];
[self.physicsWorld addJoint:joint];
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInNode:self];
if(CGRectContainsPoint(_branch.frame, location)) {
_branchIsMoving = YES;
_touchLastPosition = location;
}
}
-(void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
_branchIsMoving = NO;
}
-(void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
if(_branchIsMoving) {
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInNode:self],
branchCurrentPosition = _branch.position;
CGFloat dx = location.x - _touchLastPosition.x,
dy = location.y - _touchLastPosition.y;
_branch.position = CGPointMake(branchCurrentPosition.x + dx, branchCurrentPosition.y + dy);
_touchLastPosition = location;
}
}
-(void)didSimulatePhysics
{
//workaround for elastic effect
[_rope adjustRingPositions];
}
#end
Notice the [rope adjustRingPositions] call from [scene didSimulatePhysics]. That was the actual workaround for the elastic bug.
Complete demo code is here. I hope this helps!

SKSpriteNode rotation not working properly

I'm trying to rotate a SKSpriteNode by using UIRrotationGestureRecognizer. I've implemented a code but sometimes, when I rotate the node it jumps to a rotation that isn't the one it should be. Here you have the code:
- (void) handleRotation:(UIRotationGestureRecognizer *) rotationrecognizer{
CGFloat initialrotation = 0.0;
if (rotationrecognizer.state == UIGestureRecognizerStateBegan) {
CGPoint touchLocation = [rotationrecognizer locationInView:rotationrecognizer.view];
touchLocation = [self convertPointFromView:touchLocation];
[self selectNodeForTouch:touchLocation];
initialrotation = selected.zRotation;
}
else if (rotationrecognizer.state == UIGestureRecognizerStateChanged) {
CGFloat angle = initialrotation + rotationrecognizer.rotation;
selected.zRotation = angle;
}
}
You are resetting initial rotation to 0 on every call... you should move this to be an ivar of your view if you need it to stay resident.
The way you have it written now, the line that sets 'angle' is effectively equal to this:
CGFloat angle = 0 + rotationrecognizer.rotation;
Instead, you should do the following (where initialRotation is defined as a private ivar):
- (void) handleRotation:(UIRotationGestureRecognizer *) rotationrecognizer{
if (rotationrecognizer.state == UIGestureRecognizerStateBegan) {
CGPoint touchLocation = [rotationrecognizer locationInView:rotationrecognizer.view];
touchLocation = [self convertPointFromView:touchLocation];
[self selectNodeForTouch:touchLocation];
_initialrotation = selected.zRotation;
}
else if (rotationrecognizer.state == UIGestureRecognizerStateChanged) {
CGFloat angle = _initialrotation + rotationrecognizer.rotation;
selected.zRotation = angle;
}
}

Use value from UISlider to change another variable?

So I'm still learning this Objective C lark and i've hit a stump. All I want to do is use the value from Sliderchanged to populate the 'CGFloat lineWidth =', Hopefully to have the end result of changing the width of the crumbPath line...
******************EDIT*********************
CrumbPathView.h
#import "CrumbPathView.h"
#import "CrumbPath.h"
#import "FirstViewController.h"
#interface CrumbPathView (FileInternal)
- (CGPathRef)newPathForPoints:(MKMapPoint *)points
pointCount:(NSUInteger)pointCount
clipRect:(MKMapRect)mapRect
zoomScale:(MKZoomScale)zoomScale;
#end
#implementation CrumbPathView
-(IBAction)sliderChanged:(UISlider *)sender
{
NSLog(#"slider value = %f", sender.value);
}
- (void)drawMapRect:(MKMapRect)mapRect
zoomScale:(MKZoomScale)zoomScale
inContext:(CGContextRef)context
{
CrumbPath *crumbs = (CrumbPath *)(self.overlay);
CGFloat lineWidth = 30;
// outset the map rect by the line width so that points just outside
// of the currently drawn rect are included in the generated path.
MKMapRect clipRect = MKMapRectInset(mapRect, -lineWidth, -lineWidth);
[crumbs lockForReading];
CGPathRef path = [self newPathForPoints:crumbs.points
pointCount:crumbs.pointCount
clipRect:clipRect
zoomScale:zoomScale];
[crumbs unlockForReading];
if (path != nil)
{
CGContextAddPath(context, path);
CGContextSetRGBStrokeColor(context, 0.0f, 0.0f, 1.0f, 0.5f);
CGContextSetLineJoin(context, kCGLineJoinRound);
CGContextSetLineCap(context, kCGLineCapRound);
CGContextSetLineWidth(context, lineWidth);
CGContextStrokePath(context);
CGPathRelease(path);
}
}
#end
#implementation CrumbPathView (FileInternal)
static BOOL lineIntersectsRect(MKMapPoint p0, MKMapPoint p1, MKMapRect r)
{
double minX = MIN(p0.x, p1.x);
double minY = MIN(p0.y, p1.y);
double maxX = MAX(p0.x, p1.x);
double maxY = MAX(p0.y, p1.y);
MKMapRect r2 = MKMapRectMake(minX, minY, maxX - minX, maxY - minY);
return MKMapRectIntersectsRect(r, r2);
}
#define MIN_POINT_DELTA 5.0
- (CGPathRef)newPathForPoints:(MKMapPoint *)points
pointCount:(NSUInteger)pointCount
clipRect:(MKMapRect)mapRect
zoomScale:(MKZoomScale)zoomScale
{
// The fastest way to draw a path in an MKOverlayView is to simplify the
// geometry for the screen by eliding points that are too close together
// and to omit any line segments that do not intersect the clipping rect.
// While it is possible to just add all the points and let CoreGraphics
// handle clipping and flatness, it is much faster to do it yourself:
//
if (pointCount < 2)
return NULL;
CGMutablePathRef path = NULL;
BOOL needsMove = YES;
#define POW2(a) ((a) * (a))
// Calculate the minimum distance between any two points by figuring out
// how many map points correspond to MIN_POINT_DELTA of screen points
// at the current zoomScale.
double minPointDelta = MIN_POINT_DELTA / zoomScale;
double c2 = POW2(minPointDelta);
MKMapPoint point, lastPoint = points[0];
NSUInteger i;
for (i = 1; i < pointCount - 1; i++)
{
point = points[i];
double a2b2 = POW2(point.x - lastPoint.x) + POW2(point.y - lastPoint.y);
if (a2b2 >= c2) {
if (lineIntersectsRect(point, lastPoint, mapRect))
{
if (!path)
path = CGPathCreateMutable();
if (needsMove)
{
CGPoint lastCGPoint = [self pointForMapPoint:lastPoint];
CGPathMoveToPoint(path, NULL, lastCGPoint.x, lastCGPoint.y);
}
CGPoint cgPoint = [self pointForMapPoint:point];
CGPathAddLineToPoint(path, NULL, cgPoint.x, cgPoint.y);
}
else
{
// discontinuity, lift the pen
needsMove = YES;
}
lastPoint = point;
}
}
#undef POW2
// If the last line segment intersects the mapRect at all, add it unconditionally
point = points[pointCount - 1];
if (lineIntersectsRect(lastPoint, point, mapRect))
{
if (!path)
path = CGPathCreateMutable();
if (needsMove)
{
CGPoint lastCGPoint = [self pointForMapPoint:lastPoint];
CGPathMoveToPoint(path, NULL, lastCGPoint.x, lastCGPoint.y);
}
CGPoint cgPoint = [self pointForMapPoint:point];
CGPathAddLineToPoint(path, NULL, cgPoint.x, cgPoint.y);
}
return path;
}
#end
CrumbPathView.h
#import <MapKit/MapKit.h>
#import <UIKit/UIKit.h>
#interface CrumbPathView : MKOverlayView
{
}
- (IBAction)sliderChanged:(id)sender;
#end
I've added the rest of the code from the .h & .m ...
I think you are mostly there. Your slider is probably stored in a propery...just check it like you would any other property when you need its value.
Example:
-(IBAction)sliderChanged:(UISlider *)sender
{
//[self drawMapRect...]
}
-(void)drawMapRect:(MKMapRect)mapRect zoomScale:(MKZoomScale)zoomScale inContext:CGContextRef)context
{
CrumbPath *crumbs = (CrumbPath *)(self.overlay);
CGFloat lineWidth = self.mySliderControl.value;
//Do something with lineWidth
}
Did you tried this ?
-(IBAction)sliderChanged:(UISlider *)sender
{
NSLog(#"slider value = %f", sender.value);
CrumbPath *crumbs = (CrumbPath *)(self.overlay);
CGFloat lineWidth = (UISlider*)sender.value;
//Set value on crumbpath using slider value
}
- (void)drawMapRect:(MKMapRect)mapRect
zoomScale:(MKZoomScale)zoomScale
inContext:(CGContextRef)context
{
CrumbPath *crumbs = (CrumbPath *)(self.overlay);
}

iPhone trim audio recording

I have a voice memo component in my app, and I want to allow the user to trim the audio, similar to QuickTime X on Mac OS Ten point Six handles it, or like the Voice Memos app on the iPhone. Here's an example of both:
Any help is appreciated.
I am not a UI programmer by any means. This was a test I wrote to see how to write custom controls. This code may or may not work. I have not touched it in some time.
header
#interface SUIMaxSlider : UIControl {
#private
float_t minimumValue;
float_t maximumValue;
float_t value;
CGPoint trackPoint;
}
#property (nonatomic, assign) float_t minimumValue, maximumValue;
#property (nonatomic, assign) float_t value;
#end
implementation
#import "SUIMaxSlider.h"
#import <CoreGraphics/CoreGraphics.h>
#import <QuartzCore/QuartzCore.h>
//#import "Common.h"
#define kSliderPadding 5
#implementation SUIMaxSlider
#synthesize minimumValue, maximumValue;
#pragma mark -
#pragma mark Interface Initialization
- (id) initWithCoder:(NSCoder *)aDecoder {
if (self = [super initWithCoder:aDecoder]) {
trackPoint.x = self.bounds.size.width;
self.backgroundColor = [UIColor colorWithRed:135.0/255.0 green:173.0/255.0 blue:255.0/255.0 alpha:0.0];
}
return self;
}
- (id) initWithFrame: (CGRect) aFrame {
if (self = [super initWithFrame:aFrame]) {
self.frame = aFrame;
self.bounds = aFrame;
self.center = CGPointMake(CGRectGetMidX(aFrame), CGRectGetMidY(aFrame));
trackPoint.x = aFrame.size.width;
}
return self;
}
- (id) init {
return [self initWithFrame:CGRectZero];
}
#pragma mark -
#pragma mark Properties.
#pragma mark -
- (float_t) value {
return value;
}
- (void) setValue:(float_t) v {
value = fmin(v, maximumValue);
value = fmax(value, minimumValue);
float_t delta = maximumValue - minimumValue;
float_t scalar = ((self.bounds.size.width - 2 * kSliderPadding) / delta) ;
float_t x = (value - minimumValue) * scalar;
x += 5.0;
trackPoint.x = x;
[self setNeedsDisplay];
}
#pragma mark -
#pragma mark Interface Drawing
#pragma mark -
- (void) drawRect:(CGRect) rect {
CGContextRef context = UIGraphicsGetCurrentContext();
CGColorRef lightBlue = [UIColor colorWithRed:135.0/255.0 green:173.0/255.0 blue:255.0/255.0 alpha:1.0].CGColor;
CGColorRef lightBlueAlpha = [UIColor colorWithRed:135.0/255.0 green:173.0/255.0 blue:255.0/255.0 alpha:0.7].CGColor;
CGColorRef lightGrayColor = [UIColor colorWithRed:130.0/255.0 green:130.0/255.0 blue:130.0/255.0 alpha:1.0].CGColor;
CGColorRef darkGrayColor = [UIColor colorWithRed:70.0/255.0 green:70.0/255.0 blue:70.0/255.0 alpha:1.0].CGColor;
CGColorRef redColor = [UIColor colorWithRed:1.0 green:1.0 blue:1.0 alpha:0.6].CGColor;
CGRect boundsRect = self.bounds;
CGRect sliderRect = CGRectMake(0, 0, boundsRect.size.width, boundsRect.size.height / 2);
/*
CGContextSetFillColorWithColor(context, lightGrayColor);
CGContextFillRect(context, sliderRect);
halfHeight.origin.y = sliderRect.size.height;
CGContextSetFillColorWithColor(context, darkGrayColor);
CGContextFillRect(context, sliderRect);
*/
CGFloat tx = fmin(sliderRect.size.width - kSliderPadding, trackPoint.x);
tx = fmax(kSliderPadding, tx);
sliderRect.origin.y = boundsRect.origin.y;
sliderRect.size.width = tx ;
CGContextSetFillColorWithColor(context, lightBlueAlpha);
CGContextFillRect(context, sliderRect);
sliderRect.origin.y = sliderRect.size.height;
CGContextSetFillColorWithColor(context, lightBlue);
CGContextFillRect(context, sliderRect);
CGFloat mid = boundsRect.size.height / 2 ;
CGPoint a = CGPointMake(tx - kSliderPadding, mid);
CGPoint b = CGPointMake(tx, mid - kSliderPadding);
CGPoint c = CGPointMake(tx + kSliderPadding, mid);
CGPoint d = CGPointMake(tx, mid + kSliderPadding);
CGContextSetRGBFillColor(context, 1.0, 1.0, 1.0, 1.0);
CGContextSetLineWidth(context, 2.0);
CGContextBeginPath(context);
CGContextMoveToPoint(context, a.x, a.y);
CGContextAddLineToPoint(context, b.x, b.y);
CGContextAddLineToPoint(context, c.x, c.y);
CGContextAddLineToPoint(context, d.x, d.y);
CGContextAddLineToPoint(context, a.x, a.y);
CGContextFillPath(context);
}
#pragma mark -
#pragma mark Touch Tracking
#pragma mark -
- (void) trackTouch:(UITouch *) touch {
CGPoint p = [touch locationInView:self];
//bound track point
trackPoint.x = fmax(p.x, kSliderPadding) ;
trackPoint.x = fmin(trackPoint.x, self.bounds.size.width - kSliderPadding);
[self setNeedsDisplay];
float_t x = trackPoint.x - kSliderPadding;
float_t delta = maximumValue - minimumValue;
float_t scalar = (x / (self.bounds.size.width - 2 * kSliderPadding)) ;
value = minimumValue + (delta * scalar);
[self sendActionsForControlEvents:UIControlEventValueChanged];
}
- (void)endTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event {
[self trackTouch:touch];
}
- (BOOL)beginTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event {
CGPoint p = [touch locationInView:self];
trackPoint.x = fmax(p.x, kSliderPadding) ;
trackPoint.x = fmin(trackPoint.x, self.bounds.size.width - kSliderPadding);
[self setNeedsDisplay];
return YES;
}
- (BOOL)continueTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event {
[self trackTouch:touch];
return YES;
}
#end