Use value from UISlider to change another variable? - objective-c

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);
}

Related

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!

objective-c create and stroke an arc with CG

I was following a Ray Wenderlich tutorial on creating arcs:
http://www.raywenderlich.com/33193/core-graphics-tutorial-arcs-and-paths
-what I am looking to do is begin at point a (a fixed point) and locate where the user touches the screen, if it is + point A, then I want to draw an arc to that point. Though I don't return any errors I also don't get the path to be stroked. Could someone review the code below and see what I am doing wrong?
#implementation KIP_Arc
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
}
return self;
}
- (void) setArc {
//set the frame
float frameX = _startPoint.x;
float frameY = _startPoint.y;
float frameW = _endPoint.x;
float frameH = 50.0;
[self setFrame:CGRectMake(frameX, frameY, frameW, frameH)];
self.backgroundColor = [UIColor clearColor];
}
- (BOOL)isFlipped {
return YES;
}
- (void)drawRect:(CGRect)rect {
[[UIColor blackColor] set];
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSaveGState(context);
CGRect arcRect = self.frame;
CGMutablePathRef arcPath = [self createArcPathFromBottomOfRect:arcRect:25.0];
CGContextAddPath(context, arcPath);
CGContextClip(context);
CGContextFillPath(context);
CGContextRestoreGState(context);
CFRelease(arcPath);
}
- (CGMutablePathRef) createArcPathFromBottomOfRect : (CGRect) rect : (CGFloat) arcHeight {
CGRect arcRect = CGRectMake(rect.origin.x, rect.origin.y + rect.size.height - arcHeight, rect.size.width, arcHeight);
CGFloat arcRadius = (arcRect.size.height/2) + (pow(arcRect.size.width, 2) / (8*arcRect.size.height));
CGPoint arcCenter = CGPointMake(arcRect.origin.x + arcRect.size.width/2, arcRect.origin.y + arcRadius);
CGFloat angle = acos(arcRect.size.width / (2*arcRadius));
CGFloat startAngle = radians(180) + angle;
CGFloat endAngle = radians(360) - angle;
CGMutablePathRef path = CGPathCreateMutable();
CGPathAddArc(path, NULL, arcCenter.x, arcCenter.y, arcRadius, startAngle, endAngle, 0);
CGPathAddLineToPoint(path, NULL, CGRectGetMaxX(rect), CGRectGetMinY(rect));
CGPathAddLineToPoint(path, NULL, CGRectGetMinX(rect), CGRectGetMinY(rect));
CGPathAddLineToPoint(path, NULL, CGRectGetMinX(rect), CGRectGetMaxY(rect));
return path;
}
static inline double radians (double degrees){
return degrees * M_PI/180;
}
CGContextClip, as a side effect, empties the current path. When you call CGContextFillPath immediately afterward, the current path is empty, so you fill nothing.
As its name implies, CGContextFillPath restricts itself to the context's current path. (If it didn't, its name would just be CGContextFill.) So, you don't need to clip.
Drop the CGContextClip call and just use CGContextFillPath.

UITextview: how to indent the text?

Ok i'm trying to create a ruled notebook view.
The code to create this is below
#interface QuickNoteNoteTextView ()
#property (nonatomic) CGPoint startPoint;
#property (nonatomic) CGPoint endPoint;
#property (nonatomic, retain) UIColor *lineColor;
#end
#implementation QuickNoteNoteTextView
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
}
return self;
}
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect {
// Drawing code
// Get the graphics context
CGContextRef ctx = UIGraphicsGetCurrentContext();
[super drawRect:rect];
// Get the height of a single text line
NSString *alpha = #"ABCD";
CGSize textSize = [alpha sizeWithFont:self.font constrainedToSize:self.contentSize lineBreakMode:NSLineBreakByWordWrapping ];
NSUInteger height = textSize.height;
// Get the height of the view or contents of the view whichever is bigger
textSize = [self.text sizeWithFont:self.font constrainedToSize:self.contentSize lineBreakMode:NSLineBreakByWordWrapping ];
NSUInteger contentHeight = (rect.size.height > textSize.height) ? (NSUInteger)rect.size.height : textSize.height;
NSUInteger offset = 6 + height; // MAGIC Number 6 to offset from 0 to get first line OK ???
contentHeight += offset;
// Draw ruled lines
CGContextSetRGBStrokeColor(ctx, 0, 0, 0, 1);
for(int i=offset;i < contentHeight;i+=height) {
CGPoint lpoints[2] = { CGPointMake(0, i), CGPointMake(rect.size.width, i) };
CGContextStrokeLineSegments(ctx, lpoints, 2);
}
//vertical line
self.startPoint = CGPointMake(22.0, self.frame.size.height * -1);
self.endPoint = CGPointMake(22.0, self.frame.size.height * 2);
self.lineColor = [UIColor redColor];
CGContextSetShouldAntialias(ctx, NO);
CGContextSetStrokeColorWithColor(ctx, [self.lineColor CGColor]);
CGContextSetLineWidth(ctx, 1);
CGContextBeginPath(ctx);
CGContextMoveToPoint(ctx, self.startPoint.x, self.startPoint.y);
CGContextAddLineToPoint(ctx, self.endPoint.x, self.endPoint.y);
CGContextMoveToPoint(ctx, self.startPoint.x + 2.0f, self.startPoint.y);
CGContextAddLineToPoint(ctx, self.endPoint.x + 2.0f, self.endPoint.y);
CGContextDrawPath(ctx, kCGPathStroke);
}
This code correctly draws horizontal ruled lines in a UITextview and a vertical margin line on the left.
The last thing i'm trying to do is shift the text in my textview right so that it sits to the right of the vertical line.
Any ideas?
OK I solved the problem by using contentInset to push the text to the right by 20 pixels, then amended my drawRect to extend the horizontal line by -20 to account for the contentInset

