Some of the questions in this article came from an interview experience that was a mess, so go back to the official documents and some blog posts and ask your classmates how they answered them.

I hope you iOS developers, when learning to use some of the Cocoa Framework, read some official documentation. Sometimes the official documentation is clear, but I just didn’t read it, so I couldn’t answer it in the interview.

Objc China View-Layer collaboration https://objccn.io/issue-12-4/ GitHub: https://github.com/ZhaoBingDong/CAAnimaiton a: why are associated with UI API in the main thread (personal)

Note

For the most part, use UIKit classes only from your app’s main thread. This is particularly true forClasses Derived from UIResponder or that involved manipulating your app's user interfaceinAny way. Most of the time, classes that use UIKit in your app can only be in the main thread. This is especially true for classes UIResponder derived from or involved in manipulating the user interface of your application. Most apple Foundation and UIKit API is not thread-safe Because the program involves the multithreading place, after all, is one of the few scenarios Apple attributes for each method and the thread locked thread synchronization spinlocks what's too much trouble and cost performance It is necessary for developers to pay attention to thread safety when using local resources, to avoid multiple threads accessing the same resource, including multiple threads operating on the same UI object like NSArray NSDictionary immutable are thread safe and NSMutableArray The NSMutableDictionary thread is not secure.Copy the code

Two: Changing those UIView properties can produce animation effects

The following properties of the UIView class are animatable: Frame includes Warning If the transform property is not the identity transform, the value of this property is undefined and therefore should be ignored. frame :The frame rectangle,whichDescribes the View's location and sizeinIts superview's coordinate System. // Frame is the position and size of a view in the parent view coordinate system relative to the parent control Warning If the transform property is not the identity transform, The value of this property is undefined and therefore should be ignored. // The value of this property is undefined and therefore should be ignored CGAffineTransformIdentity type does not make sense to set up the frame of before, when to set the transform = CGAffineTransformIdentity view will use the original frame, Using transfrom to change The view after The deformation displacement of rotation effects, animation need to set up after The reset CGAffineTransformIdentity. Bounds: The bounds rectangle,whichDescribes the View's location and sizeinIts own coordinate system. // Bounds a view whose coordinate system position and size corresponds to that of the set center Transform alpha backgroundColor It's going to generate some animationCopy the code

3. Why does setting the View. frame Bounds Center Transform etc work

The actionForLayer of the view is called when the view.frame is set:forKey: Generate an object that obeys CAAction, generate an animated object, add it to the rootLayer of UIView, and then call layer addAnimation to add the animation to the layerCopy the code

1 By overwriting MyView's + (Class)layerClass {return[MyLayer class]; } // animate objects based on CAAction can be regarded as CAAnimation or CAAction compliant private class - (id<CAAction>)actionForLayer:(CALayer *)layerforKey:(NSString *)event {
    
   return  [super actionForLayer:layer forKey:event]; } change UIView's layer from system CALayer to subclassed MyLayer to make it easier to rewrite MyLayer to see what animation objects are added to MyLayer and a change to viet.frame operation will generate multiple CAAnimtaion pair 2 adds animation to rootLayer (a view has only one rootLayer, which is the View. layer property, and a layer can have multiple subLayers, one subLayer Only one parent layer) - (void)addAnimation:(CAAnimation *)animforKey:(NSString *)key {
    
    NSLog(@"\nadding animation: %@\n", [anim debugDescription]);

    [super addAnimation:anim forKey:key];
    
}


Copy the code

How to implement custom UIView animations

After ios UIView provides animateWithDuration: animations: animation block method supports some basic animation, in the support you need to use beginAnimations: context: CommitAnimations to manage animations

Encapsulate some of the animation properties that need to change the view into block code. So can we encapsulate some commonly used animations into such blocks of code, reducing the number of repeated code calls?

If you look at question 2, it will definitely be enlightening. In fact, this can be implemented. You need to use the Runtime to intercept the actionForLayer: forkey method of UIView system

+ (void)load {
    
    SEL originalSelector = @selector(actionForLayer:forKey:);
    SEL extendedSelector = @selector(DR_actionForLayer:forKey:);
    
    Method originalMethod = class_getInstanceMethod(self, originalSelector);
    Method extendedMethod = class_getInstanceMethod(self, extendedSelector);
    
    NSAssert(originalMethod, @"original method should exist");
    NSAssert(extendedMethod, @"exchanged method should exist");
    
    if(class_addMethod(self, originalSelector, method_getImplementation(extendedMethod), method_getTypeEncoding(extendedMethod))) {
        class_replaceMethod(self, extendedSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
    } else{ method_exchangeImplementations(originalMethod, extendedMethod); }} / / judge different context difference system UIView animaitons animation and write their own / / [UIView DR_popAnimationWithDuration (NSTimeInterval) duration animations:(void (^)(void))animations] - (id<CAAction>)DR_actionForLayer:(CALayer *)layerforKey:(NSString *)event
{
    
    if(DR_currentAnimationContext == DR_popAnimationContext) {// Write our custom code here... // Save the layer at which each animation object is generated, the view.layer takes the saved layer out of the DR_popAnimationWithDuration method and executes some custom CAAnimaiton animations DRSavedPopAnimationState *state = [DRSavedPopAnimationState savedStateWithLayer:layer keyPath:event]; [[UIView DR_savedPopAnimationStates] addObject:state]; } // Call the original methodreturn [self DR_actionForLayer:layer forKey:event]; // Yes, you read that right. Because they've been swapped}Copy the code

2: Implement the custom UIView Animations code section

+ (void)DR_popAnimationWithDuration:(NSTimeInterval)duration animations:(void (^)(void))animations { DR_currentAnimationContext = DR_popAnimationContext; // Perform the animation (which triggers the swapped delegate method). When you change some view.frame properties in a block block, it intercepts UIView's actionforLayer method to get the layer to animate animations(); NSMutableArray *states = [UIView DR_savedPopAnimationStates]; NSMutableArray *states = [UIView DR_savedPopAnimationStates];for (DRSavedPopAnimationState *state instates) { DRSavedPopAnimationState *savedState = (DRSavedPopAnimationState *)state; CALayer *layer = savedState.layer; NSString *keyPath = savedState.keyPath; id oldValue = savedState.oldValue; id newValue = [layer valueForKeyPath:keyPath]; CAKeyframeAnimation *anim = [CAKeyframeAnimation animationWithKeyPath:keyPath]; CGFloat much = 0.2; CAMediaTimingFunction *easeIn = [CAMediaTimingFunctionfunctionWithControlPoints: 1.0:0.0:1.0 - much) : 1.0]; CAMediaTimingFunction *easeOut = [CAMediaTimingFunctionfunctionWithControlPoints: much: 0.0:0.0, 1.0]; anim.duration = duration; Anim. keyTimes = @[@0, @(0.35), @1]; anim.values = @[oldValue, newValue, oldValue]; anim.timingFunctions = @[easeIn, easeOut]; [CATransaction begin]; [CATransactionsetDisableActions:YES];
        [layer setValue:oldValue forKeyPath:keyPath]; [CATransaction commit]; / / add"pop"Animation [layer addAnimation: animforKey:keyPath]; } state removeAllObjects: {state removeAllObjects: [states removeAllObjects]; /* I'll add */ in a moment and clear out the context information. Next time, we'll use this context to distinguish animateWithDuration from the custom DR_popAnimationWithDuration method, DR_currentAnimationContext = NULL; }Copy the code

// Through the above code part of the general understanding of UIView Aniaitons animation block operations to further understand the core animation

The final effect is as follows: