CALayer breaks in landscape mode - ios

I am coding a custom transition based on the Shutter transition tutorial. it's pretty simple, the first view controller is meant to divide into hundreds of pieces and then fade one at a time, problem however is that it doesn't work in landscape mode. apparently the "contentsRect" method of CALayer (which is supposed to work on the normalized image space) only works in the portrait mode. I have attached my file in here if anyone needs to have a look and please if you can help me.
http://tinyurl.com/88669oy
regards
here is also my trans code:
CALayer* viewLayer;
- (void)transitWithImageView:(UIImageView *)imgView inView:(UIView *)view withImage:(UIImage *)img
{
viewLayer = [imgView layer];
[view.layer addSublayer:viewLayer];
CGSize layerSize = viewLayer.bounds.size;
// BEGINING OF MAKE ARRAY OF TRANSITIONS
NSMutableArray* allTransitionsArr = [[NSMutableArray alloc] init];
for ( int i = 0; i < BLOCK_COUNT; i++ )
{
CABasicAnimation *fade = [CABasicAnimation animationWithKeyPath:#"opacity"];
fade.toValue = [NSNumber numberWithFloat:-1-sqrt(i)];
fade.autoreverses = NO;
fade.duration = DURATION;
[allTransitionsArr addObject: fade];
}
// END OF MAKE ARRAY OF TRANSITIONS
NSMutableArray *blocks = [[NSMutableArray alloc] init];
[viewLayer removeFromSuperlayer];
[CATransaction begin];
[CATransaction setCompletionBlock:^(void)
{
[blocks enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop)
{
[obj setDelegate:nil];
[obj removeFromSuperlayer];
}];
[view.layer addSublayer:viewLayer];
}];
CGFloat bandWidth = layerSize.width / sqrt(BLOCK_COUNT);
CGFloat bandHeight = layerSize.height / sqrt(BLOCK_COUNT);
int yVal = -1;
for(int i = 0; i < BLOCK_COUNT; i++)
{
CALayer *block = [[CALayer alloc] init];
block.masksToBounds = YES;
CGFloat xOffset = 1.f / sqrt(BLOCK_COUNT);
CGFloat yOffset = 1.f / sqrt(BLOCK_COUNT);
block.bounds = CGRectMake(0, 0, bandWidth, bandHeight);
block.bounds = CGRectMake(0.f, 0.f, bandWidth, bandHeight);
block.contents = (id)[img CGImage];
block.contentsGravity = kCAGravityCenter;
CGPoint bandOrigin = viewLayer.frame.origin;
bandOrigin.x = bandOrigin.x + (bandWidth * (i % (int)sqrt(BLOCK_COUNT)));
if (i % (int)sqrt(BLOCK_COUNT) == 0 )
yVal++;
bandOrigin.y = bandOrigin.y + (bandHeight * yVal);
// PROBLEM IS GOT TO BE HERE
block.contentsRect = CGRectMake(xOffset * (i % (int)sqrt(BLOCK_COUNT)) ,yOffset * yVal, xOffset, yOffset);
[block setValue:[NSValue valueWithCGPoint:bandOrigin] forKeyPath:#"frame.origin"];
[view.layer addSublayer:block];
[block addAnimation:[allTransitionsArr objectAtIndex: BLOCK_COUNT - i - 1] forKey:nil];
[blocks addObject:block];
[block release];
}
[CATransaction commit];
[blocks release];
}

Related

Zoom UIView inside UIScrollView with drawing

I have a scroll view (gray) with a zooming view inside (orange). The problem is if I zoom this view the red shape drawn on it gets zoomed too including lines width and blue squares size. What I want is to keep constant lines width and blue squares size (like on first picture) scaling just the area of the shape itself according to zoom level (drawn text is just for reference, I don't care about its size)
before zoom
after zoom
view controller
#import "ViewController.h"
#import "ZoomingView.h"
#interface ViewController ()
#property (strong, nonatomic) IBOutlet UIScrollView *scrollView;
#end
#implementation ViewController
{
ZoomingView *_zoomingView;
}
- (void)viewDidLayoutSubviews
{
[self setup];
}
- (void)setup
{
CGFloat kViewSize = self.scrollView.frame.size.width - 40;
self.scrollView.minimumZoomScale = 1;
self.scrollView.maximumZoomScale = 10;
self.scrollView.delegate = self;
self.scrollView.contentSize = self.scrollView.bounds.size;
_zoomingView = [[ZoomingView alloc] initWithFrame:
CGRectMake((self.scrollView.frame.size.width - kViewSize) / 2,
(self.scrollView.frame.size.height - kViewSize) / 2,
kViewSize,
kViewSize)];
[self.scrollView addSubview:_zoomingView];
}
#pragma mark - UIScrollViewDelegate
- (UIView*)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
return _zoomingView;
}
- (void)scrollViewDidZoom:(UIScrollView *)scrollView
{
// zooming view position fix
UIView *zoomView = [scrollView.delegate viewForZoomingInScrollView:scrollView];
CGRect zvf = zoomView.frame;
if (zvf.size.width < scrollView.bounds.size.width) {
zvf.origin.x = (scrollView.bounds.size.width - zvf.size.width) / 2.0f;
} else {
zvf.origin.x = 0.0;
}
if (zvf.size.height < scrollView.bounds.size.height) {
zvf.origin.y = (scrollView.bounds.size.height - zvf.size.height) / 2.0f;
} else {
zvf.origin.y = 0.0;
}
zoomView.frame = zvf;
[_zoomingView updateWithZoomScale:scrollView.zoomScale];
}
#end
zooming view
#import "ZoomingView.h"
#implementation ZoomingView
{
CGFloat _zoomScale;
}
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self setup];
}
return self;
}
- (void)setup
{
self.backgroundColor = [UIColor orangeColor];
_zoomScale = 1;
}
- (void)drawRect:(CGRect)rect
{
const CGFloat kPointSize = 10;
NSArray *points = #[[NSValue valueWithCGPoint:CGPointMake(30, 30)],
[NSValue valueWithCGPoint:CGPointMake(200, 40)],
[NSValue valueWithCGPoint:CGPointMake(180, 200)],
[NSValue valueWithCGPoint:CGPointMake(70, 180)]];
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(context, 1);
// points
[[UIColor blueColor] setStroke];
for (NSValue *value in points) {
CGPoint point = [value CGPointValue];
CGContextStrokeRect(context, CGRectMake(point.x - kPointSize / 2,
point.y - kPointSize / 2,
kPointSize,
kPointSize));
}
// lines
[[UIColor redColor] setStroke];
for (NSUInteger i = 0; i < points.count; i++) {
CGPoint point = [points[i] CGPointValue];
if (i == 0) {
CGContextMoveToPoint(context, point.x, point.y);
} else {
CGContextAddLineToPoint(context, point.x, point.y);
}
}
CGContextClosePath(context);
CGContextStrokePath(context);
// text
NSAttributedString *str = [[NSAttributedString alloc] initWithString:[NSString stringWithFormat:#"%f", _zoomScale] attributes:#{NSFontAttributeName : [UIFont systemFontOfSize:12]}];
[str drawAtPoint:CGPointMake(5, 5)];
}
- (void)updateWithZoomScale:(CGFloat)zoomScale
{
_zoomScale = zoomScale;
[self setNeedsDisplay];
}
#end
EDIT
Based on proposed solution (which works for sure) I was interested if I could make it work using my drawRect routine and Core Graphics methods.
So I changed my code this way, applying proposed scaling and contentsScale approach from this answer. As a result, without contentsScale drawing looks very blurry and with it much better, but a light blurriness persists anyway.
So the approach with layers gives the best result, although I don't get why.
- (void)drawRect:(CGRect)rect
{
const CGFloat kPointSize = 10;
NSArray *points = #[[NSValue valueWithCGPoint:CGPointMake(30, 30)],
[NSValue valueWithCGPoint:CGPointMake(200, 40)],
[NSValue valueWithCGPoint:CGPointMake(180, 200)],
[NSValue valueWithCGPoint:CGPointMake(70, 180)]];
CGFloat scaledPointSize = kPointSize * (1.0 / _zoomScale);
CGFloat lineWidth = 1.0 / _zoomScale;
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(context, lineWidth);
// points
[[UIColor blueColor] setStroke];
for (NSValue *value in points) {
CGPoint point = [value CGPointValue];
CGContextStrokeRect(context, CGRectMake(point.x - scaledPointSize / 2,
point.y - scaledPointSize / 2,
scaledPointSize,
scaledPointSize));
}
// lines
[[UIColor redColor] setStroke];
for (NSUInteger i = 0; i < points.count; i++) {
CGPoint point = [points[i] CGPointValue];
if (i == 0) {
CGContextMoveToPoint(context, point.x, point.y);
} else {
CGContextAddLineToPoint(context, point.x, point.y);
}
}
CGContextClosePath(context);
CGContextStrokePath(context);
// text
NSAttributedString *str = [[NSAttributedString alloc] initWithString:[NSString stringWithFormat:#"%f", _zoomScale] attributes:#{NSFontAttributeName : [UIFont systemFontOfSize:12]}];
[str drawAtPoint:CGPointMake(5, 5)];
}
- (void)updateWithZoomScale:(CGFloat)zoomScale
{
_zoomScale = zoomScale;
[self setNeedsDisplay];
[CATransaction begin];
[CATransaction setValue:[NSNumber numberWithBool:YES]
forKey:kCATransactionDisableActions];
self.layer.contentsScale = zoomScale;
[CATransaction commit];
}
You may be better off putting your boxes and line-shape on CAShapeLayers, where you can update the line-width based on the zoom scale.
You only need to create and define your line-shape once. For your boxes, though, you'll need to re-create the path when you change the zoom (to keep the width/height of the boxes at a constant non-zoomed point size.
Give this a try. You should be able to simply replace your current ZoomingView.m class - no changes to the view controller necessary.
//
// ZoomingView.m
//
// modified by Don Mag
//
#import "ZoomingView.h"
#interface ZoomingView()
#property (strong, nonatomic) CAShapeLayer *shapeLayer;
#property (strong, nonatomic) CAShapeLayer *boxesLayer;
#property (strong, nonatomic) NSArray *points;
#end
#implementation ZoomingView
{
CGFloat _zoomScale;
CGFloat _kPointSize;
}
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self setup];
}
return self;
}
- (void)setup
{
self.backgroundColor = [UIColor orangeColor];
_points = #[[NSValue valueWithCGPoint:CGPointMake(30, 30)],
[NSValue valueWithCGPoint:CGPointMake(200, 40)],
[NSValue valueWithCGPoint:CGPointMake(180, 200)],
[NSValue valueWithCGPoint:CGPointMake(70, 180)]];
_zoomScale = 1;
_kPointSize = 10.0;
// create and setup boxes layer
_boxesLayer = [CAShapeLayer new];
[self.layer addSublayer:_boxesLayer];
_boxesLayer.strokeColor = [UIColor redColor].CGColor;
_boxesLayer.fillColor = [UIColor clearColor].CGColor;
_boxesLayer.lineWidth = 1.0;
_boxesLayer.frame = self.bounds;
// create and setup shape layer
_shapeLayer = [CAShapeLayer new];
[self.layer addSublayer:_shapeLayer];
_shapeLayer.strokeColor = [UIColor greenColor].CGColor;
_shapeLayer.fillColor = [UIColor clearColor].CGColor;
_shapeLayer.lineWidth = 1.0;
_shapeLayer.frame = self.bounds;
// new path for shape
UIBezierPath *thePath = [UIBezierPath new];
for (NSValue *value in _points) {
CGPoint point = [value CGPointValue];
if ([value isEqualToValue:_points.firstObject]) {
[thePath moveToPoint:point];
} else {
[thePath addLineToPoint:point];
}
}
[thePath closePath];
[_shapeLayer setPath:thePath.CGPath];
// trigger the boxes update
[self updateWithZoomScale:_zoomScale];
}
- (void)drawRect:(CGRect)rect
{
// text
NSAttributedString *str = [[NSAttributedString alloc] initWithString:[NSString stringWithFormat:#"%f", _zoomScale] attributes:#{NSFontAttributeName : [UIFont systemFontOfSize:12]}];
[str drawAtPoint:CGPointMake(5, 5)];
}
- (void)updateWithZoomScale:(CGFloat)zoomScale
{
_zoomScale = zoomScale;
CGFloat scaledPointSize = _kPointSize * (1.0 / zoomScale);
// create a path for the boxes
// needs to be done here, because the width/height of the boxes
// must change with the scale
UIBezierPath *thePath = [UIBezierPath new];
for (NSValue *value in _points) {
CGPoint point = [value CGPointValue];
CGRect r = CGRectMake(point.x - scaledPointSize / 2.0,
point.y - scaledPointSize / 2.0,
scaledPointSize,
scaledPointSize);
[thePath appendPath:[UIBezierPath bezierPathWithRect:r]];
}
[_boxesLayer setPath:thePath.CGPath];
_boxesLayer.lineWidth = 1.0 / zoomScale;
_shapeLayer.lineWidth = 1.0 / zoomScale;
[self setNeedsDisplay];
}
#end
Results:
Note: Should go without saying, but... This is intended to be a starting point for you to work with, not "production code."

How to create buttons around circle with some radius

i try to create programmatically a number of buttons. I what that buttons will created around the circle like on image 1. Now i have some results, i can rotate buttons, but don't know how to set offset between them. Now i have this result: image 2 Here is my code. And thanks for any help!
viewDidload in ViewController
CGPoint point = self.view.center;
NSArray *arrayWithImages = #[#"red", #"pink", #"green", #"blue", #"purple", #"orange", #"yellow"];
NSArray *arrayWithAngle = #[#(0), #(M_PI / 3.8f), #(M_PI / 1.8), #(M_PI / -6), #(M_PI / 6), #(M_PI / -1.8), #(M_PI / -3.8)];
NSMutableArray *arrayWithPlacePoint = [self generatePointForRound:point andAngle:arrayWithAngle andRadius:25.0f];
NSLog(#"array with place point = %#", arrayWithPlacePoint);
for (NSInteger i = 0; i < [arrayWithImages count]; i++) {
PetalObject *petal = [[PetalObject alloc] initWithImageName:arrayWithImages andRotation:arrayWithAngle andIndex:i andPointsArray:arrayWithPlacePoint andCentrPoint:point];
[self.view addSubview:petal.petalButton];
}
I try to generate some x,y point which should be a creation point of each new button
- (NSMutableArray *)generatePointForRound:(CGPoint )centrPoint andAngle:(NSArray *)arrayAngle andRadius:(float)radiusValue {
CGPoint currentPoint;
NSMutableArray *array = [[NSMutableArray alloc] init];
for (int i = 0; i < [arrayAngle count]; i++) {
CGFloat x1 = centrPoint.x + (radiusValue * sin([[arrayAngle objectAtIndex:i] doubleValue]));
CGFloat y1 = centrPoint.y + (radiusValue * cos([[arrayAngle objectAtIndex:i] doubleValue]));
currentPoint = CGPointMake(x1, y1);
[array addObject:[NSValue valueWithCGPoint:currentPoint]];
}
return array;
}
And here is my PetalObject in which i create my buttons
-(id)initWithImageName:(NSArray *)imageNameArray andRotation:(NSArray *)angleArray andIndex:(NSInteger )index andPointsArray:(NSMutableArray *)pointsArray andCentrPoint:(CGPoint )centerPoint {
self.isRecorded = NO;
self.isComplited = NO;
UIImage *image = [UIImage imageNamed:imageNameArray[index]];
CGPoint point = [[pointsArray objectAtIndex:index] CGPointValue];
self.petalButton = [[UIButton alloc] initWithFrame:CGRectMake(point.x - 43 , point.y - 180, 86, 168)];
//self.petalButton.transform = CGAffineTransformMake(cos(angle),sin(angle),-sin(angle),cos(angle),boundsPoint.x-boundsPoint.x*cos(angle)+boundsPoint.y*sin(angle),boundsPoint.y-boundsPoint.x*sin(angle)-boundsPoint.y*cos(angle));
[self.petalButton setBackgroundImage:image forState:UIControlStateNormal];
CGFloat angle = [[angleArray objectAtIndex:index] floatValue];
[self.petalButton setTransform:CGAffineTransformMakeRotation(angle)];
return self;
}
Image 1:
Image 2:
Screenshot
And code:
- (void)viewDidLoad {
[super viewDidLoad];
for (NSInteger index = 0; index < 6; index ++) {
UIButton * button = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 50, 20)];
button.layer.anchorPoint = CGPointMake(-0.5, 0.5);
button.backgroundColor = [UIColor greenColor];
button.center = CGPointMake(self.view.center.x + CGRectGetWidth(button.frame) / 2.0, self.view.center.y);
button.transform = CGAffineTransformMakeRotation(M_PI * 2 * index / 6.0);
[self.view addSubview:button];
}
}
The key point is anchorPoint, you set all the button with same anchorPoint and frame, then apply different transform.

How to resize all subviews/sublayers when superview is resized?

This question relates to the project: https://github.com/FindForm/FFAngularPointilism
Currently, all the behavior is dictated by the dimensions of the original UIImage. Despite resizing, shrinking, and rotating, the drawing of the "double triangle squares" remains the same. The triangular blur is unchanged regardless of the bounds of the UIImageView to which it is applied.
In my UIImageView subclass, I initialize with a UIImage and call the following in the designated initializer:
- (void)loadMatrix
{
num = 12;
CGFloat width = self.bounds.size.width;
CGFloat height = self.bounds.size.height;
CGFloat theBiggestSideLength = MAX(width, height);
_array = [NSMutableArray array];
_array2 = [NSMutableArray array];
for(int i = 0; i < theBiggestSideLength; i += num)
{
[self.array addObject:[NSMutableArray array]];
[self.array2 addObject:[NSMutableArray array]];
}
for(int i = 0; i < self.array.count; i++)
{
for(int j = 0; j < self.array.count; j++)
{
[self.array[i] addObject:[self getRGBAsFromImage:self.image
atX:j * 2 * num
andY:i * 2 * num]];
int xIndex = ((j * 2 * num) - (num / 2.0));
int yIndex = ((i * 2 * num) + (num / 2.0));
xIndex %= (int)width * 2;
yIndex %= (int)width * 2;
NSLog(#"%d", xIndex);
[self.array2[i] addObject:[self getRGBAsFromImage:self.image
atX:xIndex
andY:yIndex]];
}
}
_imageGrayscaleView = [[UIImageView alloc] initWithImage:[self convertToGreyscale:self.image]];
_finalbwimage = self.imageGrayscaleView.image;
_imageGrayscaleView.frame = self.bounds;
[self insertSubview:_imageGrayscaleView atIndex:0];
_viewMask = [[UIView alloc] initWithFrame:self.frame];
_viewMask.center = CGPointMake(_viewMask.center.x,
_viewMask.center.y + _viewMask.frame.size.height / 2.0f);
_shapeLayer = [[CAShapeLayer alloc] init];
CGRect maskRect = CGRectZero;
CGPathRef path = CGPathCreateWithRect(maskRect, NULL);
_shapeLayer.path = path;
CGPathRelease(path);
self.imageGrayscaleView.layer.mask = _shapeLayer;
}
To draw these tiles, I add a timer and call the following method each interval. Num and row are updating as expected.:
- (void)drawTile
{
UIView *view = [[UIView alloc] initWithFrame:CGRectMake(pixel * num, row * num, num, num)];
view.backgroundColor = [self.array[row] objectAtIndex:pixel];
[self addSubview:view];
[self.ksubviews addObject:view];
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(view.frame.origin.x , view.frame.origin.y + (view.frame.size.height))];
[path addLineToPoint:CGPointMake(view.frame.origin.x , view.frame.origin.y)];
[path addLineToPoint:CGPointMake(view.frame.origin.x + view.frame.size.width, view.frame.origin.y + view.frame.size.height)];
[path closePath];
CAShapeLayer *shapeLayer = [CAShapeLayer layer];
shapeLayer.path = path.CGPath;
shapeLayer.strokeColor = [[UIColor blackColor] CGColor];
shapeLayer.fillColor = [((UIColor *)[self.array2[row] objectAtIndex:pixel]) CGColor];
shapeLayer.lineWidth = 0;
[self.layer addSublayer:shapeLayer];
[self.ksublayers addObject:shapeLayer];
}
The following is the correct behavior:
When this image is resized via the nib, or its bounds set programmatically, the animatable triangular squares are unchanged.
You make all the subviews changes on the method Draw Rect inside your class, and every time you make a change, to the superview, you call the method setNeedDisplay.
I didn't understand you question very well, but i hope this helps!

iOS drag shape pinned to center

Taking a next step from this post Drag UIView around Shape Comprised of CGMutablePaths , I am trying to add a line which is on one end pinned to the center of the pathLayer_ and the other end gets dragged along with the circular object (circleLayer in handleView_) on the path (figure-8).
The line has its own layer andview and path initiated in (void)initHandleView.
First: I am not able to get the line to go through the center of the pathLayer_:
- (void)viewDidLoad {
[super viewDidLoad];
[self initPathLayer];
[self initHandleView];
[self initHandlePanGestureRecognizer];
}
- (void)initPathLayer {
pathLayer_ = [CAShapeLayer layer];
pathLayer_.lineWidth = 1;
pathLayer_.fillColor = nil;
pathLayer_.strokeColor = [UIColor lightGrayColor].CGColor;
pathLayer_.lineCap = kCALineCapButt;
pathLayer_.lineJoin = kCALineJoinRound;
pathLayer_.frame = self.view.bounds;
pathLayerCenter_ = CGPointMake(CGRectGetMidX(pathLayer_.frame), CGRectGetMidY(pathLayer_.frame));
[self.view.layer addSublayer:pathLayer_];
}
- (void)initHandleView {
handlePathPointIndex_ = 0;
CGRect rect = CGRectMake(0, 0, 30, 30);
CAShapeLayer *circleLayer = [CAShapeLayer layer];
circleLayer.fillColor = [UIColor redColor].CGColor;
circleLayer.strokeColor = [UIColor redColor].CGColor;
circleLayer.lineWidth = 2;
UIBezierPath *circlePath = [UIBezierPath bezierPathWithOvalInRect:CGRectInset(rect, circleLayer.lineWidth, circleLayer.lineWidth)];
circleLayer.frame = rect;
circleLayer.path = circlePath.CGPath;
handleView_ = [[UIView alloc] initWithFrame:rect];
CGRect lineRect = CGRectMake(0,0,CGRectGetMaxX([[UIScreen mainScreen] bounds]), CGRectGetMaxY([[UIScreen mainScreen] bounds]));
CGPoint center = CGPointMake(CGRectGetMidX(pathLayer_.frame), CGRectGetMidY(pathLayer_.frame));
lineView_ = [[UIView alloc] initWithFrame:lineRect];
CAShapeLayer *lineLayer = [CAShapeLayer layer];
lineLayer.fillColor = [UIColor blueColor].CGColor;
lineLayer.strokeColor = [UIColor blueColor].CGColor;
lineLayer.lineWidth = 2;
lineLayer.frame = self.view.bounds;
UIBezierPath *linePath = [UIBezierPath bezierPath];
CGPoint circlePoint = CGPointMake(handleView_.center.x, handleView_.center.y);
[linePath moveToPoint:center]; //Center
[linePath addLineToPoint:circlePoint];
[linePath moveToPoint:circlePoint]; //Handle
lineLayer.path = linePath.CGPath;
[handleView_.layer insertSublayer:circleLayer above:handleView_.layer];
[handleView_.layer insertSublayer:lineLayer above:handleView_.layer];
// handleView_.layer.masksToBounds = YES;
float direction = DEGREES_TO_RADIANS(10);
CGAffineTransform rotationTransform = CGAffineTransformMakeRotation(direction);
[handleView_ setTransform:rotationTransform];
[self.view addSubview:lineView_];
[self.view addSubview:handleView_];
}
And second: I am not sure I do the right thing with the PanGesturerecognizer:
- (void)handleWasPanned:(UIPanGestureRecognizer *)recognizer {
switch (recognizer.state) {
case UIGestureRecognizerStateBegan: {
desiredHandleCenter_ = handleView_.center;
break;
}
case UIGestureRecognizerStateChanged:
case UIGestureRecognizerStateEnded:
case UIGestureRecognizerStateCancelled: {
CGPoint translation = [recognizer translationInView:self.view];
desiredHandleCenter_.x += translation.x;
desiredHandleCenter_.y += translation.y;
[self moveHandleTowardPointAndRotateLine:desiredHandleCenter_];
break;
}
default:
break;
}
[recognizer setTranslation:CGPointZero inView:self.view];
}
- (void)moveHandleTowardPointAndRotateLine:(CGPoint)point {
CGFloat earlierDistance = [self distanceToPoint:point ifHandleMovesByOffset:-1];
CGFloat currentDistance = [self distanceToPoint:point ifHandleMovesByOffset:0];
CGFloat laterDistance = [self distanceToPoint:point ifHandleMovesByOffset:1];
if (currentDistance <= earlierDistance && currentDistance <= laterDistance)
return;
NSInteger step;
CGFloat distance;
if (earlierDistance < laterDistance) {
step = -1;
distance = earlierDistance;
} else {
step = 1;
distance = laterDistance;
}
NSInteger offset = step;
while (true) {
NSInteger nextOffset = offset + step;
CGFloat nextDistance = [self distanceToPoint:point ifHandleMovesByOffset:nextOffset];
if (nextDistance >= distance)
break;
distance = nextDistance;
offset = nextOffset;
}
handlePathPointIndex_ += offset;
// Make one end of the line move with handle (point) while the other is pinned to center of pathLayer_ (ie. in figure8 its the cross point, in a circle it's center)
//CGFloat rot = [self getTouchAngle:point];
CGFloat rot = atan2f((point.x - pathLayerCenter_.x), -(point.y - pathLayerCenter_.y));
handleView_.layer.transform = CATransform3DMakeRotation(rot, 0., 0., 1.);
[self layoutHandleView];
}
I'm just going to ignore your code and tell you how I'd modify my original answer to add the line you want.
First, I'm going to use a dedicated shape layer for the line, so I'll add an instance variable to reference the line layer:
#implementation ViewController {
CAShapeLayer *lineLayer_; // NEW!!!
UIBezierPath *path_;
CAShapeLayer *pathLayer_;
etc.
I need to initialize the line layer when I load the view:
- (void)viewDidLoad {
[super viewDidLoad];
[self initPathLayer];
[self initHandleView];
[self initHandlePanGestureRecognizer];
[self initLineLayer]; // NEW!!!
}
- (void)initLineLayer {
lineLayer_ = [CAShapeLayer layer];
lineLayer_.lineWidth = 1;
lineLayer_.fillColor = nil;
lineLayer_.strokeColor = [UIColor redColor].CGColor;
lineLayer_.lineCap = kCALineCapRound;
lineLayer_.lineJoin = kCALineJoinRound;
[self.view.layer addSublayer:lineLayer_];
}
I need to lay out the line layer when I lay out my top-level view (when it's first shown and on rotations):
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
[self createPath];
[self createPathPoints];
[self layoutPathLayer];
[self layoutHandleView];
[self layoutLineLayer]; // NEW!!!
}
- (void)layoutLineLayer {
lineLayer_.frame = self.view.bounds;
CGRect bounds = self.view.bounds;
CGPoint start = CGPointMake(CGRectGetMidX(bounds), CGRectGetMidY(bounds));
CGPoint end = pathPoints_[handlePathPointIndex_];
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:start];
[path addLineToPoint:end];
lineLayer_.path = path.CGPath;
}
Finally, I need to lay out the line layer again (to update its path) whenever I update the handle:
- (void)moveHandleTowardPoint:(CGPoint)point {
// I have omitted most of this method body for brevity.
// Just copy it from the original answer.
...
...
handlePathPointIndex_ += offset;
[self layoutHandleView];
[self layoutLineLayer]; // NEW!!!
}
Result:

Animating image on event

I have an image with circle inside circle approx 10 circles,each of different color i want to show them one by one on button click from center to outside and on another button click disappear one by one from outer to inner. Is this possible and how?
Please suggest
You can get help from below code i m not wrong and you are looking for radar style animation
#import "RadarAnimation.h"
#implementation RadarAnimation
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self SetUPAnimation];
// Initialization code
}
return self;
}
-(void)SetUPAnimation
{
[self AddRader];
double delayInSeconds = 1.5;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
[self AddRader];
});
}
-(void)AddRader
{
UIView *view = [[UIView alloc] init];
// view.backgroundColor = [UIColor blueColor];
view.frame=self.frame;
[self setWidth:[Helper currentWidth] ofView:view];
[self setHeight:[Helper currentWidth] ofView:view];
[self setXPossitionto:self.frame.size.width/2-view.frame.size.width/2 ofView:view];
[self setYPossitionto:self.frame.size.height/2-view.frame.size.height/2 ofView:view];
view.layer.cornerRadius = CGRectGetHeight(view.frame)/2;
view.tag=175751;
int kRingExpansionTime=6.0;
CABasicAnimation *scaleAnimation = [CABasicAnimation animationWithKeyPath:#"transform.scale"];
scaleAnimation.duration = kRingExpansionTime;
scaleAnimation.repeatCount = HUGE_VAL;
scaleAnimation.autoreverses = YES;
scaleAnimation.fromValue = [NSNumber numberWithFloat:0.0];
scaleAnimation.toValue = [NSNumber numberWithFloat:1.8];
[view.layer addAnimation:scaleAnimation forKey:#"scale"];
// fade out the ring part way thru the animation
CABasicAnimation* alphaAnim = [CABasicAnimation animationWithKeyPath:#"opacity"];
alphaAnim.fromValue = [NSNumber numberWithFloat:0.3];
alphaAnim.toValue = [NSNumber numberWithFloat:0.1];
alphaAnim.beginTime = kRingExpansionTime * 0.7f; // start part way thru
alphaAnim.duration = kRingExpansionTime - alphaAnim.beginTime;
CAAnimationGroup* group = [CAAnimationGroup animation];
group.duration = kRingExpansionTime;
group.repeatCount = HUGE_VALF; // repeat forever
group.animations = [NSArray arrayWithObjects:scaleAnimation,alphaAnim, nil];
[view.layer addAnimation:group forKey:nil];
view.backgroundColor = [UIColor colorWithRed:0/255.0 green:135.0/255.0 blue:219.0/255.0 alpha:0.5];
view.alpha=0.4;
view.layer.borderColor = [UIColor colorWithRed:0/255.0 green:135.0/255.0 blue:255.0/255.0 alpha:1.0].CGColor;
view.layer.borderWidth = 2.0;
//view.center=_VwEmptyplaceholder.center;
//view.center=CGPointMake(_VwEmptyplaceholder.frame.size.width/2, _VwEmptyplaceholder.frame.size.height/2);
view.userInteractionEnabled=NO;
view.tag=175751;
//[self insertSubview:view belowSubview:btn];
}
-(void)setXPossitionto:(CGFloat)xpostion ofView:(UIView *)view
{
CGRect frame = view.frame;
frame.origin.x = xpostion;
view.frame=frame;
}
-(void)setYPossitionto:(CGFloat)ypostion ofView:(UIView *)view
{
CGRect frame = view.frame;
frame.origin.y = ypostion;
view.frame=frame;
}
-(void)setWidth:(CGFloat)Width ofView:(UIView *)view
{
CGRect frame = view.frame;
frame.size.width = Width;
view.frame=frame;
}
-(void)setHeight:(CGFloat)Height ofView:(UIView *)view
{
CGRect frame = view.frame;
frame.size.height = Height;
view.frame=frame;
}
/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect
{
// Drawing code
}
*/
#end

Resources