Only increase height or width using pinch gesture [duplicate]

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.

Making a circular NSSlide look and behave like in GarageBand

I am new to drawing with Cocoa, and I am making some software which will have sliders similar to these found in GarageBand:
GB Sliders http://img33.imageshack.us/img33/2668/schermafbeelding2010061r.png
These look beautiful and can be controld by moving the mouse up and down.
Can you help me with customizing NSSliders by subclassing them, so I can make them look and behave exactly as in GarageBand? Thanks.
I have one image for the knob which should be rotated as they do not need to be in 3D .
The simplest way is to create a NSView subclass that handles both the mouse management and the drawing.
There is a sample code that can help you to start named "TLayer". It is part of the Examples of the XCode 3.1.4. It contains a circular custom view that controls the offset and the radius of the shadow drawn for layers. It is easy to understand and easy to extend.
Note: as it does not seems to be available on the Apple website, so I have pasted the sources below.
ShadowOffsetView.h
#import <AppKit/AppKit.h>
extern NSString *ShadowOffsetChanged;
#interface ShadowOffsetView : NSView
{
CGSize _offset;
float _scale;
}
- (float)scale;
- (void)setScale:(float)scale;
- (CGSize)offset;
- (void)setOffset:(CGSize)offset;
#end
ShadowOffsetView.m
#import "ShadowOffsetView.h"
NSString *ShadowOffsetChanged = #"ShadowOffsetChanged";
#interface ShadowOffsetView (Internal)
- (NSCell *)cell;
#end
#implementation ShadowOffsetView
- (id)initWithFrame:(NSRect)frame
{
self = [super initWithFrame:frame];
if (self == nil)
return nil;
_offset = CGSizeZero;
return self;
}
- (void)dealloc
{
[super dealloc];
}
- (float)scale
{
return _scale;
}
- (void)setScale:(float)scale
{
_scale = scale;
}
- (CGSize)offset
{
return CGSizeMake(_offset.width * _scale, _offset.height * _scale);
}
- (void)setOffset:(CGSize)offset
{
offset = CGSizeMake(offset.width / _scale, offset.height / _scale);
if (!CGSizeEqualToSize(_offset, offset)) {
_offset = offset;
[self setNeedsDisplay:YES];
}
}
- (BOOL)isOpaque
{
return NO;
}
- (void)setOffsetFromPoint:(NSPoint)point
{
float radius;
CGSize offset;
NSRect bounds;
bounds = [self bounds];
offset.width = (point.x - NSMidX(bounds)) / (NSWidth(bounds) / 2);
offset.height = (point.y - NSMidY(bounds)) / (NSHeight(bounds) / 2);
radius = sqrt(offset.width * offset.width + offset.height * offset.height);
if (radius > 1) {
offset.width /= radius;
offset.height /= radius;
}
if (!CGSizeEqualToSize(_offset, offset)) {
_offset = offset;
[self setNeedsDisplay:YES];
[(NSNotificationCenter *)[NSNotificationCenter defaultCenter]
postNotificationName:ShadowOffsetChanged object:self];
}
}
- (void)mouseDown:(NSEvent *)event
{
NSPoint point;
point = [self convertPoint:[event locationInWindow] fromView:nil];
[self setOffsetFromPoint:point];
}
- (void)mouseDragged:(NSEvent *)event
{
NSPoint point;
point = [self convertPoint:[event locationInWindow] fromView:nil];
[self setOffsetFromPoint:point];
}
- (void)drawRect:(NSRect)rect
{
NSRect bounds;
CGContextRef context;
float x, y, w, h, r;
bounds = [self bounds];
x = NSMinX(bounds);
y = NSMinY(bounds);
w = NSWidth(bounds);
h = NSHeight(bounds);
r = MIN(w / 2, h / 2);
context = [[NSGraphicsContext currentContext] graphicsPort];
CGContextTranslateCTM(context, x + w/2, y + h/2);
CGContextAddArc(context, 0, 0, r, 0, 2*M_PI, true);
CGContextClip(context);
CGContextSetGrayFillColor(context, 0.910, 1);
CGContextFillRect(context, CGRectMake(-w/2, -h/2, w, h));
CGContextAddArc(context, 0, 0, r, 0, 2*M_PI, true);
CGContextSetGrayStrokeColor(context, 0.616, 1);
CGContextStrokePath(context);
CGContextAddArc(context, 0, -2, r, 0, 2*M_PI, true);
CGContextSetGrayStrokeColor(context, 0.784, 1);
CGContextStrokePath(context);
CGContextMoveToPoint(context, 0, 0);
CGContextAddLineToPoint(context, r * _offset.width, r * _offset.height);
CGContextSetLineWidth(context, 2);
CGContextSetGrayStrokeColor(context, 0.33, 1);
CGContextStrokePath(context);
}
#end
Well, for the actual drawing you'd either have to have images for each rotation angle of the knob (easier to implement) and then just draw the proper one.
(While for a real realistic 3d look—even if possible—programmatic drawing wouldn't be worth its time, I guess.)
Or draw the knob by code. This article should give you an idea I think:
http://katidev.com/blog/2008/03/07/how-to-create-a-custom-control-with-nsview/
(For both, the mouse event handling and basic NSBezerPath drawing of circular and rotating knob-like elements)