iOS magnify the view not showing - ios

Hi i am trying to magnify the view with same place finger touch (Y value). I wrote below code but, it gives half of magnified view remains blur.
- (void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
NSLog(#"%#", context);
CGContextTranslateCTM(context, self.frame.size.width / 2, self.frame.size.height / 2);
CGContextScaleCTM(context, scale, scale);
CGContextTranslateCTM(context, - touchPoint.x, - touchPoint.y + (self.scaleAtTouchPoint? 0 : self.bounds.size.height/2));
[self.viewToMagnify.layer renderInContext:context];
}
here frame width and height is 80. Scale is 1.5. if i give scale = 1 gives correct result.
but the result will be like this.

You should use the method setNeedsDisplay to redraw just like this
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
// some code here
//redraw
[magnify setNeedsDisplay];
}

Related

drawRect line moves on different screen size

I'm using drawRect to create a line. Based on the "4inch screen" my line is the correct place. When ran on a "3.5 inch screen" the line is lower down and in the wrong place.
- (void)drawRect:(CGRect)rect
{
CGContextRef firstLine = UIGraphicsGetCurrentContext();
CGContextMoveToPoint(firstLine, 15, 389);
CGContextAddLineToPoint(firstLine, 320, 389);
CGContextStrokePath(firstLine);
CGContextSetLineWidth(firstLine, 1.1);
}
I know I need to do something with self.frame.heigh to make it dynamic to screen size but don't know where to put it.
Get the height of the view bounds each time you draw it and you are good to go.
- (void)drawRect:(CGRect)rect
{
/* Get the height of the view */
float height = CGRectGetHeight(self.bounds);
/* the amount to inset the line by */
float lineInset = 20;
/* y-position after insetting the line */
float yPosition = height - lineInset;
CGContextRef firstLine = UIGraphicsGetCurrentContext();
CGContextMoveToPoint(firstLine, 15, yPosition);
CGContextAddLineToPoint(firstLine, 320, yPosition);
CGContextSetLineWidth(firstLine, 1.1);
CGContextStrokePath(firstLine);
}

iPhone: Animate circle with UIKit

I am using a CircleView class that basically inherits off UIView and implements drawRect to draw a circle. This all works, hurrah!
What I cannot figure out though is how to make it so when I touch it (touch code is implemented) the circle grows or pops. Normally I'd use the UIKit animation framework to do this but given I am basically overriding the drawRect function to directly draw the circle. So how do I animate this?
- (void)drawRect:(CGRect)rect{
CGContextRef context= UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, _Color.CGColor);
CGContextFillEllipseInRect(context, CGRectMake(0, 0, self.frame.size.width, self.frame.size.height));
}
- (void)handleSingleTap:(UITapGestureRecognizer *)recognizer {
// Animate?
}
The answers depends on what you mean by "grows or pops". When I hear "pop" I assume that the view scales up over a short period of time ans scales back down again to the original size. Something that "grows" on the other hand would scale up but not down again.
For something that scales up and down again over a short period of time I would use a transform to scale it. Custom drawing or not, UIView has build in support for animating a simple transform. If this is what you are looking for then it's not more then a few lines of code.
[UIView animateWithDuration:0.3
delay:0.0
options:UIViewAnimationOptionAutoreverse // reverse back to original value
animations:^{
// scale up 10%
yourCircleView.transform = CGAffineTransformMakeScale(1.1, 1.1);
} completion:^(BOOL finished) {
// restore the non-scaled state
yourCircleView.transform = CGAffineTransformIdentity;
}];
If on the other hand you want the circle to grow a little bit more on every tap then this won't do it for you since the view is going to look pixelated when it scales up. Making custom animations can be tricky so I would still advice you to use a scaling transform for the actual animation and then redraw the view after the animation.
[UIView animateWithDuration:0.3
animations:^{
// scale up 10%
yourCircleView.transform = CGAffineTransformMakeScale(1.1, 1.1);
} completion:^(BOOL finished) {
// restore the non-scaled state
yourCircleView.transform = CGAffineTransformIdentity;
// redraw with new value
yourCircleView.radius = theBiggerRadius;
}];
If you really, really want to do a completely custom animation then I would recommend that you watch Rob Napiers talk on Animating Custom Layer Properties, his example is even the exact thing you are doing (growing a circle).
If you want an animation that expands the ellipse from the centre, try this. In the header, define 3 variables:
BOOL _isAnimating;
float _time;
CGRect _ellipseFrame;
Then implement these 3 methods in the .m file:
- (void)drawRect:(CGRect)rect; {
[super drawRect:rect];
CGContextRef context= UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, _Color.CGColor);
CGContextFillEllipseInRect(context, _ellipseFrame);
}
- (void)expandOutward; {
if(_isAnimating){
_time += 1.0f / 30.0f;
if(_time >= 1.0f){
_ellipseFrame = self.frame;
_isAnimating = NO;
}
else{
_ellipseFrame = CGRectMake(0.0f, 0.0f, self.frame.size.width * _time, self.frame.size.height * _time);
_ellipseFrame.center = CGPointMake(self.frame.size.width / 2.0f, self.frame.size.height / 2.0f);
[self setNeedsDisplay];
[self performSelector:#selector(expandOutward) withObject:nil afterDelay:(1.0f / 30.0f)];
}
}
}
- (void)handleSingleTap:(UITapGestureRecognizer *)recognizer; {
if(_isAnimating == NO){
_time = 0.0f;
_isAnimating = YES;
[self expandOutward];
}
}
This is the most basic way you can animate the circle expanding from the centre. Look into CADisplayLink for a constant sync to the screen if you want more detailed animations. Hope that Helps!

Animated bar graph in iOS

I am drawing a bar chart, it works fine except it dosent have animation. Ex: fill colore 0-50%.
I am using simple DrawRect method to draw here is my code:
- (void)drawRect:(CGRect)rect
{
CGFloat height = self.bounds.size.height;
CGContextRef context = UIGraphicsGetCurrentContext();
// CGContextClearRect(context, rect);
CGContextSetFillColorWithColor(context, [self colorWithR:149 G:21 B:29 A:1].CGColor);
CGFloat barWidth = 52;
int count = 0;
NSNumber *num = [NSNumber numberWithFloat:graphValue];
CGFloat x = count * (barWidth + 10);
CGRect barRect = CGRectMake(x, height - ([num floatValue] * height), barWidth, [num floatValue] * height);
CGContextAddRect(context, barRect);
count++;
CGContextFillPath(context);
}
Please help me know the simple way to add animation.
I believe you want to animate the heights of the bars in the bargraph.
I suggest you implement each bar as a separate UIview, a simple rectangle. You can also put all of them in one view with a custom drawRect. Then you need to scale these views or change their frame inside the animation block of either of the following methods:
+ (void)animateWithDuration:(NSTimeInterval)duration animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completion
OR
+ (void)animateWithDuration:(NSTimeInterval)duration animations:(void (^)(void))animations
For a great tutorial see this.
This is an example if you don't have a lot of time.
[UIView animateWithDuration:1//Amount of time the animation takes.
delay:0//Amount of time after which animation starts.
options: UIViewAnimationCurveEaseOut//How the animation will behave.
animations:^{
//here you can either set a CGAffineTransform, or change your view's frame.
//Both will work just fine.
yourBarView.transform = CGAffineTransformMakeScale (
scaleForX,//say 1, you dont want it to change.
scaleForY//say 20, you want to make it 20 times larger in the y
//direction.
//Note* to animate from a zero height to a finite height change the
//view's frame.
//yourBarView.frame = CGRectMake(0,0,20,100);
);
}
completion:^(BOOL finished){//This block is called when the animation completes.
NSLog(#"Done!");
}];

Using TouchesMoved to draw a shape

I am trying to draw an arrow using my finger on the screen. the idea was that by touching the screen I set the initial coordinates of my arrow, and as I dragged on the screen the arrow would extend and follow my finger. the height and width of the arrow will be the same, its the size of the arrow that matters. The arrow will be longer as I drag it away from the starting point. I tried dong it with something like this:
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UIGraphicsBeginImageContext(CGSizeMake(1536, 2048));
UITouch *touch = [touches anyObject];
CGPoint p1 = [touch locationInView:self.view];
CGSize size;
size.width = 50;
size.height = 400;
CGContextRef context = UIGraphicsGetCurrentContext();
[self drawArrowWithContext:context atPoint:p1 withSize:size lineWidth:4 arrowHeight:20 andColor:[UIColor whiteColor]];
// converts your context into a UIImage
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
// Adds that image into an imageView and sticks it on the screen.
UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
[self.view addSubview:imageView];
}
and
- (void) drawArrowWithContext:(CGContextRef)context atPoint:(CGPoint)startPoint withSize: (CGSize)size lineWidth:(float)width arrowHeight:(float)aheight andColor:(UIColor *)color
{
float width_wing = (size.width - width) / 2;
float main = size.height-aheight;
CGContextSetFillColorWithColor(context, [color CGColor]);
CGContextSetStrokeColorWithColor(context, [color CGColor]);
CGPoint rectangle_points[] = {
CGPointMake(startPoint.x + width_wing, startPoint.y + 0.0),
CGPointMake(startPoint.x + width_wing, startPoint.y + main),
CGPointMake(startPoint.x + 0.0, startPoint.y + main), // left point
CGPointMake(startPoint.x + size.width / 2, startPoint.y + size.height),
CGPointMake(startPoint.x + size.width, startPoint.y + main), // right point
CGPointMake(startPoint.x + size.width-width_wing, startPoint.y + main),
CGPointMake(startPoint.x + size.width-width_wing, startPoint.y + 0.0),
CGPointMake(startPoint.x + width_wing, startPoint.y + 0.0),
};
CGContextAddLines(context, rectangle_points, 8);
CGContextFillPath(context);
}
The arrow does appear on the screen if I run the code from the touches moved in a normal IBOutlet, but that was not the idea. I haven't managed to get this code to work yet, but I think that even if it worked, it would cause a crash, since I am deleting and redrawing the shape each time. Is this the right approach? What should I do?
Basically, there are some different options.
Stick to the UIImageView approach and stop recreating the image view all the time. Keep a reference to that UIImageView in the class detecting the touch events and just replace the image if something changed. Might be worth the extra effort for off-screen-drawing if you need the image for something else.
Implement the arrow drawing dynamically in an extra view.
The second one I will describe here briefly:
The class detecting the event needs a member variable/property, ArrowView *arrowView and something to remember the start point, CGPoint startPoint maybe.
ArrowView needs properties for the arrow parameters, CGPoint arrowStart, CGSize arrowSize.
In the touch event handler, do
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint position = [touch locationInView:self.view];
[self.startPoint startFrom:position];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint next = [touch locationInView:self.view];
CGSize size;
size.width = next.x - self.startPoint.x;
size.height = next.y - self.startPoint.y;
self.arrowView.arrowPoint = self.startPoint;
self.arrowView.arrowSize = size;
[self.arrowView setNeedsDisplay];
}
In ArrowView:
- (void)drawRect:(CGRect)rect {
[super drawRect:rect];
CGContextRef context = UIGraphicsGetCurrentContext();
// Now do the drawing stuff here using that context!
// self.arrowSize / self.arrowPoint do contain your drawing parameters.
...
}
I hope that gives you an idea.
For further reading, start here.
Yo can try to use UIPanGestureRecognizer to follow your finger and draw above of it.

Draw images evenly spaced along a path in iOS

What I want to do is move my finger across the screen (touchesMoved) and draw evenly spaced images (perhaps CGImageRefs) along the points generated by the touchesMoved. I can draw lines, but what I want to generate is something that looks like this (for this example I am using an image of an arrow but it could be any image, could be a picture of my dog :) ) The main thing is to get the images evenly spaced when drawing with a finger on an iPhone or iPad.
First of all HUGE props go out to Kendall. So, based on his answer, here is the code to take a UIImage, draw it on screen along a path (not a real pathRef, just a logical path created by the points) based on the distance between the touches and then rotate the image correctly based on the VECTOR of the current and previous points. I hope you like it:
First you need to load an image to be used as a CGImage over and over again:
NSString *imagePath = [[NSBundle mainBundle] pathForResource:#"arrow.png" ofType:nil];
UIImage *img = [UIImage imageWithContentsOfFile:imagePath];
image = CGImageRetain(img.CGImage);
make sure in your dealloc that you call
CGImageRelease(image);
then in touchesBegan, just store the starting point in a var that is scoped outside the method (declare it in your header like this :) in this case I am drawing into a UIView
#interface myView : UIView {
CGPoint lastPoint;
}
#end
then in touches Began:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
lastPoint = [touch locationInView:self];
}
and finally in touchesMoved, draw the bitmap to the screen and then when your distance has moved enough (in my case 73, since my image is 73 pixels x 73 pixels) draw that image to the screen, save the new image and set lastPoint equal to currentPoint
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
currentPoint = [touch locationInView:self];
double deltaX = lastPoint.x - currentPoint.x;
double deltaY = lastPoint.y - currentPoint.y;
double powX = pow(deltaX,2);
double powY = pow(deltaY,2);
double distance = sqrt(powX + powY);
if (distance >= 73){
lastPoint = currentPoint;
UIGraphicsBeginImageContext(self.frame.size);
[drawImage.image drawInRect:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)];
CGContextSaveGState(UIGraphicsGetCurrentContext());
float angle = atan2(deltaX, deltaY);
angle *= (M_PI / 180);
CGContextDrawImage(UIGraphicsGetCurrentContext(), CGRectMake(currentPoint.x, currentPoint.y, 73, 73),[self CGImageRotatedByAngle:image angle:angle * -1]);
CGContextRestoreGState(UIGraphicsGetCurrentContext());
drawImage.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
distance = 0;
}
}
- (CGImageRef)CGImageRotatedByAngle:(CGImageRef)imgRef angle:(CGFloat)angle
{
CGFloat angleInRadians = angle * (M_PI / 180);
CGFloat width = CGImageGetWidth(imgRef);
CGFloat height = CGImageGetHeight(imgRef);
CGRect imgRect = CGRectMake(0, 0, width, height);
CGAffineTransform transform = CGAffineTransformMakeRotation(angleInRadians);
CGRect rotatedRect = CGRectApplyAffineTransform(imgRect, transform);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef bmContext = CGBitmapContextCreate(NULL,
rotatedRect.size.width,
rotatedRect.size.height,
8,
0,
colorSpace,
kCGImageAlphaPremultipliedFirst);
CGContextSetAllowsAntialiasing(bmContext, FALSE);
CGContextSetInterpolationQuality(bmContext, kCGInterpolationNone);
CGColorSpaceRelease(colorSpace);
CGContextTranslateCTM(bmContext,
+(rotatedRect.size.width/2),
+(rotatedRect.size.height/2));
CGContextRotateCTM(bmContext, angleInRadians);
CGContextTranslateCTM(bmContext,
-(rotatedRect.size.width/2),
-(rotatedRect.size.height/2));
CGContextDrawImage(bmContext, CGRectMake(0, 0,
rotatedRect.size.width,
rotatedRect.size.height),
imgRef);
CGImageRef rotatedImage = CGBitmapContextCreateImage(bmContext);
CFRelease(bmContext);
[(id)rotatedImage autorelease];
return rotatedImage;
}
this will create an image that looks like this :
Going to add the following (with some changes to the above code in order to try and fill in the voids where touchesMoved is missing some points when you move fast:
CGPoint point1 = CGPointMake(100, 200);
CGPoint point2 = CGPointMake(300, 100);
double deltaX = point2.x - point1.x;
double deltaY = point2.y - point1.y;
double powX = pow(deltaX,2);
double powY = pow(deltaY,2);
double distance = sqrt(powX + powY);
distance = 0;
for (int j = 1; j * 73 < distance; j++ )
{
double x = (point1.x + ((deltaX / distance) * 73 * j));
double y = (point1.y + ((deltaY / distance) * 73 * j));
NSLog(#"My new point is x: %f y :%f", x, y);
}
Assuming that you already have code that tracks the user's touch as they move their touch around the screen, it sounds like you want to detect when they have moved a distance equal to the length of your image, at which time you want to draw another copy of your image under their touch.
To achieve this, I think you will need to:
calculate the length (width) of your image
implement code to draw copies of your image onto your view, rotated to any angle
each time the user's touch moves (e.g. in touchesMoved:):
calculate the delta of the touch each time it moves and generate the "length" of that delta (e.g. something like sqrt(dx^2 + dy^2))
accumulate the distance since the last image was drawn
if the distance has reached the length of your image, draw a copy of your image under the touch's current position, rotated appropriately (probably according to the vector from the position of the last image to the current position)
How does that sound?

Resources