Way to draw NSString on a curved path? [duplicate] - iphone

Is there a set of string attributes I can specify that will draw the text at an angle when I call:
[label drawAtPoint:textStart withAttributes:attributes];

Here's an example that uses a transform to rotate the drawing context. Essentially it's just like setting a color or shadow, just make sure to use -concat instead of -set.
CGFloat rotateDeg = 4.0f;
NSAffineTransform *rotate = [[NSAffineTransform alloc] init];
[rotate rotateByDegrees:rotateDeg];
[rotate concat];
// Lock focus if needed and draw strings, images here.
[rotate release];

NSString itself doesn't have rotation, but you can rotate the context. The string will always be drawn "horizontally" as far as the coordinate space goes, but what actual direction that corresponds to depends on the context. Just use NSAffineTransform to spin it as needed.

Related

Rotating UIImageView Moves the Image Off Screen

I have a simple rotation gesture implemented in my code, but the problem is when I rotate the image it goes off the screen/out of the view always to the right.
The image view that is being rotated center X gets off or increases (hence it going right off the screen out of the view).
I would like it to rotate around the current center, but it's changing for some reason. Any ideas what is causing this?
Code Below:
- (void)viewDidLoad
{
[super viewDidLoad];
CALayer *l = [self.viewCase layer];
[l setMasksToBounds:YES];
[l setCornerRadius:30.0];
self.imgUserPhoto.userInteractionEnabled = YES;
[self.imgUserPhoto setClipsToBounds:NO];
UIRotationGestureRecognizer *rotationRecognizer = [[UIRotationGestureRecognizer alloc] initWithTarget:self action:#selector(rotationDetected:)];
[self.view addGestureRecognizer:rotationRecognizer];
rotationRecognizer.delegate = self;
}
- (void)rotationDetected:(UIRotationGestureRecognizer *)rotationRecognizer
{
CGFloat angle = rotationRecognizer.rotation;
self.imageView.transform = CGAffineTransformRotate(self.imageView.transform, angle);
rotationRecognizer.rotation = 0.0;
}
You want to rotate the image around it's center, but that's not what it is actually happening. Rotation transforms take place around the origin. So what you have to do is to apply a translate transform first to map the origin to the center of the image, and then apply the rotation transform, like so:
self.imageView.transform = CGAffineTransformTranslate(self.imageView.transform, self.imageView.bounds.size.width/2, self.imageView.bounds.size.height/2);
Please note that after rotating you'll probably have to undo the translate transform in order to correctly draw the image.
Hope this helps
Edit:
To quickly answer your question, what you have to do to undo the Translate Transform is to subtract the same difference you add to it in the first place, for example:
// The next line will add a translate transform
self.imageView.transform = CGAffineTransformTranslate(self.imageView.transform, 10, 10);
self.imageView.transform = CGAffineTransformRotate(self.imageView.transform, radians);
// The next line will undo the translate transform
self.imageView.transform = CGAffineTransformTranslate(self.imageView.transform, -10, -10);
However, after creating this quick project I realized that when you apply a rotation transform using UIKit (like the way you're apparently doing it) the rotation actually takes place around the center. It is only when using CoreGraphics that the rotation happens around the origin. So now I'm not sure why your image goes off the screen. Anyway, take a look at the project and see if any code there helps you.
Let me know if you have any more questions.
The 'Firefox' image is drawn using UIKit. The blue rect is drawn using CoreGraphics
You aren't rotating the image around its centre. You'll need correct this manually by translating it back to the correct position

UIImage remove some pixels issue

I have the application where user can erase image.
So if user touches some px of the image, alfa of these pixels should become lower.
For instance, if I touched (0,0) pixel of the image one time, (0,0) px opacity should become 0.9. If I touched that px 10 times I shouldn't see image at point (0,0).
What is the best approach to implement that?
This is the coe by which you can detect the touch values
CGPoint StartPoint = [touch previousLocationInView:self];
CGPoint Endpoint = [touch locationInView:self];
NSString *str = [NSString stringWithFormat:#"%f",StartPoint.x];
NSString *strlx = [NSString stringWithFormat:#"%f",StartPoint.y];
NSString *strcx = [NSString stringWithFormat:#"%f",Endpoint.x];
NSString *strcy = [NSString stringWithFormat:#"%f",Endpoint.y];
here touch is the object for UITouch.
I cann't say anything about the opacity.
If you want it fast (as in real time fast) you'll need to use OpenGL.
The best way to do it to create a mask of alpha values which will be applied on the original image using a custom built shader.
The easier way but slower is to get the raw pixels from the UIImage
and applying the alpha values on the raw pixels array and then turning it back to a UIImage (here is a nice example)

CorePlot display callout when select point is upside down

I have implemented the following code in my CPTScatterPlotDelegate to display a callout bubble:
-(void)scatterPlot:(CPTScatterPlot *)plot plotSymbolWasSelectedAtRecordIndex:(NSUInteger)index
{
CPTXYPlotSpace *plotSpace = (CPTXYPlotSpace *)plot.plotSpace;
CPTGraph *graph = plot.graph;
int yIdent = ((NSString*)plot.identifier).intValue;
NSNumber *yVal = [[_dataRange yForIndex:index] objectAtIndex:yIdent-1];
NSNumber *xVal = [_dataRange xForIndex:index];
double doublePrecisionPlotPoint[2];//[x,y]
doublePrecisionPlotPoint[0] = xVal.doubleValue;
doublePrecisionPlotPoint[1] = yVal.doubleValue;
CGPoint touchedPoint = [graph.defaultPlotSpace plotAreaViewPointForDoublePrecisionPlotPoint:doublePrecisionPlotPoint];
if(_annotation)
[_annotation dismissCalloutAnimated:YES];
_annotation = [[SMCalloutView alloc] init];
//todo appropriate units
_annotation.title = [NSString stringWithFormat:#"%.2f kg", yVal.doubleValue];
[_annotation presentCalloutFromRect:CGRectMake(touchedPoint.x, touchedPoint.y, 1, 1) inView:_view constrainedToView:_view permittedArrowDirections:SMCalloutArrowDirectionAny animated:YES];
}
_dataRange is just a custom class holding my data and _annotation is the instance of my callout.
The problem is I cant't get the position of the callout working properly. If I set _view to the ViewController.view I get the right callout but in the wrong place like this:
If I set _view to the CPTGraphHostingView instead I get the right point but the callout appears to be flipped like this:
How to I get the right plot point co-ordinates to display the callout?
You shouldn't add any subviews to the hosting view—it uses a flip transform on iOS so Core Plot can share drawing code between iOS and the Mac.
Use the -convertPoint:toView: or -convertPoint:fromView: method to convert touchedPoint from the hosting view's coordinate system to the ViewController.view coordinate system.
I have subclassed CPTGraphHostingView for a few charts so as an alternative I found any subview you add to it you should do the following:
subview.transform = CGAffineTransform(1.0, -1.0)
This doesn't change the fact that the coordinate system is (0,0)-bottom-left but the UIView you add will draw the right way up when rendered. Still use Eric's Answer for translating between coordinate systems but for anyone that finds themselves here trying to add a subview to CPTGraphHostingView I hope this helps.

uiimage not being put into subview correctly

This is a difficult problem to explain... but i'll do my best.
First a background on the problem, basically i am creating a paint like app for ios and wanted to add a functionality that allows the user to select part of the image (multi-touch shows an opaque rectangle) and delete/copy-paste/rotate that part. I have got the delete and copy-paste working perfectly but the rotation is another story. To rotate the part of the image I first start by copying the part of the image and setting it to be the background of the selected rectangle layer, then the user rotates by an arbitrary angle using a slider. The problem is that sometimes the image ends up being displayed from another location of the rectangle (meaning the copied image hangs off the wrong corner of the rectangle). I thought this could be a problem with my rectangle.frame.origin but the value for that seems to be correct through various tests. It also seems to change depending on the direction that the drag goes in...
These Are Screens of the problem
In each of the above cases the mismatched part of the image should be inside the grey rectangle, i am at a loss as to what the problem is.
bg = [[UIImageView alloc] initWithImage:[self crop:rectangle.frame:drawImage.image]];
[rectangle addSubview:bg];
drawImage is the users drawing, and rectangle is the selected grey area.
crop is a method which returns a part of a given image from a give rect.
I am also having trouble with pasting an arbitrarily rotated image.. any ideas on how to do that?
Edit: adding more code.
-(void)drawRect:(int)x1:(int)y1:(int)x2:(int)y2{
[rectangle removeFromSuperview];
rectangle = [[UIView alloc] initWithFrame:CGRectMake(x1, y1, x2-x1, y2-y1)];
rectangle.backgroundColor = [UIColor colorWithRed:0.9 green:0.9 blue:0.9 alpha:0.6];
selectionImage = drawImage.image;
drawImage.image = selectionImage;
[drawImage addSubview:rectangle];
rectangleVisible = true;
rectangle.transform = transformation;
Could it have anything to do with how i draw my rectangle? (above) I call this method from a part of a touchesMoved method (below) which may cause the problem (touch 1 being in the wrong location may cause width to be negative?) if so, is there an easy way to remedy this?
if([[event allTouches] count] == 2 && !drawImage.hidden){
NSSet *allTouches = [event allTouches];
UITouch *touch1 = [[allTouches allObjects] objectAtIndex:0];
UITouch *touch2 = [[allTouches allObjects] objectAtIndex:1];
[self drawRect:[touch1 locationInView:drawImage].x :[touch1 locationInView:drawImage].y:
[touch2 locationInView:drawImage].x :[touch2 locationInView:drawImage].y];
}
I'm not sure if this is your problem, but it looks like you are just assuming that touch1 represents the upper left touch. I would start out by standardizing the rectangle.
// Standardizing the rectangle before making it the frame.
CGRect frame = CGRectStandardize(CGRectMake(x1, y1, x2-x1, y2-y1));
rectangle = [[UIView alloc] initWithFrame:frame];

Antialiasing edges of UIView after transformation using CALayer's transform

I have a UIView object that rotates using CALayer's transform:
// Create uiview object.
UIImageView *block = [[UIImageView alloc] initWithFrame....]
// Apply rotation.
CATransform3D basicTrans = CATransform3DIdentity;
basicTrans.m34 = 1.0/-distance;
blockImage.layer.transform = CATransform3DRotate(basicTrans, rangle, 1.0f, 0.0f, 0.0f);
After rotating the edges of the object are not antialiasing. I need to antialias them.
Help me, please. How can it be done?
One way to do this is by placing the image inside another view that's 5 pixels bigger. The bigger view should have a transparent rasterized border that will smooth the edges of the UIImageView:
view.layer.borderWidth = 3;
view.layer.borderColor = [UIColor clearColor].CGColor;
view.layer.shouldRasterize = YES;
view.layer.rasterizationScale = [[UIScreen mainScreen] scale];
Then, place your UIImageView inside this parent view and center it (With 2.5 pixels around each edge).
Finally, rotate the parent view instead of the image view.
It works very well - you can also encapsulate the whole thing in class that creates the hierarchy.
Simply add this key-value pair to your Info.plist: UIViewEdgeAntialiasing set to YES.
check allowsEdgeAntialiasing property of CALayer.
block.layer.allowsEdgeAntialiasing = YES; // iOS7 and above.
I had a similar issue when rotating around the z-axis. Setting shouldRasterize = YES prevented the jagged edges however it came at a performance cost. In my case I was re-using the views (and its layers) and keeping the shouldRasterize = YES was slowing things down.
The solution was, to turn off rasterization right after I didn't need it anymore. However since animation runs on another thread, there was no way of knowing when the animation was complete...until I found out about an extremely useful CATransaction method. This is an actual code that I used and it should illustrate its use:
// Create a key frame animation
CAKeyframeAnimation *wiggle = [CAKeyframeAnimation animationWithKeyPath:#"transform"];
NSInteger frequency = 5; // Higher value for faster vibration
NSInteger amplitude = 25; // Higher value for lower amplitude
// Create the values it will pass through
NSMutableArray *valuesArray = [[NSMutableArray alloc] init];
NSInteger direction = 1;
[valuesArray addObject:#0.0];
for (NSInteger i = frequency; i > 0; i--, direction *= -1) {
[valuesArray addObject:#((direction * M_PI_4 * (CGFloat)i / (CGFloat)amplitude))];
}
[valuesArray addObject:#0.0];
[wiggle setValues:valuesArray];
// Set the duration
[wiggle setAdditive:YES];
[wiggle setValueFunction:[CAValueFunction functionWithName:kCAValueFunctionRotateZ]];
[wiggle setDuration:0.6];
// Turn on rasterization to prevent jagged edges (anti-aliasing issues)
viewToRotate.layer.shouldRasterize = YES;
// ************ Important step **************
// Very usefull method. Block returns after ALL animations have completed.
[CATransaction setCompletionBlock:^{
viewToRotate.layer.shouldRasterize = NO;
}];
// Animate the layer
[viewToRotate.layer addAnimation:wiggle forKey:#"wiggleAnimation"];
worked like a charm for me.
I have not tried using this with implicit animations (i.e. animations that happen due to value change in animatable property for a non-view associated layer), however I would expect it to work as long as the CATransaction method is called before the property change, just as a guarantee the block is given to CATransaction before an animation starts.