Related
Having never actually developed with iOS before, I am investigating the possibilies with standard iOS controls such as text fields, list etc. and would like to know which transforms can be applied to them.
Do these standard controls support 2D transforms such as scale, translate and rotate?
Do these standard controls also support 3D transforms such as scale, translate and rotate which include a z axis?
If the answer is yes to either questions, what "level" of support exists? For example with a text field, if I transform it in 3D coordinate space can I still enter text into it?
Yes, and a lot. UITextField for example inherits from UIControl with inherits from UIView. This means that you have access to the view's transform property directly via its transform property:
[myTextField setTransform:CGAffineTransformMake(a, b, c, d, x, y)];
Or for 3D support, you can access the transform property of the view's layer to apply a CATransform3D:
[myTextField.layer setTransform:CATransform3DRotate(trans, theta, x, y, z)];
CATransform3D is defined as follows, and as #Xman pointed out, you'll need to import the QuartzCore framework using #import <QuartzCore/QuartzCore.h>, as well as link against it in your build phases.
struct CATransform3D
{
CGFloat m11, m12, m13, m14;
CGFloat m21, m22, m23, m24;
CGFloat m31, m32, m33, m34;
CGFloat m41, m42, m43, m44;
};
typedef struct CATransform3D CATransform3D;
In both of these cases, you can still interact with the text field after a transform has been applied to it.
More information can be found in Apple's documentation.
Check CATransform3D also,
CATransform3D yourTransform = CATransform3DIdentity;
yourTransform.m34 = 1.0 / -500;
//You can rotate the component in any angle around x,y,z axis using following line.
//Below line will rotate the component in 60 degree around y-axis.
yourTransform = CATransform3DRotate(yourTransform, DEGREES_TO_RADIANS(60), 0.0f, 1.0f, 0.0f); //#define DEGREES_TO_RADIANS(d) (d * M_PI / 180)
//You can even translate the component along x,y,z axis
//Below line will translate component by 50 on y-axis
yourTransform = CATransform3DTranslate(yourTransform, 0, 50, 0);
//apply transform to component
yourComponent.layer.transform = yourTransform;
Don't forget to import
#import <QuartzCore/QuartzCore.h>
Hope this will help you.
How can I make such an interface with cocos2d for iphone? Cortex interface
I already made a subclass of CCSprite and override the draw
method like this:
-(void)draw {
ccDrawCircle(CGPointMake(480/2, 320/2), 70, 0, 50000, NO);
ccDrawCircle(CGPointMake(480/2, 320/2), 25, 0, 50000, NO);
ccDrawLine(CGPointMake(480/2, 320/2+25), CGPointMake(480/2, 320/2+70));
ccDrawLine(CGPointMake(480/2+25, 320/2), CGPointMake(480/2+70, 320/2));
ccDrawLine(CGPointMake(480/2, 320/2-25), CGPointMake(480/2, 320/2-70));
ccDrawLine(CGPointMake(480/2-25, 320/2), CGPointMake(480/2-70, 320/2));
}
The problem is that I don't have any control over the circle (can't set the position of it)...and i don't know how to place text/images into these "cells". Another problem is the touch detection..mayby just cgrects? but what if i have more than 4 cells and one cell is "rotated"?
Any ideas?
I think you have two options here, but I don't recommend subclassing CCSprite, infact very rarely would recommend doing so, theres almost no need to.
In my opinion, you could do either of these to get your image.
1. Use OpenGL to draw your image.
2. Use CCSprite to draw your image. (Cleaner)
Once you have drawn it, its simply a matter of creating it when you press down on the screen.
Once you press down on the screen (or any prescribed object) I would then employ a simple trigonometric solution.
This is the algorithm I would use:
Press down on screen, Get the position of touch. (sourcepos) and create your cortex img
On Movement of finger on screen, get the position (currentpos) the angle and magnitude in relation to the original (sourcepos) touch.
Now, using simple angles we can install different bounds on your CCSprite using if statements. Its also a good idea to use #define kMinMagnitude X statement to ensure the user moves their finger adequately.
I suppose you can either execute the //Load Twitter or Load Facebook either on the movement or the cancelation of a touch. Thats entirely up to you.
(PSUDOCODE):
dx = currentpos.x - sourcepos.x
dy = currentpos.y - sourcepos.y
mag = sqrt(dx*dx + dy*dy);
ang = CC_RADIANS_TO_DEGREES(atan2f(dy/dx));
if (ang > 0 && ang < 80 && mag > kMinMagnitude) //Load Twitter
if (ang > 80 && ang < 120 && mag > kMinMagnitude) //Load facebook
I don't think making a subclass of CCSprite is the right choice here. You will probably want a NSObject that creates the CCSprites for you.
Also CCSprite.position = CGPointMake( X, Y ) should allow you to set the position of the sprite. Don't forget to add it to a layer just like any other CCNode object.
How do I move a ball along a specific UiBezierPath? Is that possible?
I've tried everything including doing a hit test using
-(BOOL)containsPoint:(CGPoint)point onPath:(UIBezierPath*)path inFillArea:(BOOL)inFill
How do I detect a touch along a path?
Firstly ... it is an absolute fact that:
there is, definitely, NO method, provided by Apple,
to extract points from a UIBezierPath.
That is an absolute fact as of February 2011, in the latest OS, and everything just on the horizon. I've spoken to the relevant engineers at Apple about the issue. It's annoying in many game programming contexts, but that's the fact. So, it could be that answers your question?
Secondly: don't forget it's as easy as pie to animate something along a UIBezierPath. There are numerous answers on SO, for example How can I animate the movement of a view or image along a curved path?
Thirdly: Regarding finding out where touches happened, if that's what you're asking, as Cory said, this is a hard problem! But you can use CGContextPathContainsPoint to find out if a point (or touch) was on a path of a given thickness.
After that, assuming we are talking about cubics, you need to find points and likely velocities (a.k.a. tangents) along a bezier.
Here is the exact, total code to do that: Find the tangent of a point on a cubic bezier curve (on an iPhone). I pasted it in below, too.
You will have to implement your own MCsim or iteration to find where it hit, depending on your situation. That's not so hard.
(Fourthly -- as a footnote, there's that new thing where you can progressively draw a bezpath, probably not relevant to your question but just a note.)
For the convenience of anyone reading in the future, here is the summary from the linked question of the two "handy" routines...
Finally, here in the simplest possible fashion are the two routines to calculate equidistant points (in fact, approximately equidistant) and the tangents of those, along a bezier cubic:
CGFloat bezierPoint(CGFloat t, CGFloat a, CGFloat b, CGFloat c, CGFloat d)
{
CGFloat C1 = ( d - (3.0 * c) + (3.0 * b) - a );
CGFloat C2 = ( (3.0 * c) - (6.0 * b) + (3.0 * a) );
CGFloat C3 = ( (3.0 * b) - (3.0 * a) );
CGFloat C4 = ( a );
return ( C1*t*t*t + C2*t*t + C3*t + C4 );
}
CGFloat bezierTangent(CGFloat t, CGFloat a, CGFloat b, CGFloat c, CGFloat d)
{
CGFloat C1 = ( d - (3.0 * c) + (3.0 * b) - a );
CGFloat C2 = ( (3.0 * c) - (6.0 * b) + (3.0 * a) );
CGFloat C3 = ( (3.0 * b) - (3.0 * a) );
CGFloat C4 = ( a );
return ( ( 3.0 * C1 * t* t ) + ( 2.0 * C2 * t ) + C3 );
}
The four precalculated values, C1 C2 C3 C4, are sometimes called the coefficients of the bezier. (Recall that a b c d are usually called the four control points.) Of course, t runs from 0 to 1, perhaps for example every 0.05. Simply call these routines once for X and separately once for Y.
Hope it helps someone!
there is a method in bezierpath class called containsPoint: .. refer: http://developer.apple.com/library/ios/#documentation/2ddrawing/conceptual/drawingprintingios/BezierPaths/BezierPaths.html
and you can detect weather the touch point is in bezier path object or not. I have used this with my own method by which a user can easily detect a touch on the bezier path (not inside or out sied if a circle or close path is there).
This code lets user select a bezier path drawing object on touch of it and a dashed line with animation appears on it. hope it helps someone.
Here is code from my own project:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
if([[self tapTargetForPath:path] containsPoint:startPoint]) // 'path' is bezier path object
{
[self selectShape:currentSelectedPath];// now select a new/same shape
NSLog(#"selectedShapeIndex: %d", selectedShapeIndex);
break;
}
}
// this method will let you easily select a bezier path ( 15 px up and down of a path drawing)
- (UIBezierPath *)tapTargetForPath:(UIBezierPath *)path
{
if (path == nil) {
return nil;
}
CGPathRef tapTargetPath = CGPathCreateCopyByStrokingPath(path.CGPath, NULL, fmaxf(35.0f, path.lineWidth), path.lineCapStyle, path.lineJoinStyle, path.miterLimit);
if (tapTargetPath == NULL) {
return nil;
}
UIBezierPath *tapTarget = [UIBezierPath bezierPathWithCGPath:tapTargetPath];
CGPathRelease(tapTargetPath);
return tapTarget;
}
-(void)selectShape:(UIBezierPath *)pathToSelect
{
centerline = [CAShapeLayer layer];
centerline.path = pathToSelect.CGPath;
centerline.strokeColor = [UIColor whiteColor].CGColor;
centerline.fillColor = [UIColor clearColor].CGColor;
centerline.lineWidth = 1.0;
centerline.lineDashPattern = [NSArray arrayWithObjects:[NSNumber numberWithInt:10], [NSNumber numberWithInt:5], nil];
[self.layer addSublayer:centerline];
// showing animation on line
CABasicAnimation *dashAnimation;
dashAnimation = [CABasicAnimation animationWithKeyPath:#"lineDashPhase"];
[dashAnimation setFromValue:[NSNumber numberWithFloat:0.0f]];
[dashAnimation setToValue:[NSNumber numberWithFloat:30.0f]];
[dashAnimation setDuration:1.0f];
[dashAnimation setRepeatCount:10000];
[centerline addAnimation:dashAnimation forKey:#"linePhase"];
}
This is a really hard problem. You'll need to determine the point on the path closest to the touched point, which is a complicated math problem. This is the best thing I could find using a quick Google search. (Beware: the code is in BASIC.)
http://www.tinaja.com/glib/bezdist.pdf
I admit, I'm tempted to write this myself. It'd be very useful, and I like a challenge.
Mathematically, you can not determine if a point inside a curve, a pure mathematical one, as a curve has no surface. What you can do, is test if the point is inside the surface defined by the bounds of the closed curve created by CGPathCreateCopyByStrokingPath with CGPathContainsPoint. It creates the curve that is filled to render your curve on the screen.
If CGPathCreateCopyByStrokingPath is called with its parameter:CGFloat lineWidth less or equal to the minimum radius of curvature of the original curve, it will not self-intersect. Also the original curve should not self intersects.
If CGPathContainsPoint returns true, and both conditions I mentioned are met, you are guaranteed to find one and only point on the curve that is at a distance of lineWidth. There is no algebraic solution to this, so you have to find an approximate point on the curve that is close to the touched point with the method described here: Find a point, a given distance, along a simple cubic bezier curve. (On an iPhone!)
Swit: for #rakesh's answer
func tapTargetForPath(_ path: CGPath) -> UIBezierPath? {
let path = UIBezierPath(cgPath: path)
guard let tapTargetPath = CGPath(__byStroking: path.cgPath,
transform: nil,
lineWidth: CGFloat(fmaxf(35.0, Float(path.lineWidth))),
lineCap: path.lineCapStyle,
lineJoin: path.lineJoinStyle,
miterLimit: path.miterLimit) else {
return nil
}
return UIBezierPath(cgPath: tapTargetPath)
}
Is there a way to simulate handwriting using quartz?
I mean there is a path between points A, B and C.
I want path to come out of point A and go animated to point B and then C.
What comes to mind is two options to do it:
Ugly- Create path then mask it and move mask around to reveal a path.
Takes a lot of time to create and unreliable and ugly hack
move points A,B,C and draw line between them.
Some way to animate a circle along a path leaving the trail?
Any techniques, examples?
Thanks.
Make a CAShapeLayer and then animate its path.
As mentioned in the comment above an ideal API would be one that would let you draw any arbitrary segment along the path but I have not seen any such API.
Another approach would be to define your path is discreet segments. Then use NSBezierPath's element methods to walk along the path and draw each segment along the way on a timer or using NSAnimation. The problem with this approach is that is does not let you use any arbitrary path.
A bezier curve defines a way to get a set of points based on an unrelated parameter, usually called t. To render the full curve, you evaluate t between 0 and 1 and draw a line from each point to the next. To render less than the full curve, you evaluate t from 0 to a number less than one. To animate drawing the curve, you could evaluate the points and draw the segments on a timer.
You can split a bezier curve at an arbitrary t. Doing that will allow you to pass the curve to the system to draw, or to use in a CAShapeLayer.
A handwritten letter will usually be a series of bezier curves, or a bezier spline. The end point of one curve is the start point of the next. Think of t as going from zero to the number of segments in the spline. If there are 3 curves, think of t as going from 0 to 3. When t is between 1 and 2, you would pass the whole first segment and part of the second segment to the system to draw.
You can read about DeCasteljau's algorithm for splitting bezier curves. Here is a code sample for a cubic bezier curve on a plane:
// initial curve is four point x0,y0 , x1,y1 , x2,y2 , x3,y3
// x0,y0 and x3,y3 are the anchors
// point to split curve at is 0<t<1
nt = 1.0 - t;
x01 = nt * x0 + t * x1;
y01 = nt * y0 + t * y1;
x12 = nt * x1 + t * x2;
y12 = nt * y1 + t * y2;
x23 = nt * x2 + t * x3;
y23 = nt * y2 + t * y3;
x012 = nt * x01 + t * x12;
y012 = nt * y01 + t * y12;
x123 = nt * x12 + t * x23;
y123 = nt * y12 + t * y23;
x0123 = nt * x012 + t * x123;
y0123 = nt * y012 + t * y123;
// now the new curve you want is
// x0,y0 , x01,y01 , x012,y012 , x0123,y0123
// the other half of the curve, discarded in your case, is
// x0123,y0123 , x123,y123 , x23,y23 , x3,y3
So given a series of curves that describe your handwritten character, you would animate from 0 to T, where T is the number of curves. calculate t=T-floor(T) and when t is not zero, use it to split the curve at n=floor(T).
Think about a speedometer, and imagine all these little strokes around it like 10 mph, 20 mph, 30 mph, and so on. I try to draw something similar.
Imagine this: There are three views: A, B, C.
A owns B owns C. C is a stroke on that speedometer. So to create it easily, there is view B inbetween which is exactly same size of A. First, C is positioned centered at the top with y=0. That's simple. And then, B is rotated by 10 degrees. Also simple. I keep adding "copies" of B+C on top of A, until I have all strokes on that speedometer.
But now: That's all a big waste of memory, because B is just garbage. It contains only C and is just used once to bring C easily into the desired position. I want to get rid of B, and instead position C directly on top of A.
How could I do this? Could I convert the coordinates from C into those of A, then get rid of B and position C directly on A? Notice that B was rotated and as big as A. It's hard to explain. Let me know if you need further descriptions to follow.
This question is vague, but here are three ideas that might help a little:
(1) Instead of getting rid of B, can you just get rid of A?
(2) The UIView method convertPoint:toView: -- You might be able to call this on B to find the coordinates of C in terms of A, and use that to move B's subviews to A.
(3) Instead of rotating a view, you could use some good ol' trig to add the small subviews or even draw directly where you want them.
For example, here's something that might help draw tick marks around a speedometer:
CGPoint from, to;
CGFloat innerRadius = 50.0;
CGFloat outerRadius = 60.0;
for (float angle = 0; angle < M_PI*2; angle += M_PI/100) {
from.x = innerRadius * cos(angle);
from.y = innerRadius * sin(angle);
to.x = outerRadius * cos(angle);
to.y = outerRadius * sin(angle);
[self drawLineFrom:from to:to];
}
To be useful, you'd probably want to translate those points (which have rotational center (0,0)) to be centered around a point in your view.
I think you can get rid of B pretty easily by adding C, then setting the anchor point of C to be the center of the circle (I assume y=A.frame.size.height, x is centered), and then you can apply the rotation to C and it should rotate around that anchor getting the desired effect.
Something like
C.layer.anchorPoint = CGPointMake(A.frame.size.height, A.center.x);
C.transform = CGAffineTransformMakeRotation(angle);
Or you could also add C to the center of A, then create a concatenation transform of a translation up and a rotation.
i.e
CGAffineTransform trans1 = CGAffineTransformMakeRotation(angle);
CGAffineTransform trans2 = CGAffineTransformMakeTranslation(0, A.frame.size.height);
C.transform = CGAffineTransformConcat(trans1, trans2);
Though if you are really going to be drawing all those tick marks, Tyler's drawing solution is probably better.