IOS (6)

The matrix transformation for the following animation

[CATransaction begin]; CGFloat belowGap=50; CGFloat startAngle = atan (belowGap/(_cview. Frame. The size, width / 2.0)) + 20 * M_PI / 180; CGFloat totalAngle=M_PI-2*startAngle; CGFloat gapAngle=totalAngle/(arr.count-1); for(int i=0; i<arr.count; i++){ UIView* item=arr[i]; CGFloat angle=-M_PI/2+startAngle+gapAngle*i; CGFloat Dis = TESTFanViewController_Item_Height - 0.1 belowGap TESTFanViewController_VisibleHeigh - TESTFanViewController_Item_Height *;  CAKeyframeAnimation* keyAnim=[CAKeyframeAnimation animationWithKeyPath:@"transform"]; KeyAnim. Duration = 0.3; CATransform3D endTransform; If (CATransform3DEqualToTransform (item. Layer. The transform, CATransform3DIdentity)) {# if 0 / / the first transformation, a complex CGFloat Len = item. Layer. The position. X-ray _cview. Bounds. Size. The width / 2.0 f; CGFloat side1=len/tan(angle)+dis; CGFloat side2=len/sin(angle); CATransform3D transform1=CATransform3DMakeTranslation(0, -side1, 0); CATransform3D transform2=CATransform3DRotate(transform1, angle, 0, 0, 1); CATransform3D transform3 = CATransform3DTranslate (side2 transform2, 0, 0); # else / / the second transformation, simpler CGFloat len = _cview. Bounds. The size, width / 2.0 f - item. Layer. Position. X. CATransform3D transform1=CATransform3DMakeTranslation(0, -dis, 0); CATransform3D transform2=CATransform3DTranslate(transform1,len,0, 0); CATransform3D transform3=CATransform3DRotate(transform2, angle, 0, 0, 1); #endif keyAnim.values=@[ [NSValue valueWithCATransform3D:CATransform3DIdentity], [NSValue valueWithCATransform3D:transform3] ]; endTransform=transform3; } else{ keyAnim.values=@[ [NSValue valueWithCATransform3D:item.layer.transform], [NSValue valueWithCATransform3D:CATransform3DIdentity] ]; endTransform=CATransform3DIdentity; } [item.layer addAnimation:keyAnim forKey:@"transform"]; item.layer.transform=endTransform; } [CATransaction commit];Copy the code

The first: move anchorPoint up -> rotate -> down

The second is simpler: move the anchorPoint into position and then rotate it

About different views starting different animations at the same time

[CATransaction begin]; [CATransaction disableActions]; // Your animation code [CATransaction commit];Copy the code

Of course, if you want to add different animations to the same view, you can use CAAnimationGroup

On the drawing direction of arc

If the current context is directly below the UIKit coordinate / / set the clockwise to YES UIBezierPath * path1 = [UIBezierPath bezierPathWithArcCenter: center radius:radius startAngle:0 endAngle:angle clockwise:YES];Copy the code

// This needs to be set to NO, since after all the frame has been flipped relative to the Qutarz2D frame. CGContextAddArc(CTX, center.x, center.y,radius, Angle, Angle *2.0, NO);


CAKeyframeAnimation and timingFunction

The time function in CAKeyFrameAnimation has no effect

CAKeyframeAnimation ignores timingFunction (you need to use keyTimes). If you prefer to just using aCAMediaTimingFunction you could add the CAKeyframeAnimation to a CAAnimationGroup. The timing function of the CAKeyframeAnimation will take precedence over the keyframe animation’s timings.


Understanding matrix transformation

- (void)viewDidLoad { [super viewDidLoad]; //create a new transform CGAffineTransform transform = CGAffineTransformIdentity; Transform = CGAffineTransformScale(transform, 0.5, 0.5); //scale by 50% transform = CGAffineTransformRotate(transform, M_PI / 180.0 * 30.0); //rotate by 30 degrees transform = CGAffineTransformTranslate(transform, 200, 0); //translate by 200 points //apply transform to layer self.layerView.layer.affineTransform = transform; }Copy the code

We can understand the transformation, first shrink the coordinate system by 50%, then rotate the whole coordinate system by another 30 degrees, and then move the whole coordinate system to the right by another 200 degrees, notice that the direction and scale of the shift have changed. Because of the transformation of the first two steps, the orientation of the coordinate system and the scale have changed, so the square is going to move down, and it’s only moved 100.

The image has been shifted to the right, but not that far (200 pixels), and it has been shifted slightly downward. The reason is that when you do the transformation in order, the result of the last transformation will affect the subsequent transformation, so the 200 pixel shift to the right is also rotated by 30 degrees, which is 50% smaller, so it’s actually moved 100 pixels diagonally.

This means that the order of transformation affects the final result, meaning that translation after rotation may be different from rotation after rotation.


UIKit to Qutarz2D matrix transformation

/ / the UIKit coordinate system transformation into Quartz2D CGAffineTransform flip = CGAffineTransformMakeScale (1.0, 1.0); CGAffineTransform flipThenShift = CGAffineTransformTranslate(flip,0,-inputHeight); CGContextConcatCTM(context, flipThenShift); / / will Qutarz2D coordinate system transformation into UIKit CGAffineTransform flip = CGAffineTransformMakeScale (1.0, 1.0); CGAffineTransform flipThenShift = CGAffineTransformTranslate(flip,0,-inputHeight); CGContextConcatCTM(context, flipThenShift); If you want a point to be in the same absolute position when you transform the matrix, and you transform the coordinate system, and you apply the same matrix transformation to that point, then this. I'm going to take this point and convert it to another point in the same frame.Copy the code

Matrix transformation does not affect CALayer postion and UIView Center

Matrix transformations do not affect CALayer’s postion and UIView’s center, such as the title.

If you want to calculate it, you have to calculate it on a case-by-case basis.


The calculation of the rotation Angle in the following effect

First, calculate the matrix transformation of rotation by the following method:

The general idea is to transform the view matrix to get the different positions of the view, and then use those positions as keyframes for the keyframe animation. The following code implements the upward motion:

// Adjust anchorPoint and position at the same time so that the position does not change. _tipLabel.layer.position=CGPointMake(_tipLabel.frame.origin.x+_tipLabel.frame.size.width, _tipLabel.layer.position.y); _tipLabel. Layer. AnchorPoint = CGPointMake (1, 0.5); CGFloat dis = ((_txtField. Frame. The size, height - _tipLabel. Frame. The size, height) / 2.0 f + _tipLabel frame. The size, height / 2.0 f); _dis=dis; / / calculate the rotation Angle CGFloat Angle = asin (dis / _tipLabel. Frame. The size, width); _angle=angle; CATransform3D trans1=CATransform3DMakeRotation(angle,0, 0, 1); CATransform3D trans2=CATransform3DTranslate(CATransform3DIdentity,0 , -dis, 0); CAKeyframeAnimation* keyAnim=[CAKeyframeAnimation animationWithKeyPath:@"transform"]; [keyAnim setValues:@[ [NSValue valueWithCATransform3D:CATransform3DIdentity], [NSValue valueWithCATransform3D:trans1], [NSValue valueWithCATransform3D:trans2] ]]; KeyAnim. Duration = 0.3 f; keyAnim.calculationMode=kCAAnimationCubic; keyAnim.removedOnCompletion=NO; keyAnim.fillMode=kCAFillModeForwards; If you want to control the speed of the animation, you can only control the time effect of the animation by setting a number of keyframes, which control the data changes between the keyframes. keyAnim.timingFunction=[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; _tipLabel.layer.transform=trans2; [_tipLabel.layer addAnimation:keyAnim forKey:@"transform"]; _txtField.layer.borderColor=[UIColor redColor].CGColor;Copy the code

On the calculation of matrix transformation

For matrix transformation, scale transformation can be interpreted as scaling to the current coordinate system, rotation transformation can be interpreted as rotation to the current coordinate system, and translation transformation can be interpreted as translation to the current coordinate system. Notice it’s in the current coordinate system, so if I rotate and then I shift, then I shift in the direction of the rotated coordinate.

CGFloat thirtyDegrees = 30.0 * M_PI / 180.0; CGFloat distanceToRotationPoint = _av.bounds.size.width; CGAffineTransform rotation1=CGAffineTransformIdentity; rotation1=CGAffineTransformTranslate(rotation1, -distanceToRotationPoint, 0); rotation1=CGAffineTransformRotate(rotation1, -thirtyDegrees); rotation1=CGAffineTransformTranslate(rotation1, distanceToRotationPoint, 0); _av.transform=rotation1; NSLog(@"av:%@",NSStringFromCGPoint(_av.center)); NSLog(@"av_frame:%@",NSStringFromCGRect(_av.frame)); NSLog(@"outer:%@",NSStringFromCGPoint(_outer.center)); NSLog(@"outer_frame:%@",NSStringFromCGRect(_outer.frame)); CGPoint center = CGPointMake (_av. Frame. Origin. X + _av. Frame. The size, width / 2.0 f, _av. Frame. The origin, y + _av. Frame. The size, height / 2.0 f); NSLog(@"hyper:%lf",hypot(_outer.center. X-center. X, _outer.center. CGFloat sideLen=distanceToRotationPoint; NSLog(@"cos:%lf", SQRT (2*sideLen*sideLen*(1-cos(thirtyDegrees))));Copy the code

Output:

The 2015-12-26 17:04:21. 024 TEST_AnchorP_Position [93406-2133797] av: {156, 329} 2015-12-26 17:04:21.026 TEST_AnchorP_Position[93406:2133797] av_frame:{{-12.07695154586736, 93.574374157795958}, {271.84609690826528, 230.8512516440817}} 2015-12-26 17:04:21.027 TEST_AnchorP_Position[93406:2133797] outer:{271.84609690826528, 230.8512516440817}} 2015-12-26 17:04:21.027 329} 2015-12-26 17:04:21.027 TEST_AnchorP_Position[93406:2133797] outer_frame:{{36, 265}, {240, 128}} 2015-12-26 17:04:21.028 TEST_AnchorP_Position[93406:2133797] hyper:124.233142 2015-12-26 17:04:21.028 TEST_AnchorP_Position [93406-2133797] cos: 124.233142Copy the code

Short for animation properties

Note that before using the following properties, add a Transform, such as transform.rotation. X

About the width distribution of the drawn line segment

In the figure above, the radius of the white circle is 100, and the width of the line segment is 100, so half of the segment is inside the white circle and half is outside the white circle, so it looks like the radius of the white circle is only 50. In other words, half of the width of the line segment is distributed inside and half outside its drawing position

CGRect rect=CGRectMake(0, 0, 200, 200); _circleLayer.frame=rect; _circleLayer.position=self.view.center; _centerPoint=CGPointMake(100, 100); [self.view.layer addSublayer:_circleLayer]; _circleLayer.contents=(__bridge id)[self getImage:_circleLayer]; CGPoint p=CGPointMake(100, 150); UIBezierPath* bPath=[UIBezierPath bezierPathWithArcCenter:p radius:100 startAngle:0 endAngle:2*M_PI clockwise:YES]; _shaperLayer=[CAShapeLayer layer]; _shaperLayer.path=bPath.CGPath; _shaperLayer.lineWidth=100; _shaperLayer.strokeColor=[UIColor blackColor].CGColor; _shaperLayer.strokeStart=0; _shaperLayer.strokeEnd=0; _shaperLayer.fillColor=[UIColor clearColor].CGColor; CABasicAnimation* anim=[CABasicAnimation animationWithKeyPath:@"strokeEnd"]; anim.removedOnCompletion=NO; anim.fillMode=kCAFillModeForwards; Anim. ByValue = @ 1.0; anim.duration=8.f; anim.timingFunction=[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; _shaperLayer.frame=rect; _circleLayer.mask=_shaperLayer; [_shaperLayer addAnimation:anim forKey:@"strokeEnd"];Copy the code

End callback for explicit animation

When using implicit animation, we can detect the completion of the animation in the CATransaction completion block. However, this approach does not apply to explicit animation, where the animation has little to do with transactions.

So to know when an explicit animation ends, we need to use a delegate that implements the CAAnimationDelegate protocol.

The CAAnimationDelegate is not found in any header file, but the related functions can be found in the CAAnimation header file or in the Apple developer documentation. In this example, we use – animationDidStop: finished: method to update at the end of the animation layer backgroundColor.

To expand on the spinning spaceship example, add a button to stop or start the animation. This time we use a non-nil value as the animation key so we can remove it later. – animationDidStop: finished: flag parameters of the method shows that the animation is a natural end or is interrupted, we can print in the console. If you terminate the animation with the stop button, it prints NO, and if you allow it to finish, it prints YES. Ref :[008] Explicit animation


Override implicit animation

To avoid this, save the original model value before changing it and explicitly set the animation’s fromValue:

// Save the original value CGFloat originalY = layer.position.y; // Change the model value layer.position = CGPointMake(layer.position.x, 300.0); CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position.y"]; // Now specify the fromValue for the animation because // the current model value is already the correct toValue animation.fromValue = @(originalY); Animation. Duration = 1.0; // Use the name of the animated property as key // to override the implicit animation [layer addAnimation:animation forKey:@"position"];Copy the code

Override the Implicit Animation

Note a third modification I made to the code: The key passed to the -addAnimation:forKey: method is usually an arbitrary string (or even nil) you can use to later identify a specific animation. Here, I used the name of the property our animation changes (@”position”) for the key argument. This makes sure that any implicit animation that has been created by the modification of the model value gets overridden with our explicit animation. Had we used a different key for the explicit animation, it’s possible that Core Animation would have to deal with two conflicting animations at the same time

Ref :[005]Prevent Layers From being snapped Back to Original Values When Using Explicit CAAnimations – Ole Begemann


IOS uses Quartz 2D to draw dotted lines

IOS uses Quartz 2D to draw dotted lines – Bannings’ column – blog channel – CSDN.NET


draw NSString

// Create textAttributes NSDictionary *textAttributes = @{NSFontAttributeName: [UIFont systemFontOfSize:18.0]}; // Create string drawing context NSStringDrawingContext *drawingContext = [[NSStringDrawingContext alloc] init]; DrawingContext. MinimumScaleFactor = 0.5; // Half the font size CGRect drawRect = CGRectMake(0.0, 0.0, 200.0, 100.0); [string drawWithRect:drawRect options:NSStringDrawingUsesLineFragmentOrigin attributes:textAttributes context:drawingContext]; NSFont *font = [NSFont fontWithName:@" palatino-Roman "size:14.0]; NSDictionary *attrsDictionary = [NSDictionary dictionaryWithObjectsAndKeys: The font, NSFontAttributeName, [NSNumber numberWithFloat: 1.0], NSBaselineOffsetAttributeName, nil];Copy the code

Effect of parent view’s Transform on child View

- (void)viewDidLoad { [super viewDidLoad]; NSLog(@"before transform....." ); NSLog(@"rect_child:%@",NSStringFromCGRect(_miview.frame)); NSLog(@"rect_parent:%@",NSStringFromCGRect(_mview.frame)); _mview. Transform = CGAffineTransformMakeScale (0.1, 0.1); NSLog(@"after transform....." ); NSLog(@"rect_child:%@",NSStringFromCGRect(_miview.frame)); NSLog(@"rect_parent:%@",NSStringFromCGRect(_mview.frame)); }Copy the code

After the transformation:

Output:

2015-12-18 14:21:37.979 TEST_CoreText1[97594:206517] before Transform..... TEST_CoreText1[97594:206517] rect_Child :{{39, 21}, {162, TEST_CoreText1[97594:206517] rect_parent:{{26, 213}, {240, TEST_CoreText1[97594:206517] after transform..... TEST_CoreText1[97594:206517] rect_Child :{{39, 21}, {162, 981 TEST_CoreText1[97594:206517] rect_parent:{{134, 270.6}, {24, 12.8}}Copy the code

Calculates the size of the rotated frame

NSLog(@"%@",NSStringFromCGRect(self.view.bounds));
UIBezierPath* b=[UIBezierPath bezierPathWithRect:self.view.bounds];
[b applyTransform:CGAffineTransformMakeRotation(M_PI_2)];
NSLog(@"%@",NSStringFromCGRect(CGPathGetBoundingBox(b.CGPath)));
Copy the code

Another way is to calculate directly:

Stackoverflow.com/questions/9…


Both UIView and CALayer frames are affected by transform

NSLog(@"%@",NSStringFromCGRect(_bview.layer.frame)); _bview. Layer. The transform = CATransform3DMakeRotation (M_PI f / 4.0, 0, 0, 1); NSLog(@"%@",NSStringFromCGRect(_bview.layer.frame)); NSLog(@"%@",NSStringFromCGRect(_bview.frame)); _bview. Transform = CGAffineTransformMakeRotation (M_PI / 4.0 f); NSLog(@"%@",NSStringFromCGRect(_bview.frame));Copy the code

About clockwise and counterclockwise in CGContextAddArc

UIGraphicsBeginImageContextWithOptions(layer.bounds.size, NO, [UIScreen mainScreen].scale); CGContextRef ctx=UIGraphicsGetCurrentContext(); int totalSperate=16; CGFloat startAngle=0; for(int i=0; i<totalSperate ; I++){UIColor* color=[UIColor colorWithRed:(arc4random()%255)/255.0 green:(arc4random()%255)/255.0 Blue: (arc4random () % 255)/alpha 255.0:1); NSLog(@"color:%@,angle:%lf",color,startAngle); CGContextBeginPath(ctx); CGContextMoveToPoint(ctx, _centerPoint.x, _centerPoint.y); // Notice that in the code below, since Ctx has been rotated, the clockwise indicated in the API below will become counterclockwise in the actual drawing. CGContextAddArc(ctx, _centerPoint.x, _centerPoint.y,100,startAngle,startAngle+M_PI*2/totalSperate, 0); CGContextSetFillColorWithColor(ctx, color.CGColor); CGContextClosePath(ctx); CGContextFillPath(ctx); startAngle+=M_PI*2/totalSperate; } CGImageRef temp=UIGraphicsGetImageFromCurrentImageContext().CGImage; UIGraphicsEndImageContext();Copy the code

The CABasicAnimation on CALayer has no frame animation

Since frame is a combination of bounds,positon, and Transform, to animate a frame on CALayer requires changing both bounds and frame.

So if I change the bounds separately, the layer won’t be fully displayed in the parent layer because the position hasn’t changed. Note: if I change the frame of the layer, the position will change to the corresponding value based on the anchorPoint,

If I just change the bounds of the layer, the value of position will not change. It was stated above that frame is a composite property, bounds is not.

-(void)didMoveToSuperview{ [super didMoveToSuperview]; CGFloat width=self.frame.size.width; CGFloat height=10; CALayer* layer=[CALayer layer]; layer.backgroundColor=[UIColor redColor].CGColor; [self.layer addSublayer:layer]; CABasicAnimation* a=[CABasicAnimation animationWithKeyPath:@"bounds"]; a.fromValue=[NSValue valueWithCGRect:CGRectMake(0, 0, 0, height)]; a.toValue=[NSValue valueWithCGRect:CGRectMake(0, 0,width , height)]; CABasicAnimation* b=[CABasicAnimation animationWithKeyPath:@"position"]; B. romValue = [NSValue valueWithCGPoint: CGPointMake (0, height / 2.0 f)]; B.t oValue = [NSValue valueWithCGPoint: CGPointMake (width / 2.0 f, height / 2.0 f)]; CAAnimationGroup * group =[CAAnimationGroup animation]; group.removedOnCompletion=NO; group.fillMode=kCAFillModeForwards; group.animations =[NSArray arrayWithObjects:a, b, nil]; Group. The duration = 0.7; [layer addAnimation:group forKey:@"frame"]; }Copy the code

Use UIGraphicsBeginImageContext Retain the screen
You need to use UIGraphicsBeginImageContextWithOptions instead of UIGraphicsBeginImageContext, so that you can specify the scale factor of the image. This will use the scale factor of the device’s main screen:

UIGraphicsBeginImageContextWithOptions(imageSize, NO, 0);

This will use the scale factor of the screen containing cellImage, if cellImage is on a screen:

UIGraphicsBeginImageContextWithOptions(imageSize, NO, cellImage.window.screen.scale);

This will hardcode the scale factor:

UIGraphicsBeginImageContextWithOptions(imageSize, NO, 2);


CALayer CATransform3D is

CATransform3D transform1 = CATransform3DIdentity; Transform1.m34 = -1/500.0; CATransform3D transform2 = CATransform3DTranslate(transform1, 0, height / 2-40, 0); transform2 = CATransform3DRotate(transform2, M_PI / 20, 1, 0, 0); transform2 = CATransform3DTranslate(transform2, 0, -height / 2, 0);Copy the code

Set the contentsScale of the CATextLayer to make it clear

CGFloat midx=CGRectGetMidX(self.bounds)-3; CGFloat midy=CGRectGetMidY(self.bounds)+3; CATextLayer *label = [[CATextLayer alloc] init]; [label setFontSize:10]; [label setFrame:CGRectMake(MIDx + MIDx / 2.0F-2, 0, 10, 10)]; [label setString: @ "today"]; label.contentsScale=[[UIScreen mainScreen] scale]; [label setAlignmentMode:kCAAlignmentCenter]; [label setForegroundColor:[[UIColor whiteColor] CGColor]]; [_infoLayer addSublayer:label];Copy the code

About using timeOffset in CAAnimation.

By controlling timeOffset then I can manipulate the animation to a particular state. Starting really simple, we create a basic animation for the background color of a layer and add it to that layer. We set the speed of the layer to 0 to pause the animation.

CABasicAnimation *changeColor = [CABasicAnimation animationWithKeyPath:@"backgroundColor"]; changeColor.fromValue = (id)[UIColor orangeColor].CGColor; changeColor.toValue = (id)[UIColor blueColor].CGColor; ChangeColor. Duration = 1.0; // For convenience [self.myLayer addAnimation:changeColor forKey:@"Change color"]; Self. MyLayer. Speed = 0.0; // Pause the animation Then in the action method for when the slider is dragged we set the current value of the slider (also configured to go from 0 to 1) as the time offset of the layer - (IBAction)sliderChanged:(UISlider *)sender { self.myLayer.timeOffset = sender.value; // Update "current time" }Copy the code

This gives the effect that as we drag the slider the current value of the animation changes and updates the background color of the layer.

With the above code, you can achieve the effect of changing the color as the slider moves.

ref:Ronnqvi. St/controlling…


What’s the difference between Quartz Core, Core Graphics and Quartz 2D?

Quartz 2D is an API of the Core Graphics framework that implements drawing. Quartz Core is a framework that includes APIs for animation and image processing.

Foundation: The core library of Objective-C. Write a library that an Objective-C program must include. Provides basic data types and services in Objective-C. He’s actually closer to Cocoa, and he should be at the same level as Cocoa. Core Foundation: A system-close C library that gives callers easy access to system-level content. Foundation and Cocoa have encapsulation of some of their functionality, which is why there are multiple solutions to do the same thing. Personally, I think it has more powerful functions than Foundation. After all, it is closer to the bottom and more efficient. And Foundation, as we’ll see later, in order to make good calls to Core Foundation, you also have to talk to Toll Free Bridging. Quartz frameworks and their APIs

CoreGraphics.framework

  • Quartz 2D API manages the graphic context and implements drawing.
  • Quartz Services API provides low level access to the window server. This includes display hardware, resolution, refresh rate, and others.

QuartzCore.framework

  • Core Animation: Objective-C API to do 2D animation.
  • Core Image: image and video processing (filters, warp, transitions).iOS 5

Quartz.frameworkOS X only

  • Image Kit: display and edit images.
  • PDF Kit: display and edit PDFs.
  • Quartz Composer: display Quartz Composer compositions.
  • QuickLookUI: preview media elements.

All three frameworks use OpenGL underneath because all drawing in iOS or OS X goes through OpenGL at some point. See the section Media Layer Frameworks of the Mac OS X Technology Overview for details. Other “Quartz” technologies you may have heard of:

  • Quartz Extreme: GPU acceleration for Quartz Composer.
  • QuartzGL (aka “Quartz 2D Extreme”): GPU acceleration for Quartz 2D.

These are internal implementations of GPU rendering, not APIs. They decide whether to create the window buffer in the CPU and only use OpenGL to upload as a texture (the default) or to do the whole rendering using OpenGL, which not always improves performance. You can alternate between the two using the QuartzGLEnable Info.plist setting. For an explanation see John Siracusa review of Mac OS X 10.4 Tiger, pages 13 and 14.

“Quartz” and “Core” are marketing names forefront over frameworks and APIs in a random manner. If they wanted to create a confusing naming mess, they succeeded.


The use of the UIBezierPath

To draw the above graph, you can use the following code:

UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:lowerLeftCorner];
[path addLineToPoint:beforeLeftArc];
[path addArcWithCenter:leftCircleCenter
                radius:smallRadius
            startAngle:straightLeftAngle
              endAngle:straightUpAngle
             clockwise:YES];
[path addLineToPoint:upperRightCorner];
[path addLineToPoint:beforeRightArc];
[path addArcWithCenter:rightCircleCenter
                radius:largeRadius
            startAngle:straightRightAngle
              endAngle:straightDownAngle
             clockwise:YES];
[path closePath];
Copy the code

It can also be reduced to:

UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:lowerLeftCorner];
[path addArcWithCenter:leftCircleCenter
                radius:smallRadius
            startAngle:straightLeftAngle
              endAngle:straightUpAngle
             clockwise:YES];
[path addLineToPoint:upperRightCorner];
[path addArcWithCenter:rightCircleCenter
                radius:largeRadius
            startAngle:straightRightAngle
              endAngle:straightDownAngle
             clockwise:YES];
[path closePath];
Copy the code

Ref :Thinking like a Bezier path


The use of UIBezierPath

1. When defining a new UIBezierPath object, the current point is undefined and you need to specify it explicitly with moveToPoint. When you add a path, the last point of the path automatically becomes current Point. 2. Call closePath to close the path by adding a line between the current point and the first point. When you call moveToPoint, you end the current subPath and start the new subPath. After defining the shape, you can use additional methods of this class to render the path in the current drawing context. When you create a new empty path object, the current point is undefined and must be set explicitly. After adding the segment, the end point of the new segment automatically becomes the current point. Calling the closePath method closes a subpath by adding a straight line segment from the current point to the first point in the subpath. Calling the moveToPoint: method ends the current subpath (without closing it) and sets the starting point of the next subpath. The subpaths of a Bezier path object share the same drawing attributes and must be manipulated as a group. To draw subpaths with different  attributes, you must put each subpath in its own UIBezierPath object. After configuring the geometry and attributes of a Bezier path, you draw the path in the current graphics context using the stroke and fill methods. In addition to using a Bezier path object to draw shapes, you can also use it to define a new clipping region.Copy the code

The operation order of IOS transform.

1. The matrix constructed by CGAffineTransform function is on the left, as follows:

* t '= CGAffineTransformTranslate (t, tx, ty) * results as follows: t' = [1 0 0 1 tx ty] * t * t '= CGAffineTransformTranslate (t, tx, ty ') * results as follows: t ' '= [1 0 0 1 tx' ty '] * [1 0 0 1 tx ty] * tCopy the code

CGContextRotateCTM is also transformed according to the above order of operations.

In other words, in IOS, the order in which the matrix is evaluated, combined on the left, let’s say I apply the B transformation to matrix A. So it’s going to be BA, and then applying the C transformation to that, it’s going to be CB times A

Note the differences with CGAffineTransformConcat:

CGAffineTransform t1 = CGAffineTransformMakeTranslation(0, 100);
CGAffineTransform t2 = CGAffineTransformMakeScale(.8, .8);
CGAffineTransform t3 = CGAffineTransformConcat(t2, t1);
Copy the code

The t3 = t2 * t1

2. Understanding of matrix transformation:


Coordinate system transformation on the system

For two methods in the framework:

- (void)drawRect:(CGRect)rect
-(void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx
Copy the code

The context in both of these methods is a coordinate transformation, UIKit coordinate system.

For the UIGraphicsBeginImageContext is to coordinate transformation, through UIGraphicsGetCurrentContext () is the Core of Graphics coordinate system, rather than Quartz2D coordinate system

-(void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx{ CGRect rect=CGRectMake(0, 0, 100, 100); CGContextRef context = ctx; CGPoint p = CGPointMake(rect.sie.width /2.0f, rect.sie.height /2.0f); UIGraphicsBeginImageContextWithOptions(rect.size,NO,[UIScreen mainScreen].scale); CGContextRef con=UIGraphicsGetCurrentContext(); NSLog(@"Image_CTM:%@",NSStringFromCGAffineTransform(CGContextGetCTM(con))); NSLog(@"Layer_CTM:%@",NSStringFromCGAffineTransform(CGContextGetCTM(ctx))); // Transform the current con Core Graphics coordinate system to Quartz2D coordinate system by using the following transformation, because the following CGContextClipToMask uses the Core Graphics coordinate system. CGContextRotateCTM(con, M_PI); // Rotate 180 degrees clockwise. CGContextScaleCTM(con, -1, 1) is offset; // Rotate CGContextTranslateCTM(con,0, -100); UIBezierPath* bPath =[UIBezierPath bezierPath]; [[UIColor blackColor] setFill]; [bpath moveToPoint:p]; [bpath addArcWithCenter:p radius:p.x startAngle:0 endAngle:M_PI clockwise:YES]; CGContextAddPath(con, bpath.CGPath); CGContextFillPath(con); CGImageRef mask = CGBitmapContextCreateImage(UIGraphicsGetCurrentContext()); NSLog(@"image width:%ld height:%ld",CGImageGetWidth(mask),CGImageGetHeight(mask)); UIGraphicsEndImageContext(); CGContextClipToMask(context, rect, mask); CGFloat components [8] = {1.0 1.0 f, 0,0,1.0 f, f, f, 245.0/255.0 207.0/255.0 f, f / 1.0 / end color}; CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB(); CGGradientRef gradient = CGGradientCreateWithColorComponents(space, components, NULL,2); CGPoint p1 = CGPointMake(rect.sie.width /2.0f, rect.sie.height /2.0f); CGContextDrawRadialGradient (context, gradient, p1, 0.0 f, p1, p1, x, 0). CGColorSpaceRelease(space); CGContextClosePath(context); CGContextFillPath(context); }Copy the code

Image display coordinate system conversion

- (void)drawRect:(CGRect)rect { CGContextRef context = UIGraphicsGetCurrentContext(); CGPoint p = CGPointMake(rect.sie.width /2.0f, rect.sie.height /2.0f); [[UIColor redColor] setStroke]; CGContextSetLineWidth(context, 1); CGContextMoveToPoint(context, 0, 0); CGContextAddLineToPoint(context, p.x, p.y); CGContextStrokePath(context); UIImage* image=[UIImage imageNamed:@"Professortocat_v2"]; CGRect rect=CGRectMake(30,30, 50, 50); // [image drawInRect:rect]; // Note that the following transformation is based on the origin of its coordinate system. // CGContextTranslateCTM(context, 0, 50); // CGContextTranslateCTM(context, 0, 50); / / CGContextScaleCTM (context, 1.0, 1.0); // CGContextDrawImage(context,r, [image CGImage]); CGContextSaveGState(Context); CGContextSaveGState(Context); CGContextTranslateCTM(context, rect.origin.x, rect.origin.y); //4 CGContextTranslateCTM(context, 0, rect.size.height); / / 3 CGContextScaleCTM (context, 1.0, 1.0); //2 CGContextTranslateCTM(context, -rect.origin.x, -rect.origin.y); //1 CGContextDrawImage(context, rect, image.CGImage); CGContextRestoreGState(context); }}Copy the code


On coordinate transformations between UIKit and Quartz

Transform can be understood as the transformation of point position or coordinate system. UIKit’s coordinate system has its origin in the upper left corner, whereas Qutarz’s coordinate system has its origin in the lower left corner.

CGContextRef imgCtx = UIGraphicsGetCurrentContext(); // CGContextRotateCTM(imgCtx, M_PI); // Rotate 180 degrees clockwise. CGContextScaleCTM(imgCtx, -1, 1); // CGContextScaleCTM(imgCtx, -1, 1); ImgCtx,0, -100; // CGContextTranslateCTM(imgCtx,0, -100); // Adjust the offset position backCopy the code

Through the above transformation, the position of the point in Quartz can be changed after the above transformation, which exactly corresponds to the relevant position in UIKit coordinate system..


Let the image rotate continuously from the current position

Notice that when the additive is YES, the animation goes from fromValue + current state to toValue + current state, and that’s it. When the additive is NO, the animation goes from fromValue to toValue.

- (IBAction)btnClick:(id)sender { // Change the model to the new "end value" (key path can work like this but properties  don't) CGFloat angleToAdd = M_PI_2; // 90 deg = pi/2 NSString *zRotationKeyPath = @"transform.rotation.z"; // The killer of typos CGFloat currentAngle = [[_myview.layer valueForKeyPath:zRotationKeyPath] floatValue]; [_myview.layer setValue:@(currentAngle+angleToAdd) forKeyPath:zRotationKeyPath]; // Change the model to the new "end value" (key path can work like this but properties don't) CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:zRotationKeyPath]; animation.delegate=self; animation.additive=YES; animation.duration = 1; // @( ) is fancy NSNumber literal syntax ... ToValue = @(0.0); toValue = @(0.0); // model value was already changed. End at that value animation.byValue = @(angleToAdd); // start from - this value (it's toValue - byValue (see above)) // Add the animation. Once it completed it will be removed and you will see the value // of the model layer which happens to be the same value as the animation stopped at.  [_myview.layer addAnimation:animation forKey:@"90rotation"]; }Copy the code

ref:Using CABasicAnimation to rotate a UIImageView more than once

Other methods for continuous animation:

Option 1: If (! _isForwardCenter) { _isForwardCenter = YES; animation.fromValue = @([fromValue intValue] - [toValue intValue]); } else { _isForwardCenter = NO; animation.fromValue = @([toValue intValue] - [fromValue intValue]); endValue = fromValue; } animation.toValue = @(0); animation.additive = YES; animationLayer.position = CGPointMake(animationLayer.position.x, [endValue intValue]); animation.timingFunction = [CAMediaTimingFunction functionWithControlPoints:.5 :0 :.5 :1]; // better easing function static NSUInteger number = 0; //use nil key or integer, not [NSDate date] because string description only shows seconds NSString *key = [NSString stringWithFormat:@"ani_%lu", (unsigned long)number++]; [animationLayer addAnimation:animation forKey:key]; }Copy the code

Option 2, use UIView animation, pay attention to the Settings of related options:

- (void)UIKitAnimation:(BOOL)isReverse { if (! IsReverse) {[UIView animateWithDuration: 1.0 f animations: ^ {self. ImageViewRight. Center = CGPointMake(self.imageViewRight.center.x, 500); }]; } else {[UIView animateWithDuration: 1.0 f delay: 0 options: UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionCurveLinear animations:^{ self.imageViewRight.center = CGPointMake(self.imageViewRight.center.x, 88); } completion:^(BOOL finished) { }]; }}Copy the code
The general steps of the above method are as follows: 1. Set the animation to additive=YES 2. 3. Set toValue =@(0) of the animation and set byValue of the animation to the difference between the animation ending state and the animation starting state.Copy the code

Animation of position switching back and forth

The problem you are facing is that the position is not the same as the origin. Anyway, you are doing a relative animation of the position so it would be easier if you skip the toValue and fromValue and instead use the byValue property (with the relative change). Your code would look like this:

CABasicAnimation *moveUp; moveUp = [CABasicAnimation animationWithKeyPath:@"position.y"]; MoveUp. ByValue = @ (50.0 f); // or [NSNumber numberWithFloat:-50.0f] if you really need to moveUp. Duration = 1.0; moveUp.removedOnCompletion = NO; moveUp.fillMode = kCAFillModeBoth; moveUp.delegate = self; [[retestBTN layer] addAnimation:moveUp forKey:@"y"];Copy the code

and

CABasicAnimation *moveDown; moveDown = [CABasicAnimation animationWithKeyPath:@"position.y"]; MoveDown. ByValue = @ (50.0 f); // or [NSNumber numberWithFloat:50.0f] if you really need to moveDown.duration = 1.0; moveDown.removedOnCompletion = NO; moveDown.fillMode = kCAFillModeForwards; [[retestBTN layer] addAnimation:moveDown forKey:@"y"];Copy the code

Ref:stackoverflow.com/questions/1…


CAAnimation keeps the animation in its final state

CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position.y"]; Animation. ToValue = @ 300.0; Animation. Duration = 1.0; [layeraddAnimation:animationforKey:@"positionAnimation"];Copy the code

To:

// Save the original value CGFloat originalY = layer.position.y; // Change the model value layer.position = CGPointMake(layer.position.x, 300.0); CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position.y"]; // Now specify the fromValue for the animation because // the current model value is already the correct toValue animation.fromValue = @(originalY); Animation. Duration = 1.0; // Use the name of the animated property as key // to override the implicit animation [layer addAnimation:animation forKey:@"position"];Copy the code

When the animation is complete, the view will return to its initial state, explained as follows:

1.Explicit Animations Only Affect the Presentation Layer. 2. Change the animation name to postion to override the corresponding implicit animation. AppKit's implicit pattern creates a default key which may or may not be identical to the keyPath being animated. But there is one single key per property keyPath, and new animations overwrite previous ones. 3. For the scheme of using Kcafillmodeforward mentioned on the Internet, after the animation is finished, its position remains unchanged. It just keeps the animation in the final state.Copy the code

If you search the web for this problem, you will find other “solutions”, such as setting the animation’s fillMode property to kCAFillModeForwards and at the same time setting removedOnCompletion to NO. This seems to fix the cause of the issue when in fact it only hides the symptoms. This approach basically lets the animation live forever – and show the layer in its target position as long as it lives on. However, when you query the model layer, it will still return its original value. And you may also run into problems as soon as you set up another animation for the same property

The absolute worst would be to set a layer’s model value after an animation has ended, which was something I struggled with when first learning Core Animation.

Ref :Prevent Layers From being snapped Back to Original Values When Using Explicit CAAnimations – Ole Begemann


FromValue of CAAnimation byValue toValue

The fromValue, byValue and toValue properties define the values being interpolated between. All are optional, and no more than two should be non-nil. The object type should match the type of the property being animated. The interpolation values are used as follows:

Both fromValue and toValue are non-nil. Interpolates between fromValue and toValue. fromValue and byValue are non-nil. Interpolates between fromValue and (fromValue + byValue). byValue and toValue are non-nil. Interpolates between (toValue  - byValue) and toValue. fromValue is non-nil. Interpolates between fromValue and the current presentation value of the Property.tovalue is non-nil. Interpolates between the current value of keyPath in the target layer's presentation layer And tovalue.byvalue is non-nil. Interpolates between the current value of keyPath in the target layer's presentation layer and that value plus byValue. All properties are nil. Interpolates between the previous value of keyPath in the Target layer's presentation layer and the current value of keyPath in the target layer's presentation layer.Copy the code

Precautions for loading resources in IBDesignable

I customize a view, create a XIB, and then create a class that inherits UIView and adds IB_DESIGNABLE to preview it in interface Builder. The initialization code in this class (initWithFrame initWithCoder) used in [[NSBundle mainBundle] loadNibNamed: @ “CustomView owner:” self options: nil]; To load the XIB, but after doing so, it will work fine on the phone, but it will give an error in the Xcode preview. In the code, it should be written like this so that xcode and runtime will work properly: [[NSBundle bundleForClass:[self class]] loadNibNamed:@”CustomView” owner:self options:nil]; Solution:

As of when this question was first asked, Creating an IB- Designable control required packaging it in a framework target. You don’t have to do that anymore — the Shipping Xcode 6.0 (and later) will preview IB- Designable Controls from your app target, too. However, the problem and the solution are the same.

Why? [NSBundle mainBundle] returns the primary bundle of the currently running app. When you call that from a framework, you’re getting a different bundle returned based on which app is loading your framework. When you run your app, your app loads the framework. When you use the control in IB, a special Xcode helper app loads the framework. Even if your IB-designable control is in your app target, Xcode is creating a special helper app to run the control inside of IB.

The solution? Call +[NSBundle bundleForClass:] instead (or NSBundle(forClass:) in Swift). This gets you the bundle containing the executable code for whichever class you specify. (You can use[self class]/self.dynamicType there, but beware the result will change for subclasses defined in different bundles.)

If you’re using the framework approach — which can be useful for some apps even though it’s no longer required for IB- Designable Controls — it’s best to put image resources in the same framework with the code that uses them. If your framework code expects to use resources provided at run time by whatever app loads the framework, the best thing to do for making it IB-designable is to fake it. Implement the prepareForInterfaceBuilder method in your control and have it load resources from a known place (like the framework bundle or a static path in your Xcode workspace).

Ref:stackoverflow.com/questions/2…


About using IVar in blocks

For direct ivar access, use ->. For example:

__weak VeryCool *weakSelf = self; something.changeHandler = ^(NSUInteger newIndex) { if (newIndex == 0) { VeryCool* strongSelf = weakSelf; if (strongSelf) [strongSelf->options removeObjectForKey:@"seller"]; }};Copy the code

It’s important to check that strongSelf is non-nil because direct access to an instance variable will crash for a nil pointer (which is different from invoking methods with a nil receiver and property access is just method invocation).

Ref:stackoverflow.com/questions/1…

Ivar also creates retain cycle, so the above method is needed to prevent retain cycle

In a less obvious manifestation of the same potential retain cycle, any ivar you use will also capture the parent object:

// The following block will retain "self"
SomeBlockType someBlock = ^{
    BOOL isDone = _isDone;  // _isDone is an ivar of self
};
Copy the code

ref:ARC Best Practices

About IVar in ARC

If a variable:

  1. Is declared in a class using ARC.
  2. Is used solely for class implementation (not exposed as part of the class interface). But U can access public ivar using ClassInstance ->iVar = @”New value “, You should avoid this, it’s not a good programming practice.
  3. Does not require any KVO.
  4. Does not require any custom getter/setter.

Then it is appropriate to declare it as an ivar without a corresponding @property/@synthesize, and to refer to it directly within the implementation. It is inline with Encapsulation to declare this ivar in the class implementation file.

// MyClass.h
@interface MyClass : ParentClass
@end

// MyClass.m
@implementation MyClass {
    NSString *myString;
}

- (void)myMethod {
    myString = @"I'm setting my ivar directly";
}
@end
Copy the code
  • This ivar will be treated as __strong by the ARC compiler.
  • It will be initialized to nil if it is an object, or 0 if it is a primitive.

Ref:stackoverflow.com/questions/7…


The composition of 0. CAAnimation

CAAnimation is the basic Object of CoreAnimation. CAPropertyAnimation mainly uses the following two subClass CAKeyframeAnimation CABasicAnimation CAAnimationGroup CATransition CATransactionCopy the code

About CATransaction

Finally, CATransaction; With CALayer, as soon as we modify the frame, the Layer will start displaying animations, and CATransaction can be used to control the animation time and animation speed, similar to UIView Aniamtion usage.

[CATransaction begin]; if (! animated) { [CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions]; } else { [CATransaction setAnimationDuration:.3f]; } self.layer.backgroundColor = [UIColor clearColor].CGColor; self.gradientLayer.colors = colors; self.gradientLayer.locations = locations; [CATransaction commit];Copy the code

1. General process of CoreImage image rendering.

ref:www.cnblogs.com/YouXianMing…

2. Solve the problem that CABasicAnimation resets to initial value after animation completes

CATransform3D trans=CATransform3DIdentity; Trans. M34 = - 1.0/500.0; trans=CATransform3DTranslate(trans, 10, -20, 0); Trans = CATransform3DRotate (trans, M_PI / 5, 0.5, 1,0.75); Trans = CATransform3DScale (trans, 0.8, 0.8, 0.8); CABasicAnimation *basicAnimation = [CABasicAnimation animationWithKeyPath:@"transform"] basicAnimation.toValue = [NSValue valueWithCATransform3D:trans]; basicAnimation.removedOnCompletion = NO; Basicanimation. duration = 0.2f; / / set the animation time basicAnimation. Cumulative = NO. basicAnimation.repeatCount = 0; BasicAnimation. FillMode = kcafillmodeforward; // basicAnimation. FillMode = kcafillmodeforward; / / this way, can make the animation at the end of the last condition. [_imgView. Layer addAnimation: basicAnimation forKey: nil];Copy the code

3. There is no difference between keyframe animation and basic animation type, but the difference is that keyframe animation is more powerful than ordinary animation, because it can set more values, ordinary animation can achieve effects can be replaced by keyframe animation oh.

4. Mask the layer.

After layer A is masked with A mask layer, all but the areas covered by the mask will be displayed as if they did not exist. Note that if the fill color of the mask layer mask is transparent, the masked area will not be shown. CALayer’s default backgroundColor is nil

5.UIBezierPath has a method that fills the current context directly.

Notice that the drawRect call is done after the layout is done, and the drawRect is the last step, or you can manually call [self setNeedsDisplay] when appropriate; To enforce drawRect

- (void)drawRect:(CGRect)rect{
    UIBezierPath  *round = [UIBezierPath bezierPathWithRoundedRect:CGRectMake((CGRectGetWidth(self.frame)- 35) /2.CGRectGetHeight(self.frame)- 12.35.5) byRoundingCorners:(UIRectCornerAllCorners) cornerRadii:CGSizeMake(10.10)];
    [[UIColor lightGrayColor] setFill];
    [round fill];
}
Copy the code

It’s a fill, and there’s also a stroke method to draw a line. Reference: Open source library JKNotifier

6. Before an animation starts, we can remove the animation in progress and then start the current animation

Remove all animations attached to the layer.

- (void)removeAllAnimations [self.notifierBar.layer removeAllAnimations]; self.notifierBar.userInteractionEnabled = YES; [self.notifierBar show:note name:appName icon:appIcon]; [UIView animations animateWithDuration: (0.4) : ^ {self. NotifierBar. Alpha = 1.0; self. NotifierBar. Transform = CGAffineTransformIdentity; }];Copy the code

Reference: Open source library JKNotifier

7.CAShapeLayer supports the following animation types.

/* The path defining the shape to be rendered. If the path extends

  • outside the layer bounds it will not automatically be clipped to the
  • layer, only if the normal layer masking rules cause that. Defaults
  • to null. Animatable. (Note that although the path property is
  • animatable, no implicit animation will be created when the property
  • is changed.) */

@property CGPathRef path;

/* The color to fill the path’s stroked outline, or nil for no stroking.

  • Defaults to nil. Animatable. */

@property CGColorRef strokeColor;

/* These values define the subregion of the path used to draw the

  • The values must be in The range [0,1] with zero
  • representing the start of the path and one the end. Values in
  • between zero and one are interpolated linearly along the path
  • length. strokeStart defaults to zero and strokeEnd to one. Both are
  • animatable. */

@property CGFloat strokeStart, strokeEnd;

/* The line width used when stroking the path. Defaults to one.

  • Animatable. */

@property CGFloat lineWidth;

/* The miter limit used when stroking the path. Defaults to ten.

  • Animatable. */

@property CGFloat miterLimit;

/* The phase of the dashing pattern applied when creating the stroke.

  • Defaults to zero. Animatable. */

@property CGFloat lineDashPhase;

8. Difference between CATransformLayer and CALayer.

CALayer *plane1 = [CALayer layer]; Plane1. AnchorPoint = CGPointMake (0.5, 0.5); // Anchor point plane1.frame = (CGRect){CGPointZero, CGSizeMake(100, 100)}; Opacity = 0; // Opacity = 0; // Z-axis translation CATransform3D plane1_3D = CATransform3DIdentity; plane1_3D = CATransform3DTranslate(plane1_3D, 0, 0, -10); plane1.transform = plane1_3D; CALayer *plane2 = [CALayer layer]; Plane2. AnchorPoint = CGPointMake (0.5, 0.5); // Anchor point plane2.frame = (CGRect){CGPointZero, CGSizeMake(100, 100)}; Opacity = 0; // Opacity = 0; // Z-axis translation CATransform3D plane2_3D = CATransform3DIdentity; plane2_3D = CATransform3DTranslate(plane2_3D, 0, 0, -30); plane2.transform = plane2_3D; // CALayer *container = [CALayer layer]; CATransformLayer *container = [CATransformLayer layer]; Container. The position = CGPointMake (0.5, 0.5); container.frame = self.view.bounds; [self.view.layer addSublayer:container]; [container addSublayer:plane1]; [container addSublayer:plane2]; CATransform3D plane_3D = CATransform3DIdentity; Plane_3D. M34 = 1.0 / - 500; plane_3D = CATransform3DRotate(plane_3D, DEGREE(30), 0, 1, 0); container.transform = plane_3D;Copy the code

When CALayer is used as a container, one layer is hidden by another.

This is what happens when you use CATransformLayer as a container, so you have a 3D effect.

Ref: www.cnblogs.com/YouXianMing…

There are flashing problems in the animation process

Try using a CATransaction lock around your updates to see if that helps. This will prevent the previous animations from changing the position of the layers while you’re in the process of updating them with new animations. In your touch handling method, wrap the animations in a transaction and lock:

[CATransaction lock];
[CATransaction begin];
// update the sublayers with new animations
[CATransaction commit];
[CATransaction unlock];
Copy the code

The CAAnimationGroup overrides the properties of individual animations in its animation array

CAAnimationGroup *anim = [CAAnimationGroup animation]; [anim setAnimations:[NSArray arrayWithObjects:[self transparencyAnimation], [self translateAnimation], nil]]; [anim setDuration: 3.0]; / / the time cycle, can cover in an array of other animation cycle [anim setRemovedOnCompletion: NO]; // This will also cover... [anim setDelegate:self]; // This will also cover... [anim setFillMode:kCAFillModeForwards]; // This will also [layer addAnimation:anim forKey:nil]; - (CABasicAnimation *)transparencyAnimation { CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"opacity"]; [animation setFromValue: [NSNumber numberWithFloat: 1.0 f]]. [animation setToValue: [NSNumber numberWithFloat: 0.2 f]]. return animation; } - (CABasicAnimation *)translateAnimation { CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"]; [animation setFromValue: [NSValue valueWithCGPoint: CGPointMake (100.0, 100.0)]]. Animation. ToValue = [NSValue valueWithCGPoint: CGPointMake (100.0, 250.0)]. return animation; }Copy the code