IOS-Core-Animation 9-layer time

Source: Internet
Author: User

IOS-Core-Animation 9-layer time



> * The biggest difference between time and space is that time cannot be reused * -- forstermerick


In the above two chapters, we have discussed a variety of layer animations that can be implemented using 'caanimation 'and its subclass. Animation takes a period of time, so * Timing * is crucial to the entire concept. In this chapter, let's take a look at 'camediatiming' and see how Core Animation tracks time.


# 'Camediatiming' Protocol


The 'camediatiming' Protocol defines a set of attributes used to control the elapsed time in an animation. Both 'calayer' and 'caanimation 'implement this protocol, therefore, time can be controlled by any class based on a layer or an animation.


### Persistence and repetition


In chapter 8 "Explicit Animation", we briefly mentioned 'duration' (one of the attributes of 'camediatiming ), 'duration' is a 'cftimeinterval' type (similar to a double-precision floating point type of 'nstimeinterval'). It specifies the time for an iteration of the animation to be performed.


What does * One iteration * mean? Another attribute of 'camediatiming' is 'repeatcount', which indicates the number of iterations of the animation. If 'duration' is 2 and 'repeatcount' is set to 3.5 (three semi-iterations), the animation duration is 7 seconds.


'Duration' and 'repeatcount' are both 0 by default. However, this does not mean that the animation duration is 0 seconds, or 0 times. Here 0 only represents the "default", that is, 0.25 seconds and 1 time, you can use a simple test to assign multiple values to these two attributes, for example, listing 9.1 and Figure 9.1 show the program results.


Listing 9.1 tests 'duration' and 'repeatcount'


'''Objective-c

@ Interface ViewController ()


@ Property (nonatomic, weak) IBOutlet UIView * containerView;

@ Property (nonatomic, weak) IBOutlet UITextField * durationField;

@ Property (nonatomic, weak) IBOutlet UITextField * repeatField;

@ Property (nonatomic, weak) IBOutlet UIButton * startButton;

@ Property (nonatomic, strong) CALayer * shipLayer;


@ End


@ Implementation ViewController


-(Void) viewDidLoad

{

[Super viewDidLoad];

// Add the ship

Self. shipLayer = [CALayer layer];

Self. shipLayer. frame = CGRectMake (0, 0,128,128 );

Self. shipLayer. position = CGPointMake (150,150 );

Self. shipLayer. contents = (_ bridge id) [UIImage imageNamed: @ "Ship.png"]. CGImage;

[Self. containerView. layer addSublayer: self. shipLayer];

}


-(Void) setControlsEnabled :( BOOL) enabled

{

For (UIControl * control in @ [self. durationField, self. repeatField, self. startButton]) {

Control. enabled = enabled;

Control. alpha = enabled? 1.0f: 0.25f;

}

}


-(IBAction) hideKeyboard

{

[Self. durationField resignFirstResponder];

[Self. repeatField resignFirstResponder];

}


-(IBAction) start

{

CFTimeInterval duration = [self. durationField. text doubleValue];

Float repeatCount = [self. repeatField. text floatValue];

// Animate the ship rotation

CABasicAnimation * animation = [CABasicAnimation animation];

Animation. keyPath = @ "transform. rotation ";

Animation. duration = duration;

Animation. repeatCount = repeatCount;

Animation. byValue = @ (M_PI * 2 );

Animation. delegate = self;

[Self. shipLayer addAnimation: animation forKey: @ "rotateAnimation"];

// Disable controls

[Self setControlsEnabled: NO];

}


-(Void) animationDidStop :( CAAnimation *) anim finished :( BOOL) flag

{

// Reenable controls

[Self setControlsEnabled: YES];

}


@ End

'''




Figure 9.1 demonstrates the test programs of 'duration' and 'repeatcount'


Another way to create a repeated animation is to use the 'repeatduration' attribute, which allows the animation to repeat a specified time instead of a specified number of times. You even set an attribute called 'autoreverses '(BOOL type) to automatically play back during the alternating cycle of each interval. This is useful for playing a continuous, non-repeating animation, such as opening a door and closing it (Figure 9.2 ).




Figure 9.2 swing door Animation


The code for swinging the door can be found in listing 9.2. We used 'autoreverses 'to automatically close the door after opening. Here we set 'repeatduration' to 'infinity, so the animation is played infinitely, setting 'repeatcount' to 'infinity also has the same effect. Note that 'repeatcount' and 'repeatduration' may conflict with each other, so you only need to specify a non-zero value for one of them. The behavior of setting non-0 values for both attributes is not defined.


Listing 9.2 uses the 'autoreverses 'attribute to swing the door


'''Objective-c

@ Interface ViewController ()


@ Property (nonatomic, weak) UIView * containerView;


@ End


@ Implementation ViewController


-(Void) viewDidLoad

{

[Super viewDidLoad];

// Add the door

CALayer * doorLayer = [CALayer layer];

DoorLayer. frame = CGRectMake (0, 0,128,256 );

DoorLayer. position = CGPointMake (150-64,150 );

DoorLayer. anchorPoint = CGPointMake (0, 0.5 );

DoorLayer. contents = (_ bridge id) [UIImage imageNamed: @ "Door.png"]. CGImage;

[Self. containerView. layer addSublayer: doorLayer];

// Apply perspective transform

CATransform3D perspective = CATransform3DIdentity;

Perspective. m34 =-1.0/500.0;

Self. containerView. layer. sublayerTransform = perspective;

// Apply swinging animation

CABasicAnimation * animation = [CABasicAnimation animation];

Animation. keyPath = @ "transform. rotation. y ";

Animation. toValue = @ (-M_PI_2 );

Animation. duration = 2.0;

Animation. repeatDuration = INFINITY;

Animation. autoreverses = YES;

[DoorLayer addAnimation: animation forKey: nil];

}


@ End

'''


### Relative time


Each time we talk about Core Animation, the time is relative. Each Animation has its own time, which can be accelerated, delayed, or offset independently.


'Begintime' specifies the delay time before the animation starts. The latency starts from the moment the animation is added to the visible layer. The default value is 0 (that is, the animation will be executed immediately ).


'Speed' is a multiple of time. The default value is 1.0. Reducing it will slow down the layer/animation time, and increasing it will speed up. If the speed is 2.0, an animation with a 'duration' of 1 is completed in 0.5 seconds.


'Timeoffset 'is similar to 'begintime', but unlike the delayed animation caused by the addition of 'begintime', the addition of 'timeoffset' only allows the animation to quickly enter a certain point. For example, for an animation that lasts for 1 second, setting 'timeoffset' to 0.5 means that the animation starts from half of the place.


Unlike 'begintime', 'timeoffset 'is not affected by 'speed. So if you set 'speed' to 2.0 and 'timeoffset 'to 0.5, your animation will start from the end of the animation, because one second animation is actually reduced to 0.5 seconds. However, even if the 'timeoffset 'is used to start the animation from the end point, it still plays a complete duration. This animation only loops through and then plays from the beginning.


You can use the test program in listing 9.3 to verify it. Set the 'speed' and 'timeoffset 'slider to a random value, and click play to observe the effect (see Figure 9.3)


Listing 9.3 tests the 'timeoffset 'and 'speed' attributes.


'''Objective-c

@ Interface ViewController ()


@ Property (nonatomic, weak) IBOutlet UIView * containerView;

@ Property (nonatomic, weak) IBOutlet UILabel * speedLabel;

@ Property (nonatomic, weak) IBOutlet UILabel * timeOffsetLabel;

@ Property (nonatomic, weak) IBOutlet UISlider * speedSlider;

@ Property (nonatomic, weak) IBOutlet UISlider * timeOffsetSlider;

@ Property (nonatomic, strong) UIBezierPath * bezierPath;

@ Property (nonatomic, strong) CALayer * shipLayer;


@ End


@ Implementation ViewController


-(Void) viewDidLoad

{

[Super viewDidLoad];

// Create a path

Self. bezierPath = [[UIBezierPath alloc] init];

[Self. bezierPath moveToPoint: CGPointMake (0,150)];

[Self. bezierPath addCurveToPoint: CGPointMake (300,150) controlPoint1: CGPointMake (75, 0) controlPoint2: CGPointMake (225,300)];

// Draw the path using a CAShapeLayer

CAShapeLayer * pathLayer = [CAShapeLayer layer];

PathLayer. path = self. bezierPath. CGPath;

PathLayer. fillColor = [UIColor clearColor]. CGColor;

PathLayer. strokeColor = [UIColor redColor]. CGColor;

PathLayer. lineWidth = 3.0f;

[Self. containerView. layer addSublayer: pathLayer];

// Add the ship

Self. shipLayer = [CALayer layer];

Self. shipLayer. frame = CGRectMake (0, 0, 64, 64 );

Self. shipLayer. position = CGPointMake (0,150 );

Self. shipLayer. contents = (_ bridge id) [UIImage imageNamed: @ "Ship.png"]. CGImage;

[Self. containerView. layer addSublayer: self. shipLayer];

// Set initial values

[Self updateSliders];

}


-(IBAction) updateSliders

{

CFTimeInterval timeOffset = self. timeOffsetSlider. value;

Self. timeOffsetLabel. text = [NSString stringWithFormat: @ "% 0.2f", imeOffset];

Float speed = self. speedSlider. value;

Self. speedLabel. text = [NSString stringWithFormat: @ "% 0.2f", speed];

}


-(IBAction) play

{

// Create the keyframe animation

CAKeyframeAnimation * animation = [CAKeyframeAnimation animation];

Animation. keyPath = @ "position ";

Animation. timeOffset = self. timeOffsetSlider. value;

Animation. speed = self. speedSlider. value;

Animation. duration = 1.0;

Animation. path = self. bezierPath. CGPath;

Animation. rotationMode = kCAAnimationRotateAuto;

Animation. removedOnCompletion = NO;

[Self. shipLayer addAnimation: animation forKey: @ "slide"];

}


@ End

'''




Figure 9.3 simple application to test time offset and speed


### 'Fillmode'


For an animation that is not 'begintime', a State is displayed when the animation is added to the layer but nothing happens. Similarly, an animation whose 'removeoncompletion 'is set to 'no' will remain in the previous State at the end of the animation. This creates a problem. Before and after the animation starts, what is the value of the property of the animation to be set?


One possibility is that the attributes are consistent with the animation before being added, that is, the value defined in the model layer (See Chapter 7 "implicit Animation", model layer and rendering layer interpretation ).


Another possibility is to keep the frame before the animation starts, or the frame after the animation ends. This is the so-called * fill *, because the animation start and end values are used to fill the time before and after the start.


This behavior is handed over to the developer. It can be controlled by 'fillmode' of 'camediatiming. 'Fillmode' is a 'nsstring' type and can accept the following four constants:


KCAFillModeForwards

KCAFillModeBackwards

KCAFillModeBoth

KCAFillModeRemoved

The default value is 'kcafillmoderemoved'. When the animation is no longer played, the three types of forward values specified by the layer model are displayed. The animation state is filled either backward or backward, this allows the animation to remain the starting and ending values before or after the animation.


This provides another solution to avoid returning quickly when the animation ends (see Chapter 8 ). But remember, when you use it to solve this problem, you need to set 'removeoncompletion 'to 'no' and add a non-empty key to the animation, therefore, you can remove the animation from the layer when it is not required.


# Hierarchical relationship time


In Chapter 3 "layer geometry", you have learned how each layer defines its coordinate system relative to its parent layer in the layer tree. Animation time is similar to it. Each animation and layer has its own hierarchical concept in time, which is measured relative to its father. Adjusting the Time of the layer will affect the animation of the layer itself and the child layer, but will not affect the parent layer. Another similarity is that all animations are combined by hierarchy (using the 'caanimationgroup' instance ).


Adjusting the 'duration' and 'repeatcount'/'repeatduration' attributes for 'calayer 'or 'cagroupanimation' does not affect the sub-animation. However, the 'begintime', 'timeoffset ', and 'speed' attributes will affect the sub-animation. However, in a hierarchical relationship, 'begintime' specifies the offset between the parent layer animation (or the parent animation in the composite relationship) and the object to start its own animation. Similarly, adjusting the 'speed' attribute of 'calayer 'and 'cagroupanimation' will apply a scaling factor to the animation and sub-animation speed.


### Global time and local time


CoreAnimation has a concept of * Global time *, that is, the so-called * mahtime * ("mah's" is actually the name of the kernel in iOS and Mac OS systems ). All processes on the device are global-but not global on different devices-it is enough to facilitate the animation reference point, you can use the 'cacurrentmediatime 'function to access the Mach time:

CFTimeInterval time = CACurrentMediaTime ();



The value returned by this function does not matter (it returns the number of seconds since the last start of the device, not what you care about ), it provides a relative value for animation time measurement. Note that when the device is sleep, the Mach time will be paused, that is, all 'caanimations' (based on the Mach time) will also be paused.



Therefore, the Mach time is not useful for long-time measurements. For example, it is unwise to use 'cacurrentmediatime' to update a real-time alarm. (You can use '[ NSDate date] 'instead, as shown in the example in Chapter 3 ).



Each 'calayer' and 'caanimation 'instance has its own * local * time concept, which is based on the 'begintime' in the parent layer/animation hierarchy ', the 'timeoffset 'and 'speed' attributes are calculated. Just like converting the coordinates between different layers, 'calayer 'also provides a method to convert * local time * between different layers *. As follows:


-(CFTimeInterval) convertTime :( CFTimeInterval) t fromLayer :( CALayer *) l;

-(CFTimeInterval) convertTime :( CFTimeInterval) t toLayer :( CALayer *) l;


These methods are useful when different layers have different 'speed', 'timeoffset ', and 'begintime' animations.



### Pause, reverse and fast forward


When the 'speed' attribute of an animation is set to 0, the animation can be paused, but it is unlikely to be modified after the animation is added to a layer. Therefore, you cannot use this attribute for ongoing animations. Adding a 'caanimation 'to a layer is actually an unchangeable copy of the animation object. Therefore, changing the attributes of the original animation object does not affect the real animation. On the contrary, you can use '-animationForKey:' directly to retrieve ongoing animations of a layer and return the correct animation object. However, an exception is thrown when you modify its attributes.


If you remove an animation from a layer, the layer returns to the State before the animation. However, if you copy the rendering layer to the model layer before the animation is removed, the animation will appear paused. But the bad thing is that the animation cannot be restored later.


A simple method is to use 'camediatiming' to pause * Layer * itself. If you set 'speed' of a layer to 0, it suspends any animation added to the layer. Similarly, if you set 'speed' to greater than 1.0, the system will return to the animation. If you set it to a negative value, the system will return to the animation.



You can pause the animation of the entire application by adding the 'speed' of the main window layer. This benefits UI automation. We can accelerate all view animations for automated testing (note that the view outside the main window will not be affected, such as 'uialertview '). You can set the following parameters in app delegate for verification:


Self. window. layer. speed = 100;


You can also ** slow down * in this way, but you can also achieve it in the simulator by switching the slow animation.


# Manual Animation


'Timeoffset 'is a useful feature that allows you to manually control the animation process. By setting 'speed' to 0, you can disable automatic playback of the animation, then, use 'timeoffset 'to display the animation sequence. This makes it easy to use gestures to manually control the animation.


Let's take a simple example: You can use the closed animation to modify the code to control the animation with a gesture. We add a 'uipangesturerecognizer 'to the view and shake it around with 'timeoffset.


Because the animation cannot be modified after it is added to the layer, we can adjust the 'timeoffset 'of 'layer' to achieve the same effect (list 9.4 ).


Listing 9.4 manual animation control through touch gestures


'''Objective-c

@ Interface ViewController ()


@ Property (nonatomic, weak) UIView * containerView;

@ Property (nonatomic, strong) CALayer * doorLayer;


@ End


@ Implementation ViewController


-(Void) viewDidLoad

{

[Super viewDidLoad];

// Add the door

Self. doorLayer = [CALayer layer];

Self. doorLayer. frame = CGRectMake (0, 0,128,256 );

Self. doorLayer. position = CGPointMake (150-64,150 );

Self. doorLayer. anchorPoint = CGPointMake (0, 0.5 );

Self. doorLayer. contents = (_ bridge id) [UIImage imageNamed: @ "Door.png"]. CGImage;

[Self. containerView. layer addSublayer: self. doorLayer];

// Apply perspective transform

CATransform3D perspective = CATransform3DIdentity;

Perspective. m34 =-1.0/500.0;

Self. containerView. layer. sublayerTransform = perspective;

// Add pan gesture recognizer to handle swipes

UIPanGestureRecognizer * pan = [[UIPanGestureRecognizer alloc] init];

[Pan addTarget: self action: @ selector (pan :)];

[Self. view addGestureRecognizer: pan];

// Pause all layer animations

Self. doorLayer. speed = 0.0;

// Apply swinging animation (which won't play because layer is paused)

CABasicAnimation * animation = [CABasicAnimation animation];

Animation. keyPath = @ "transform. rotation. y ";

Animation. toValue = @ (-M_PI_2 );

Animation. duration = 1.0;

[Self. doorLayer addAnimation: animation forKey: nil];

}


-(Void) pan :( UIPanGestureRecognizer *) pan

{

// Get horizontal component of pan gesture

CGFloat x = [pan translationInView: self. view]. x;

// Convert from points to animation duration // using a reasonable scale factor

X/= 200366f;

// Update timeOffset and clamp result

CFTimeInterval timeOffset = self. doorLayer. timeOffset;

TimeOffset = MIN (0.999, MAX (0.0, timeOffset-x ));

Self. doorLayer. timeOffset = timeOffset;

// Reset pan gesture

[Pan setTranslation: CGPointZero inView: self. view];

}


@ End

'''


This is actually a little trick. It may be easier to directly set the 'transform' of the door with a mobile gesture than setting an animation and then displaying a frame at a time.


This is true in this example, but for a more complex scenario, such as the key, or an animation group with multiple layers, relative to the Real-Time Calculation of attributes of each layer, this is much more convenient.


# Summary


In this chapter, we learned about the 'camediatiming' protocol and the Core Animation mechanism used to operate Time-controlled animations. In the next chapter, we will get into touch with 'buffer', another technique used to make the animation more realistic.



Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.