• UI基础--自定义UISwitch


    建一个动画管理类

    .h

    #import <UIKit/UIKit.h>
    #import <Foundation/Foundation.h>
    
    
    @interface LLAnimationManager : NSObject
    
    
    /**
     *  init
     */
    - (instancetype)initWithAnimationDuration:(CGFloat)animationDuration;
    
    
    /**
     * the face layer move position
     */
    - (CABasicAnimation *)moveAnimationWithFromPosition:(CGPoint)fromPosition toPosition:(CGPoint)toPosition;
    
    /**
     * the layer background color animation
     */
    - (CABasicAnimation *)backgroundColorAnimationFromValue:(NSValue *)fromValue toValue:(NSValue *)toValue;
    
    /**
     * the eye layer move position
     */
    - (CABasicAnimation *)eyeMoveAnimationFromValue:(NSValue *)fromValue toValue:(NSValue *)toValue;
    
    /**
     * mouth key frame animation
     */
    - (CAKeyframeAnimation *)mouthKeyFrameAnimationWidthOffSet:(CGFloat)offSet on:(BOOL)on;
    
    /**
     *  eyes close and open key frame animation
     */
    - (CAKeyframeAnimation *)eyesCloseAndOpenAnimationWithRect:(CGRect)rect;
    
    
    @end

    .m

    #import "LLAnimationManager.h"
    #import <QuartzCore/QuartzCore.h> 
    
    @interface LLAnimationManager()
    
    /**
     *  the duration is the face moving time not include spring animation
     */
    @property (nonatomic, assign) CGFloat animationDuration;
    
    @end
    
    
    
    @implementation LLAnimationManager
    
    
    /**
     *  init
     */
    - (instancetype)initWithAnimationDuration:(CGFloat)animationDuration {
        self = [super init];
        if (self) {
            _animationDuration = animationDuration;
        }
        return self;
    }
    
    /**
     *  faceLayer move animation
     */
    - (CABasicAnimation *)moveAnimationWithFromPosition:(CGPoint)fromPosition toPosition:(CGPoint)toPosition {
        CABasicAnimation *moveAnimation = [CABasicAnimation animationWithKeyPath:@"position"];
        moveAnimation.fromValue = [NSValue valueWithCGPoint:fromPosition];
        moveAnimation.toValue = [NSValue valueWithCGPoint:toPosition];
        moveAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
        moveAnimation.duration = _animationDuration * 2 /3;
        moveAnimation.removedOnCompletion = NO;
        moveAnimation.fillMode = kCAFillModeForwards;
        return moveAnimation;
    }
    
    /**
     *  layer background color animation
     */
    - (CABasicAnimation *)backgroundColorAnimationFromValue:(NSValue *)fromValue toValue:(NSValue *)toValue {
        CABasicAnimation *colorAnimation = [CABasicAnimation animationWithKeyPath:@"backgroundColor"];
        colorAnimation.fromValue = fromValue;
        colorAnimation.toValue = toValue;
        colorAnimation.duration = _animationDuration * 2 /3;
        colorAnimation.removedOnCompletion = NO;
        colorAnimation.fillMode = kCAFillModeForwards;
        return colorAnimation;
    
    }
    
    /**
     * the eyes layer move
     */
    - (CABasicAnimation *)eyeMoveAnimationFromValue:(NSValue *)fromValue toValue:(NSValue *)toValue{
        CABasicAnimation *moveAnimation = [CABasicAnimation animationWithKeyPath:@"transform.translation.x"];
        moveAnimation.fromValue = fromValue;
        moveAnimation.toValue = toValue;
        moveAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
        moveAnimation.duration = _animationDuration / 3;
        moveAnimation.removedOnCompletion = NO;
        moveAnimation.fillMode = kCAFillModeForwards;
        return moveAnimation;
    }
    
    
    /**
     * mouth key frame animation
     */
    - (CAKeyframeAnimation *)mouthKeyFrameAnimationWidthOffSet:(CGFloat)offSet on:(BOOL)on{
        CGFloat frameNumber = _animationDuration * 60 / 3;
        CGFloat frameValue = on ? offSet : 0;
        NSMutableArray *arrayFrame = [NSMutableArray array];
        for (int i = 0; i < frameNumber; i++) {
            if (on) {
                frameValue = frameValue - offSet / frameNumber;
            } else {
                frameValue = frameValue + offSet / frameNumber;
            }
            [arrayFrame addObject:@(frameValue)];
        }
        CAKeyframeAnimation *keyAnimation = [CAKeyframeAnimation animationWithKeyPath:@"mouthOffSet"];
        keyAnimation.values = arrayFrame;
        keyAnimation.duration = _animationDuration / 4;
        if (!on && _animationDuration >= 1.f) {
            keyAnimation.beginTime = CACurrentMediaTime() + _animationDuration / 12;
        }
        keyAnimation.removedOnCompletion = NO;
        keyAnimation.fillMode = kCAFillModeForwards;
        return keyAnimation;
    }
    
    /**
     *  eyes close and open key frame animation
     */
    - (CAKeyframeAnimation *)eyesCloseAndOpenAnimationWithRect:(CGRect)rect {
        CGFloat frameNumber = _animationDuration * 180 / 9;         // 180 frame erver second
        CGFloat eyesX = rect.origin.x;
        CGFloat eyesY = rect.origin.y;
        CGFloat eyesWidth = rect.size.width;
        CGFloat eyesHeight = rect.size.height;
        NSMutableArray *arrayFrame = [NSMutableArray array];
        for (int i = 0; i < frameNumber; i++) {
            if (i < frameNumber / 3) {
                // close
                eyesHeight = eyesHeight - rect.size.height / (frameNumber / 3);
            } else if (i >= frameNumber / 3 && i < frameNumber * 2 / 3) {
                // zero
                eyesHeight = 0;
            } else {
                // open
                eyesHeight = eyesHeight + rect.size.height / (frameNumber / 3);
            }
            eyesY = (rect.size.height - eyesHeight) / 2;
            [arrayFrame addObject:[NSValue valueWithCGRect:CGRectMake(eyesX, eyesY, eyesWidth, eyesHeight)]];
        }
        CAKeyframeAnimation *keyAnimation = [CAKeyframeAnimation animationWithKeyPath:@"eyeRect"];
        keyAnimation.values = arrayFrame;
        keyAnimation.duration = _animationDuration / 3;
        keyAnimation.removedOnCompletion = NO;
        keyAnimation.fillMode = kCAFillModeForwards;
        return keyAnimation;
    }
    
    @end

    创建一个类:继承CALayer

    .h

    #import <QuartzCore/QuartzCore.h>
    #import <UIKit/UIKit.h>
    
    @interface LLEyesLayer : CALayer
    
    /**
     *  eye property
     */
    @property (nonatomic, assign) CGRect eyeRect;
    
    @property (nonatomic, assign) CGFloat eyeDistance;
    
    @property (nonatomic, strong) UIColor *eyeColor;
    
    @property (nonatomic, assign) BOOL isLiking;
    
    @property (nonatomic, assign) CGFloat mouthOffSet;
    
    @property (nonatomic, assign) CGFloat mouthY;
    
    @end

    .m

    #import "LLEyesLayer.h"
    
    @implementation LLEyesLayer
    
    /**
     *  init layer
     *
     *  @return self
     */
    - (instancetype)init {
        if (self = [super init]) {
            // 默认属性
            _eyeRect = CGRectMake(0, 0, 0, 0);
            _mouthOffSet = 0.f;
        }
        return self;
    }
    
    - (instancetype)initWithLayer:(LLEyesLayer *)layer {
        self = [super initWithLayer:layer];
        if (self) {
            self.eyeRect = layer.eyeRect;
            self.eyeDistance = layer.eyeDistance;
            self.eyeColor = layer.eyeColor;
            self.isLiking = layer.isLiking;
            self.mouthOffSet = layer.mouthOffSet;
            self.mouthY = layer.mouthY;
        }
        return self;
    }
    
    /**
     *  draw
     */
    - (void)drawInContext:(CGContextRef)ctx {
        UIBezierPath *bezierLeft = [UIBezierPath bezierPathWithOvalInRect:_eyeRect];
        UIBezierPath *bezierRight = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(_eyeDistance, _eyeRect.origin.y, _eyeRect.size.width, _eyeRect.size.height)];
        
        
        UIBezierPath *bezierMouth = [UIBezierPath bezierPath];
        CGFloat mouthWidth = _eyeRect.size.width + _eyeDistance;
        if (_isLiking) {
            // funny mouth
            [bezierMouth moveToPoint:CGPointMake(0, _mouthY)];
            [bezierMouth addCurveToPoint:CGPointMake(mouthWidth, _mouthY) controlPoint1:CGPointMake(mouthWidth - _mouthOffSet * 3 / 4, _mouthY + _mouthOffSet / 2) controlPoint2:CGPointMake(mouthWidth - _mouthOffSet / 4, _mouthY + _mouthOffSet / 2)];
        } else {
            // boring mouth
            bezierMouth = [UIBezierPath bezierPathWithRect:CGRectMake(0, _mouthY, mouthWidth, _eyeRect.size.height / 4)];
        }
    
        [bezierMouth closePath];
        CGContextAddPath(ctx, bezierLeft.CGPath);
        CGContextAddPath(ctx, bezierRight.CGPath);
        CGContextAddPath(ctx, bezierMouth.CGPath);
        CGContextSetFillColorWithColor(ctx, _eyeColor.CGColor);
        CGContextSetStrokeColorWithColor(ctx, [UIColor redColor].CGColor);
        CGContextFillPath(ctx);
    }
    
    
    /**
     * key animation
     */
    +(BOOL)needsDisplayForKey:(NSString *)key{
        if ([key isEqual:@"mouthOffSet"]) {
            return YES;
        }
        if ([key isEqual:@"eyeRect"]) {
            return YES;
        }
        return [super needsDisplayForKey:key];
    }
    
    @end

    创建一个开关,继承:UIView

    .h

    #import <UIKit/UIKit.h>
    
    @protocol LLSwitchDelegate;
    
    IB_DESIGNABLE @interface LLSwitch : UIView
    
    
    /**
     *  switch on color
     */
    @property (nonatomic, strong) IBInspectable UIColor *onColor;
    
    /**
     *  switch off color
     */
    @property (nonatomic, strong) IBInspectable UIColor *offColor;
    
    /**
     *  face on and off color
     */
    @property (nonatomic, strong) IBInspectable UIColor *faceColor;
    
    /**
     *  the duration is the face moving time
     */
    @property (nonatomic, assign) IBInspectable CGFloat animationDuration;
    
    
    /**
     *  the switch status is or isn't on
     */
    @property (nonatomic, assign) IBInspectable BOOL on;
    
    @property (nonatomic, weak) IBOutlet id <LLSwitchDelegate> delegate;
    
    @end
    
    
    #pragma mark LLSwitch delegate
    @protocol LLSwitchDelegate <NSObject>
    
    @optional
    
    
    - (void)didTapLLSwitch:(LLSwitch *)llSwitch;
    
    
    - (void)animationDidStopForLLSwitch:(LLSwitch *)llSwitch;
    
    @end

    .m

    #import "LLSwitch.h"
    #import "LLAnimationManager.h"
    #import "LLEyesLayer.h"
    
    
    NSString * const FaceMoveAnimationKey = @"FaceMoveAnimationKey";
    NSString * const BackgroundColorAnimationKey = @"BackgroundColorAnimationKey";
    NSString * const EyesMoveStartAnimationKey = @"EyesMoveStartAnimationKey";
    NSString * const EyesMoveEndAnimationKey = @"EyesMoveEndAnimationKey";
    NSString * const EyesMoveBackAnimationKey = @"EyesMoveBackAnimationKey";
    NSString * const MouthFrameAnimationKey = @"MouthFrameAnimationKey";
    NSString * const EyesCloseAndOpenAnimationKey = @"EyesCloseAndOpenAnimationKey";
    
    @interface LLSwitch()
    
    /**
     *  switch background view
     */
    @property (nonatomic, strong) UIView *backgroundView;
    
    /**
     *  face layer
     */
    @property (nonatomic, strong) CAShapeLayer *circleFaceLayer;
    
    /**
     *  paddingWidth
     */
    @property (nonatomic, assign) CGFloat paddingWidth;
    
    /**
     *  eyes layer
     */
    @property (nonatomic, strong) LLEyesLayer *eyesLayer;
    
    /**
     *  face radius
     */
    @property (nonatomic, assign) CGFloat circleFaceRadius;
    
    /**
     *  the faceLayer move distance
     */
    @property (nonatomic, assign) CGFloat moveDistance;
    
    /**
     *  handler layer animation manager
     */
    @property (nonatomic, strong) LLAnimationManager *animationManager;
    
    
    /**
     *  whether is animated
     */
    @property (nonatomic, assign) BOOL isAnimating;
    
    @property (nonatomic, assign) CGFloat faceLayerWidth;
    
    
    
    @end
    
    
    @implementation LLSwitch
    
    - (instancetype)initWithFrame:(CGRect)frame {
        self = [super initWithFrame:frame];
        if (self) {
            [self initSetUpView];
        }
        return self;
    }
    
    - (instancetype)initWithCoder:(NSCoder *)aDecoder {
        self = [super initWithCoder:aDecoder];
        if (self) {
            [self initSetUpView];
        }
        return self;
    }
    
    
    - (void)initSetUpView {
        
        /**
         *  check the switch width and height
         */
        NSAssert(self.frame.size.width >= self.frame.size.height, @"switch width must be tall!");
        
        /**
         *  init property
         */
        _onColor = [UIColor colorWithRed:73/255.0 green:182/255.0 blue:235/255.0 alpha:1.f];
        _offColor = [UIColor colorWithRed:211/255.0 green:207/255.0 blue:207/255.0 alpha:1.f];
        _faceColor = [UIColor whiteColor];
        _paddingWidth = self.frame.size.height * 0.1;
        _circleFaceRadius = (self.frame.size.height - 2 * _paddingWidth) / 2;
        _animationDuration = 1.2f;
        _animationManager = [[LLAnimationManager alloc] initWithAnimationDuration:_animationDuration];
        _moveDistance = self.frame.size.width - _paddingWidth * 2 - _circleFaceRadius * 2;
        _on = NO;
        _isAnimating = NO;
        
        /**
         *  setting init property
         */
        self.backgroundView.backgroundColor = _offColor;
        self.circleFaceLayer.fillColor = _faceColor.CGColor;
        self.faceLayerWidth = self.circleFaceLayer.frame.size.width;
        [self.eyesLayer setNeedsDisplay];
        
        [self addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapSwitch)]];
    }
    
    
    #pragma mark set property
    
    - (void)setBackgroundColor:(UIColor *)backgroundColor {
        return;
    }
    
    - (void)setOffColor:(UIColor *)offColor {
        _offColor = offColor;
        if (!_on) {
            _backgroundView.backgroundColor = offColor;
            _eyesLayer.eyeColor = offColor;
            [self.eyesLayer setNeedsDisplay];
        }
    }
    
    - (void)setOnColor:(UIColor *)onColor {
        _onColor = onColor;
        if (_on) {
            _backgroundView.backgroundColor = onColor;
            _eyesLayer.eyeColor = onColor;
            [self.eyesLayer setNeedsDisplay];
        }
    }
    
    - (void)setFaceColor:(UIColor *)faceColor {
        _faceColor = faceColor;
        _circleFaceLayer.fillColor = faceColor.CGColor;
    }
    
    - (void)setAnimationDuration:(CGFloat)animationDuration {
        _animationDuration = animationDuration;
        _animationManager = [[LLAnimationManager alloc] initWithAnimationDuration:_animationDuration];
    }
    
    - (void)setOn:(BOOL)on {
        _on = on;
        if (on) {
            self.backgroundView.backgroundColor = _onColor;
            self.circleFaceLayer.position = CGPointMake(self.circleFaceLayer.position.x + _moveDistance, self.circleFaceLayer.position.y);
            self.eyesLayer.eyeColor = _onColor;
            self.eyesLayer.isLiking = YES;
            self.eyesLayer.mouthOffSet = _eyesLayer.frame.size.width;
            [self.eyesLayer needsDisplay];
        }
    }
    
    
    #pragma mark GestureRecognizer
    - (void)handleTapSwitch {
        if (_isAnimating) {
            return;
        }
        _isAnimating = YES;
        // faceLayer
        CABasicAnimation *moveAnimation = [_animationManager moveAnimationWithFromPosition:_circleFaceLayer.position toPosition:_on ? CGPointMake(_circleFaceLayer.position.x - _moveDistance, _circleFaceLayer.position.y) : CGPointMake(_circleFaceLayer.position.x + _moveDistance, _circleFaceLayer.position.y)];
        moveAnimation.delegate = self;
        [_circleFaceLayer addAnimation:moveAnimation forKey:FaceMoveAnimationKey];
        
        // backfroundView
        CABasicAnimation *colorAnimation = [_animationManager backgroundColorAnimationFromValue:(id)(_on ? _onColor : _offColor).CGColor toValue:(id)(_on ? _offColor : _onColor).CGColor];
        [_backgroundView.layer addAnimation:colorAnimation forKey:BackgroundColorAnimationKey];
        
        // eyesLayer
        CABasicAnimation *rotationAnimation = [_animationManager eyeMoveAnimationFromValue:@(0) toValue:@(_on ? -_faceLayerWidth : _faceLayerWidth)];
        rotationAnimation.delegate = self;
        [_eyesLayer addAnimation:rotationAnimation forKey:EyesMoveStartAnimationKey];
        _circleFaceLayer.masksToBounds = YES;
        if (_on) {
            [self eyesKeyFrameAnimationStart];
        }
        
        
        // start delegate
        if ([self.delegate respondsToSelector:@selector(didTapLLSwitch:)]) {
            [self.delegate didTapLLSwitch:self];
        }
        
    }
    
    
    
    #pragma mark Init
    
    /**
     *  init backgroundView
     *
     *  @return backgroundView
     */
    - (UIView *)backgroundView {
        if (!_backgroundView) {
            _backgroundView = [[UIView alloc] init];
            _backgroundView.frame = self.bounds;
            _backgroundView.layer.cornerRadius = self.frame.size.height / 2;
            _backgroundView.layer.masksToBounds = YES;
            [self addSubview:_backgroundView];
        }
        return _backgroundView;
    }
    
    
    /**
     *  init circleFaceLayer
     *
     *  @return circleFaceLayer
     */
    - (CAShapeLayer *)circleFaceLayer {
        if (!_circleFaceLayer) {
            _circleFaceLayer = [CAShapeLayer layer];
            [_circleFaceLayer setFrame:CGRectMake(_paddingWidth, _paddingWidth, _circleFaceRadius * 2, _circleFaceRadius *2)];
            UIBezierPath *circlePath = [UIBezierPath bezierPathWithOvalInRect:_circleFaceLayer.bounds];
            _circleFaceLayer.path = circlePath.CGPath;
            [self.backgroundView.layer addSublayer:_circleFaceLayer];
        }
        return _circleFaceLayer;
    }
    
    
    /**
     *  eyes and mouth layer
     *
     *  @return eyesLayer
     */
    - (LLEyesLayer *)eyesLayer {
        if (!_eyesLayer) {
            _eyesLayer = [LLEyesLayer layer];
            _eyesLayer.eyeRect = CGRectMake(0, 0, _faceLayerWidth / 6, _circleFaceLayer.frame.size.height * 0.22);
            _eyesLayer.eyeDistance = _faceLayerWidth / 3;
            _eyesLayer.eyeColor = _offColor;
            _eyesLayer.isLiking = NO;
            _eyesLayer.mouthY = _eyesLayer.eyeRect.size.height * 7 / 4;
            _eyesLayer.frame = CGRectMake(_faceLayerWidth / 4, _circleFaceLayer.frame.size.height * 0.28, _faceLayerWidth / 2, _circleFaceLayer.frame.size.height * 0.72);
        //    _eyesLayer.backgroundColor = [UIColor redColor].CGColor;
            [self.circleFaceLayer addSublayer:_eyesLayer];
    
        }
        return  _eyesLayer;
    }
    
    #pragma mark AnimationDelegate
    - (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag {
        if (flag) {
            
            // start eyes ending animation
            if (anim == [_eyesLayer animationForKey:EyesMoveStartAnimationKey]) {
                _eyesLayer.eyeColor = _on ?  _offColor: _onColor;
                _eyesLayer.isLiking = !_on;
                [_eyesLayer setNeedsDisplay];
                CABasicAnimation *rotationAnimation = [_animationManager eyeMoveAnimationFromValue:@(_on ? _faceLayerWidth : -_faceLayerWidth) toValue:@(_on ? -_faceLayerWidth / 6 :  _faceLayerWidth / 6)];
                rotationAnimation.delegate = self;
                [_eyesLayer addAnimation:rotationAnimation forKey:EyesMoveEndAnimationKey];
                
                if (!_on) {
                    [self eyesKeyFrameAnimationStart];
                }
            }
            
            // start eyes back animation
            if (anim == [_eyesLayer animationForKey:EyesMoveEndAnimationKey]) {
                CABasicAnimation *rotationAnimation = [_animationManager eyeMoveAnimationFromValue:@(_on ? -_faceLayerWidth / 6 :  _faceLayerWidth / 6) toValue:@(0)];
                rotationAnimation.delegate = self;
                [_eyesLayer addAnimation:rotationAnimation forKey:EyesMoveBackAnimationKey];
                
                if (!_on) {
                    CAKeyframeAnimation *eyesKeyFrameAnimation = [_animationManager eyesCloseAndOpenAnimationWithRect:_eyesLayer.eyeRect];
                    [_eyesLayer addAnimation:eyesKeyFrameAnimation forKey:EyesCloseAndOpenAnimationKey];
                }
            }
            
            // eyes back animation end
            if (anim == [_eyesLayer animationForKey:EyesMoveBackAnimationKey]) {
                [_eyesLayer removeAllAnimations];
                _eyesLayer.mouthOffSet = _on ? 0 : _eyesLayer.frame.size.width;
                
                if (_on) {
                    _circleFaceLayer.position = CGPointMake(_circleFaceLayer.position.x - _moveDistance, _circleFaceLayer.position.y);
                    _on = NO;
                } else {
                    _circleFaceLayer.position = CGPointMake(_circleFaceLayer.position.x + _moveDistance, _circleFaceLayer.position.y);
                    _on = YES;
                }
                _isAnimating = NO;
                
                
                // stop delegate
                if ([self.delegate respondsToSelector:@selector(animationDidStopForLLSwitch:)]) {
                    [self.delegate animationDidStopForLLSwitch:self];
                }
            }
        }
    }
    
    /**
     *  add mouth keyFrameAnimation
     */
    - (void)eyesKeyFrameAnimationStart {
        CAKeyframeAnimation *keyAnimation = [_animationManager mouthKeyFrameAnimationWidthOffSet:_eyesLayer.frame.size.width on:_on];
        [_eyesLayer addAnimation:keyAnimation forKey:MouthFrameAnimationKey];
    }
    
    - (void)dealloc {
        self.delegate = nil;
    }
    
    
    @end

    在需要的地方使用即可:

    #import "ViewController.h"
    #import "LLSwitch.h"
    
    @interface ViewController () <LLSwitchDelegate>
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        LLSwitch *llSwitch = [[LLSwitch alloc] initWithFrame:CGRectMake(100, 100, 120, 60)];
        [self.view addSubview:llSwitch];
        llSwitch.delegate = self;
    }
    
    -(void)didTapLLSwitch:(LLSwitch *)llSwitch {
        NSLog(@"start");
    }
    
    - (void)animationDidStopForLLSwitch:(LLSwitch *)llSwitch {
        NSLog(@"stop");
    }
    
    @end
  • 相关阅读:
    对于石家庄铁道大学软件个人总结
    Android Studio安装
    典型用户模板和用户场景模板
    java+jsp+sql server实现网页版四则运算.
    四则运算一
    学习进度
    构建之法阅读笔记(一)
    记账本小程序7天开发记录(第一天)
    javabean+jsp+servlet+jdbc从软件安装到开发实例
    编写一个文件分割工具,能把一个大文件分割成多个小的文件。并且能再次把它们合并起来得到完整的文件。
  • 原文地址:https://www.cnblogs.com/LzwBlog/p/5864570.html
Copyright © 2020-2023  润新知