iOS开发-实现自定义Tabbar及tabbar按钮动画效果

news2024/12/25 12:54:00

iOS开发-实现自定义Tabbar及tabbar按钮动画效果

之前整理了一个继承UITabbarController的Tabbar效果
查看

https://blog.csdn.net/gloryFlow/article/details/132012628

这里是继承与UIViewController的INSysTabbarViewController实现及点击tabbar按钮动画效果。

在这里插入图片描述
在这里插入图片描述

一、INSysTabbar自定义

INSysTabbar继承UIView,实现tabbarButton效果

INSysTabbar.h

#import <UIKit/UIKit.h>
#import "INSysTabbarButton.h"
#import "INSysTabbarShapeView.h"

@protocol INSysTabbarDelegate;
@interface INSysTabbar : UIView

@property (nonatomic, weak) id<INSysTabbarDelegate>tabDelegate;        //代理
@property (nonatomic, strong) UIImage *bgroundImage;                    //背景图
@property (nonatomic, strong) INSysTabbarShapeView *tabbarBGShapeView;  //背景图

@property (nonatomic, strong) UIColor *lineColor;                   //线条的颜色
@property (nonatomic, strong) NSArray *dataSources;                 //tabbarItem列表
@property (nonatomic, assign) BOOL showLine;                        //线条的颜色

@property (nonatomic, assign) NSInteger selectedIndex;              //选中的tabbar按钮index

- (instancetype)initWithFrame:(CGRect)frame;

/**
 默认设置第几个index
 */
- (void)resetSysTabbar:(NSInteger)index;

/**
 更新tabbar样式
 
 @param tabbarItem item
 */
- (void)updateTabbarStyle:(INSysTabbarItem *)tabbarItem;

@end

@protocol INSysTabbarDelegate <NSObject>

- (void)tabBar:(INSysTabbar *)tabBar tabDidSelectedIndex:(NSInteger)index;

@end

INSysTabbar.m

#import "INSysTabbar.h"
#import "UIView+SafeEdgeInsets.h"
#import "UIColor+Addition.h"

static CGFloat kLineHeight = 1.0;
static CGFloat kPadding = 5.0;

@interface INSysTabbar ()

@property (nonatomic, strong) UIImageView *bgImageView;
@property (nonatomic, strong) UIImageView *lineImageView;
@property (nonatomic, assign) CGFloat safeInsetBottom;
@property (nonatomic, strong) NSMutableArray *buttonViews;

@end

@implementation INSysTabbar

- (instancetype)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
        self.buttonViews = [NSMutableArray arrayWithCapacity:0];
        [self addSubview:self.bgImageView];
        [self addSubview:self.lineImageView];
        [self addSubview:self.tabbarBGShapeView];
        self.showLine = NO;
    }
    return self;
}

- (void)layoutSubviews {
    [super layoutSubviews];
    self.bgImageView.frame = self.bounds;
    self.tabbarBGShapeView.frame = self.bounds;
    self.safeInsetBottom = [UIView baseSafeAreaEdgeInsets].bottom;
    
    if (self.dataSources && self.dataSources.count > 0) {
        CGFloat width = CGRectGetWidth(self.bounds) / self.dataSources.count;
        CGFloat height = CGRectGetHeight(self.bounds);
        for (UIView *subView in self.subviews) {
            if ([subView isKindOfClass:[INSysTabbarButton class]]) {
                INSysTabbarButton *tabbarButton = (INSysTabbarButton *)subView;
                CGRect imageBounds = CGRectMake(0.0, 0.0, width, height);
                CGPoint imageCenter = CGPointMake((tabbarButton.tag + 0.5) * width, height/2 - self.safeInsetBottom/2);
                tabbarButton.bounds = imageBounds;
                tabbarButton.center = imageCenter;
            }
        }
    }
    
    self.lineImageView.frame = CGRectMake(0.0, 0.0, CGRectGetWidth(self.bgImageView.frame), kLineHeight);
    
    [self setTabbarSubview];
}

/**
 更新系统tabbar的选中状态
 */
- (void)updateTabbarButtons {
    for (UIView *subView in self.subviews) {
        if ([subView isKindOfClass:[INSysTabbarButton class]]) {
            INSysTabbarButton *tabbarButton = (INSysTabbarButton *)subView;
            if (tabbarButton.tag == self.selectedIndex) {
                tabbarButton.selected = YES;
            } else {
                tabbarButton.selected = NO;
            }
        }
    }
}

/**
 隐藏系统的tabbarButton
 */
- (void)setTabbarSubview {
    for (UIView *child in self.subviews) {
        Class class = NSClassFromString(@"UITabBarButton");
        if ([child isKindOfClass:class]) {
            child.hidden = YES;
        }
    }
}

/**
 重新创建tabbarButtons
 */
- (void)setupTabbarButtons {
    
    [self.buttonViews removeAllObjects];
    
    for (UIView *subView in self.subviews) {
        if ([subView isKindOfClass:[INSysTabbarButton class]]) {
            [subView removeFromSuperview];
        }
    }
    
    for (NSInteger index = 0; index < self.dataSources.count; index ++) {
        INSysTabbarItem *tabbarItem = [self.dataSources objectAtIndex:index];
        INSysTabbarButton *tabbarButton = [[INSysTabbarButton alloc] initWithFrame:CGRectZero];
        tabbarButton.userInteractionEnabled = YES;
        tabbarButton.tabbarItem = tabbarItem;
        tabbarButton.tag = index;
        tabbarButton.maxCircleSize = 40.0;
        [tabbarButton addTarget:self action:@selector(tabbarButtonSelected:) forControlEvents:UIControlEventTouchUpInside];
        [self addSubview:tabbarButton];
        [self.buttonViews addObject:tabbarButton];
    }
    
    [self.bgImageView bringSubviewToFront:self.lineImageView];
    
    [self setNeedsLayout];
}

- (void)setDataSources:(NSArray *)dataSources {
    _dataSources = dataSources;
    [self setupTabbarButtons];
    [self setNeedsLayout];
}

- (void)setBgroundImage:(UIImage *)bgroundImage {
    _bgroundImage = bgroundImage;
    self.bgImageView.image = bgroundImage;
    [self setNeedsLayout];
}

- (void)setLineColor:(UIColor *)lineColor {
    _lineColor = lineColor;
    self.lineImageView.backgroundColor = lineColor;
    [self setNeedsLayout];
}

- (void)setShowLine:(BOOL)showLine {
    _showLine = showLine;
    self.lineImageView.hidden = !showLine;
    [self setNeedsLayout];
}

- (void)setSelectedIndex:(NSInteger)selectedIndex {
    _selectedIndex = selectedIndex;
    [self updateTabbarButtons];
    if (self.tabDelegate && [self.tabDelegate respondsToSelector:@selector(tabBar:tabDidSelectedIndex:)]) {
        [self.tabDelegate tabBar:self tabDidSelectedIndex:selectedIndex];
    }
}

/**
 更新tabbar样式
 
 @param tabbarItem item
 */
- (void)updateTabbarStyle:(INSysTabbarItem *)tabbarItem {
    for (UIView *subView in self.subviews) {
        if ([subView isKindOfClass:[INSysTabbarButton class]]) {
            INSysTabbarButton *tabbarButton = (INSysTabbarButton *)subView;
            INSysTabbarItem *item = tabbarButton.tabbarItem;
            if (tabbarItem.identifier && [tabbarItem.identifier isEqualToString:item.identifier]) {
                //更新tabbar
                [item copyClone:tabbarItem];
                tabbarButton.tabbarItem = item;
                break;
            }
        }
    }
}

/**
 默认设置第几个index
 */
- (void)resetSysTabbar:(NSInteger)index {
    INSysTabbarButton *selectedButton = nil;
    for (INSysTabbarButton *subButton in self.buttonViews) {
        if (subButton.tag == index) {
            selectedButton = subButton;
        }
    }
    
    if (selectedButton) {
        [self buttonClick:selectedButton];
        self.selectedIndex = selectedButton.tag;
    }
}

#pragma mark - Actions
- (void)tabbarButtonSelected:(INSysTabbarButton *)tabbarButton {
    if (self.selectedIndex != tabbarButton.tag) {
        [self buttonClick:tabbarButton];
    }
    self.selectedIndex = tabbarButton.tag;
}

- (void)buttonClick:(INSysTabbarButton *)button {
    CGRect buttonFrame = button.frame;
    self.tabbarBGShapeView.anchorPointX = CGRectGetMidX(buttonFrame);
    
    for (INSysTabbarButton *subButton in self.buttonViews) {
        subButton.hasShowedCircle = NO;
        CGRect subButtonFrame = subButton.frame;
        subButtonFrame.origin.y = 0.0;
        subButton.frame = subButtonFrame;
        [subButton reset];
    }
    
    [button circleAnimationDuration:0.5];
}

- (void)addShakeAnimation:(UIView *)view {
    CABasicAnimation* shake = [CABasicAnimation animationWithKeyPath:@"transform.translation.y"];
    shake.fromValue = [NSNumber numberWithFloat:-5];
    shake.toValue = [NSNumber numberWithFloat:5];
    shake.duration = 0.1;//执行时间
    shake.autoreverses = YES; //是否重复
    shake.repeatCount = 1;//次数
    [view.layer addAnimation:shake forKey:@"shakeAnimation"];
}

#pragma mark - GETTER
- (UIImageView *)bgImageView {
    if (!_bgImageView) {
        _bgImageView = [[UIImageView alloc] initWithFrame:CGRectZero];
        _bgImageView.backgroundColor = [UIColor clearColor];
        _bgImageView.clipsToBounds = YES;
    }
    return _bgImageView;
}

- (INSysTabbarShapeView *)tabbarBGShapeView {
    if (!_tabbarBGShapeView) {
        _tabbarBGShapeView = [[INSysTabbarShapeView alloc] initWithFrame:CGRectZero];
        _tabbarBGShapeView.buttonSize = 30.0;
        _tabbarBGShapeView.anchorPointX = 0.0;
    }
    return _tabbarBGShapeView;
}

- (UIImageView *)lineImageView {
    if (!_lineImageView) {
        _lineImageView = [[UIImageView alloc] initWithFrame:CGRectZero];
        _lineImageView.backgroundColor = [UIColor colorWithHexString:@"f3f3f3" alpha:1.0];
    }
    return _lineImageView;
}

@end

二、TabbarButton实现

UIButton继承UIControl,我这里继承UIControl实现自定义按钮

INSysTabbarButton.h

#import <UIKit/UIKit.h>
#import "INSysTabbarItem.h"

@interface INSysTabbarButton : UIControl

@property (nonatomic, strong) UIImageView *bkImageView;
@property (nonatomic, strong) INSysTabbarItem *tabbarItem;
@property (nonatomic, assign) BOOL hasShowedCircle;   // 当前显示的
@property (nonatomic) CGFloat maxCircleSize;             // circle的size

- (instancetype)initWithFrame:(CGRect)frame;

- (void)reset;

- (void)circleAnimationDuration:(CGFloat)aDuration;

@end

INSysTabbarButton.m

#import "INSysTabbarButton.h"
#import "NSString+Size.h"

static CGFloat kIconSize = 26.0;
static CGFloat kTitleHeight = 18.0;
static CGFloat kBadgeSize = 8.0;
static CGFloat kPadding = 5.0;
static CGFloat defaultBadgeRadius = 9.0;
static CGFloat defaultDotRadius = 5.0;

#define kTabbarDotShown @"dotShown"
#define kTabbarBadge @"badge"

@interface INSysTabbarButton ()

@property (nonatomic, strong) UIImageView *iconImageView;
@property (nonatomic, strong) UIImageView *badgeImageView;
@property (nonatomic, strong) UILabel *titleLabel;
@property (nonatomic, strong) UILabel *badgeLabel;

@end

@implementation INSysTabbarButton

#pragma mark - INIT
- (instancetype)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
        [self addSubview:self.bkImageView];
        [self addSubview:self.iconImageView];
        [self addSubview:self.titleLabel];
        [self addSubview:self.badgeImageView];
        [self addSubview:self.badgeLabel];
    }
    return self;
}

- (void)layoutSubviews {
    [super layoutSubviews];
        
    CGSize titleSize = [self.titleLabel.text sizeWithFont:self.titleLabel.font forMaxSize:CGSizeMake(CGRectGetWidth(self.bounds), kTitleHeight)];
    
    CGFloat titleHeight = MIN(ceil(titleSize.height), kTitleHeight);
    
    CGFloat iconSize = 0.0;
    if (self.iconImageView.image) {
        iconSize = kIconSize;
    }
    self.titleLabel.frame = CGRectMake(0.0, CGRectGetHeight(self.bounds) - kPadding - titleHeight, CGRectGetWidth(self.bounds), titleHeight);
    
    if (self.hasShowedCircle) {
        self.iconImageView.frame = CGRectMake((CGRectGetWidth(self.bounds) - iconSize)/2, CGRectGetMinY(self.titleLabel.frame) - self.maxCircleSize + (self.maxCircleSize - iconSize)/2, iconSize, iconSize);
    } else {
        self.iconImageView.frame = CGRectMake((CGRectGetWidth(self.bounds) - iconSize)/2, CGRectGetMinY(self.titleLabel.frame) - iconSize, iconSize, iconSize);
    }
    
    self.bkImageView.center = self.iconImageView.center;
    
    CGSize badgeSize = [self.badgeLabel.text sizeWithFont:self.badgeLabel.font forMaxSize:CGSizeMake(20.0, 20.0)];
    CGFloat minWidth = MAX(defaultBadgeRadius * 2, badgeSize.width + 10.0);
    CGFloat minHight = MAX(defaultBadgeRadius * 2, badgeSize.height);
    
    CGRect badgeBounds = CGRectMake(0.0, 0.0, minWidth, minHight);
    CGPoint badgeCenter = CGPointMake(CGRectGetMidX(self.iconImageView.frame) + CGRectGetHeight(badgeBounds), CGRectGetMidY(self.iconImageView.frame) - CGRectGetHeight(badgeBounds)/2 + 5.0);
    self.badgeLabel.bounds = badgeBounds;
    self.badgeLabel.center = badgeCenter;
    self.badgeLabel.layer.cornerRadius = minHight / 2;
}

#pragma mark - SETTER
- (void)setTabbarItem:(INSysTabbarItem *)tabbarItem {
    _tabbarItem = tabbarItem;
    
    //设置icon
    self.iconImageView.image = tabbarItem.image;
    
    //设置标题
    self.titleLabel.font = tabbarItem.titleFont;
    self.titleLabel.textColor = tabbarItem.titleColor;
    self.titleLabel.text = [NSString stringWithFormat:@"%@",(tabbarItem.title?tabbarItem.title:@"")];
    
    //设置红点
    self.badgeImageView.hidden = !tabbarItem.dotShown;
    
    //设置badge
    self.badgeLabel.backgroundColor = tabbarItem.badgeColor;
    self.badgeLabel.text = [NSString stringWithFormat:@"%@",(tabbarItem.badge?tabbarItem.badge:@"")];
    if(tabbarItem.badge && tabbarItem.badge.length > 0) {
        self.badgeLabel.hidden = NO;
    } else {
        self.badgeLabel.hidden = YES;
    }
    
    [self addObserver];
    [self setNeedsLayout];
}

- (void)setSelected:(BOOL)selected {
    [super setSelected:selected];
    if (selected) {
        self.titleLabel.textColor = self.tabbarItem.selectedTitleColor;
        self.iconImageView.image = self.tabbarItem.selectedImage;
    } else {
        self.titleLabel.textColor = self.tabbarItem.titleColor;
        self.iconImageView.image = self.tabbarItem.image;
    }
}

- (void)setHasShowedCircle:(BOOL)hasShowedCircle {
    _hasShowedCircle = hasShowedCircle;
    if (hasShowedCircle) {
        self.bkImageView.alpha = 1.0;
    } else {
        self.bkImageView.alpha = 0.0;
    }
}

- (void)circleAnimationDuration:(CGFloat)aDuration {
    self.hasShowedCircle = YES;
    self.bkImageView.alpha = 0.0;
    self.bkImageView.transform = CGAffineTransformMakeScale(0.1, 0.1);

    CGRect iconFrame = self.iconImageView.frame;
    iconFrame.origin.y = CGRectGetMinY(self.titleLabel.frame) - self.maxCircleSize + (self.maxCircleSize - iconFrame.size.height)/2;
    
    [UIView animateWithDuration:aDuration animations:^{
        self.bkImageView.transform = CGAffineTransformMakeScale(1.0, 1.0);
        self.bkImageView.alpha = 1.0;
        self.iconImageView.frame = iconFrame;
    } completion:^(BOOL finished) {
        [self.iconImageView.layer removeAnimationForKey:@"shakeAnimation"];
        [self startIconShakeAnimation];
    }];
}

- (void)startIconShakeAnimation {
    CABasicAnimation* shake = [CABasicAnimation animationWithKeyPath:@"transform.translation.y"];
    shake.fromValue = [NSNumber numberWithFloat:-3];
    shake.toValue = [NSNumber numberWithFloat:3];
    shake.duration = 0.15;//执行时间
    shake.autoreverses = YES; //是否重复
    shake.repeatCount = 1;//次数
    [self.iconImageView.layer addAnimation:shake forKey:@"shakeAnimation"];
}

- (void)reset {
    self.hasShowedCircle = NO;
    [self layoutSubviews];
    [self.iconImageView.layer removeAnimationForKey:@"shakeAnimation"];
}

#pragma mark - GETTER
- (UIImageView *)bkImageView {
    if (!_bkImageView) {
        _bkImageView = [[UIImageView alloc] initWithFrame:CGRectZero];
        _bkImageView.backgroundColor = [UIColor colorWithHexString:@"ff244c" alpha:0.25];
        _bkImageView.contentMode = UIViewContentModeScaleAspectFit;
        _bkImageView.clipsToBounds = YES;
        _bkImageView.frame = CGRectMake(0.0, 0.0, 40.0, 40.0);
        _bkImageView.layer.cornerRadius = 20.0;
        _bkImageView.layer.masksToBounds = YES;
        _bkImageView.alpha = 0.0;
    }
    return _bkImageView;
}

- (UIImageView *)iconImageView {
    if (!_iconImageView) {
        _iconImageView = [[UIImageView alloc] initWithFrame:CGRectZero];
        _iconImageView.backgroundColor = [UIColor clearColor];
        _iconImageView.contentMode = UIViewContentModeScaleAspectFit;
        _iconImageView.clipsToBounds = YES;
    }
    return _iconImageView;
}

- (UIImageView *)badgeImageView {
    if (!_badgeImageView) {
        _badgeImageView = [[UIImageView alloc] initWithFrame:CGRectZero];
        _badgeImageView.backgroundColor = [UIColor clearColor];
        _badgeImageView.frame = CGRectMake(0.0, 0.0, kBadgeSize, kBadgeSize);
        _badgeImageView.layer.cornerRadius = kBadgeSize/2;
        _badgeImageView.layer.masksToBounds = YES;
        _badgeImageView.hidden = YES;
    }
    return _badgeImageView;
}

- (UILabel *)titleLabel {
    if (!_titleLabel) {
        _titleLabel = [[UILabel alloc]initWithFrame:CGRectZero];
        _titleLabel.backgroundColor = [UIColor clearColor];
        _titleLabel.textAlignment = NSTextAlignmentCenter;
    }
    return _titleLabel;
}

- (UILabel *)badgeLabel {
    if (!_badgeLabel) {
        _badgeLabel = [[UILabel alloc]initWithFrame:CGRectZero];
        _badgeLabel.backgroundColor = [UIColor clearColor];
        _badgeLabel.textAlignment = NSTextAlignmentCenter;
        _badgeLabel.clipsToBounds = YES;
        _badgeLabel.textColor = [UIColor whiteColor];
        _badgeLabel.font = [UIFont systemFontOfSize:11];
    }
    return _badgeLabel;
}

#pragma mark KVO Refresh
- (void)addObserver{
    __weak typeof(self) weakSelf = self;
    [self.KVOController observe:self.tabbarItem keyPath:kTabbarBadge options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew block:^(id  _Nullable observer, id  _Nonnull object, NSDictionary<NSString *,id> * _Nonnull change) {
        if ([change objectForKey:NSKeyValueChangeNewKey]) {
            __strong typeof(weakSelf) strongSelf = weakSelf;
            NSString *badge = weakSelf.tabbarItem.badge;
            weakSelf.badgeLabel.text = [NSString stringWithFormat:@"%@",(badge?badge:@"")];
            if(badge && badge.length > 0) {
                strongSelf.badgeLabel.hidden = NO;
            } else {
                strongSelf.badgeLabel.hidden = YES;
            }
            [strongSelf setNeedsLayout];
        }
    }];

    [self.KVOController observe:self.tabbarItem keyPath:kTabbarDotShown options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew block:^(id  _Nullable observer, id  _Nonnull object, NSDictionary<NSString *,id> * _Nonnull change) {
        if ([change objectForKey:NSKeyValueChangeNewKey]) {
            __strong typeof(weakSelf) strongSelf = weakSelf;
            strongSelf.badgeImageView.hidden = !strongSelf.tabbarItem.dotShown;
            [strongSelf setNeedsLayout];
        }
    }];
}

- (void)removeObserver{
    [self.KVOController unobserveAll];
}

- (void)dealloc {
    [self removeObserver];
}
@end

三、实现切换点击按钮动画效果

当点击tabbar按钮时候,被点击的按钮上移,tabbar显示凸起效果。实现CAShapeLayer结合path,最后由CADisplayLink控制动画效果。

代码如下

INSysTabbarShapeView.h

#import <UIKit/UIKit.h>
#import "UIColor+Addition.h"

@interface INSysTabbarShapeView : UIView

@property (nonatomic, assign) CGFloat anchorPointX; // 圆角的中心点
@property (nonatomic, assign) CGFloat buttonSize; // 按钮的大小

@end

INSysTabbarShapeView.m

#import "INSysTabbarShapeView.h"

@interface INSysTabbarShapeView ()

@property (nonatomic, strong) CAShapeLayer *shapeLayer;
@property (nonatomic, strong) UIBezierPath *path;
@property (nonatomic, strong) CADisplayLink *displayLink;
@property (nonatomic, assign) int currentFrame;
@property (nonatomic, assign) BOOL animationStepOne;
@property (nonatomic, assign) BOOL animationStepTwo;

@end

@implementation INSysTabbarShapeView

- (instancetype)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
        [self.layer addSublayer:self.shapeLayer];
                
        self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(updateShapeConfig)];
        [self.displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
        self.displayLink.paused = YES;
        
        [self configPath:0.0 btnSize:0.0];
    }
    return self;
}

- (void)configPath:(CGFloat)btnScale btnSize:(CGFloat)btnSize{
    
    CGFloat width = CGRectGetWidth(self.bounds);
    CGFloat height = CGRectGetHeight(self.bounds);
    
    CGFloat btnWidth = btnScale*btnSize;
    
    CGFloat distance = btnScale*10;
        
    CGPoint pointA = CGPointMake(0.0, 0.0);
    CGPoint pointB = CGPointMake(0.0, height);

    CGPoint pointC = CGPointMake(width, height);
    CGPoint pointD = CGPointMake(width, 0.0);
    
    CGPoint pointF = CGPointMake(self.anchorPointX + btnWidth + distance, 0.0);
    CGPoint pointE = CGPointMake(self.anchorPointX - btnWidth - distance, 0.0);
    
    CGPoint pointK = CGPointMake(pointE.x+distance, 0.0);
    CGPoint pointL = CGPointMake(pointF.x-distance, 0.0);

    CGPoint pointH = CGPointMake(pointK.x+distance, -btnWidth/4);
    CGPoint pointI = CGPointMake(pointL.x-distance, -btnWidth/4);

    CGPoint pointG = CGPointMake(self.anchorPointX, -btnWidth*3.5/4);

    UIBezierPath *path = [UIBezierPath bezierPath];
    [path moveToPoint:pointA];
    [path addLineToPoint:pointB];
    [path addLineToPoint:pointC];
    [path addLineToPoint:pointD];
    [path addLineToPoint:pointF];

    [path addQuadCurveToPoint:pointI controlPoint:pointL];

    [path addQuadCurveToPoint:pointH controlPoint:pointG];

    [path addQuadCurveToPoint:pointE controlPoint:pointK];

    [path addLineToPoint:pointA];

    [path closePath];

    self.shapeLayer.path = path.CGPath;
}

- (void)setAnchorPointX:(CGFloat)anchorPointX {
    _anchorPointX = anchorPointX;
    self.currentFrame = 1;
    self.animationStepOne = NO;
    self.animationStepTwo = NO;
    self.displayLink.paused = NO;
}

- (void)setButtonSize:(CGFloat)buttonSize {
    _buttonSize = buttonSize;
}

- (void)updateShapeConfig {
    // 先放大的大一点,之后回到正常的大小
    if (!self.animationStepOne) {
        CGFloat animationDuration = 0.5;
        int maxFrames = animationDuration / self.displayLink.duration;
        self.currentFrame++;
        
        CGFloat scale = 1.0;
        if (self.currentFrame <= maxFrames) {
            scale = ((CGFloat)(_currentFrame) / (CGFloat)(maxFrames));
        }else {
            scale = 1.0;
            self.animationStepOne = YES;
            self.animationStepTwo = NO;
            
            // 第二阶段时间为0.15,maxFrames为
            CGFloat stepTwoAnimationDuration = 0.35;
            int stepTwoMaxFrames = stepTwoAnimationDuration / self.displayLink.duration;
            self.currentFrame = stepTwoMaxFrames;
        }
        
        [self configPath:scale btnSize:self.buttonSize*1.1];
    } else {
        if (!self.animationStepTwo) {
            CGFloat animationDuration = 0.35;
            int maxFrames = animationDuration / self.displayLink.duration;
            self.currentFrame--;
            
            CGFloat scale = 1.0;
            if (self.currentFrame > 1 && scale > 0.9) {
                scale = 1.0 - ((CGFloat)(_currentFrame) / (CGFloat)(maxFrames));
            }else {
                scale = 0.9;
                self.currentFrame = 1;
                self.animationStepTwo = YES;
                self.displayLink.paused = YES;
            }
            
            if (scale < 0.9) {
                scale = 0.9;
                
                self.currentFrame = 1;
                self.animationStepTwo = YES;
                self.displayLink.paused = YES;
            }
            
            [self configPath:scale btnSize:self.buttonSize*1.1];
        }
    }
}

#pragma mark - SETTER/GETTER
- (CAShapeLayer *)shapeLayer {
    if (!_shapeLayer) {
        _shapeLayer = [CAShapeLayer layer];
        _shapeLayer.fillColor = [UIColor whiteColor].CGColor;
        _shapeLayer.shadowColor = [UIColor colorWithHexString:@"acacac"].CGColor;
        _shapeLayer.shadowOffset = CGSizeMake(0, -1);
        _shapeLayer.shadowOpacity = 0.25;
        _shapeLayer.shadowRadius = 2.0;
    }
    return _shapeLayer;
}

@end

四、实现Tabbar的INSysTabbarViewController控制器

INSysTabbarViewController继承UIViewController

通过在viewDidLoad中将Tabbar放置到view。

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    self.view.exclusiveTouch = YES;
    
    // iOS 7.0 以上系统的处理
    self.automaticallyAdjustsScrollViewInsets = NO;
    self.extendedLayoutIncludesOpaqueBars = YES;
    self.edgesForExtendedLayout = UIRectEdgeAll;
    self.navigationController.navigationBar.translucent = NO;
    
    // 将tabbar放置到view上
    [self.view addSubview:self.sysTabbar];
}

在点击tabbar按钮进行切换显示对应的controller时候,实现tabbar的delegate

#pragma mark - SDTabBarDelegate
- (void)tabBar:(INSysTabbar *)tabBar tabDidSelectedIndex:(NSInteger)index {
    self.selectedIndex = index;
    
    UIViewController *viewController = [self.viewControllers objectAtIndex:index];
    viewController.view.tag = index;
    viewController.view.frame = CGRectMake(0.0, 0.0, kTabScreenWidth, kTabScreenHeight);
    [self.view insertSubview:viewController.view belowSubview:self.sysTabbar];
    [self addChildViewController:viewController];
}

将需要显示的controller显示的insertSubview。

INSysTabbarViewController代码如下

INSysTabbarViewController.h

#import <UIKit/UIKit.h>
#import "INSysTabbar.h"
#import "UIView+SafeEdgeInsets.h"
#import "UIViewController+SysTabbarItem.h"
#import "SDBaseViewController.h"

@interface INSysTabbarViewController : SDBaseViewController

// 如果是tabbar嵌套Navigation时候,这个selectedNavigationController不为null
// 如果Navigation嵌套tabbar时候,这个selectedNavigationController可能为null
@property (nonatomic, strong, readonly) UINavigationController *selectedNavigationController;

@property (nonatomic, strong) NSArray *tabViewControllers;
@property (nonatomic, strong) INSysTabbar *sysTabbar;
@property (nonatomic, assign) NSInteger selectedIndex;

- (void)reset;

@end

INSysTabbarViewController.m

#import "INSysTabbarViewController.h"
#import "UIViewController+SysTabbarItem.h"
#import "UIImage+Color.h"

#define kTabScreenWidth [UIScreen mainScreen].bounds.size.width
#define kTabScreenHeight [UIScreen mainScreen].bounds.size.height

#define K_TAB_DEFAULT_INDEX  0

@interface INSysTabbarViewController ()<INSysTabbarDelegate>

@property (nonatomic, strong, readwrite) UINavigationController *selectedNavigation;
@property (nonatomic, assign) float systemTabBarHeight;
@property (nonatomic, strong) NSArray *viewControllers;
@property (nonatomic, assign) BOOL isViewApeared;

@end

@implementation INSysTabbarViewController

- (instancetype)init
{
    self = [super init];
    if (self) {
        self.isViewApeared = NO;
        
        UIEdgeInsets aSafeEdgeInsets = UIEdgeInsetsMake(0.0, 0.0, 0.0, 0.0);
        if (@available(iOS 11.0, *)) {
            UIEdgeInsets safeInsets = [UIApplication sharedApplication].keyWindow.safeAreaInsets;
            aSafeEdgeInsets = safeInsets;
        } else {
            // Fallback on earlier versions
        }
        
        UITabBarController *systemTabbar = [[UITabBarController alloc] init];
        self.systemTabBarHeight = systemTabbar.tabBar.frame.size.height + aSafeEdgeInsets.bottom;
        
        self.sysTabbar = [[INSysTabbar alloc] initWithFrame:CGRectZero];
        self.sysTabbar.frame = CGRectMake(0.0, kTabScreenHeight - self.systemTabBarHeight, kTabScreenWidth, self.systemTabBarHeight);
        self.sysTabbar.tabDelegate  = self;
        
        UIImage *bgImage = [UIImage imageWithColor:[UIColor colorWithWhite:1.0 alpha:1.0]];
        self.sysTabbar.bgroundImage = bgImage;
    }
    return self;
}


- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    self.view.exclusiveTouch = YES;
    
    // iOS 7.0 以上系统的处理
    self.automaticallyAdjustsScrollViewInsets = NO;
    self.extendedLayoutIncludesOpaqueBars = YES;
    self.edgesForExtendedLayout = UIRectEdgeAll;
    self.navigationController.navigationBar.translucent = NO;
    
    // 将tabbar放置到view上
    [self.view addSubview:self.sysTabbar];
}

- (void)loadView {
    [super loadView];
}

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
}

- (void)viewWillDisappear:(BOOL)animated {
    [super viewWillDisappear:animated];
}

- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    
    if (!self.isViewApeared) {
        [self.sysTabbar resetSysTabbar:K_TAB_DEFAULT_INDEX];
        self.isViewApeared = YES;
    }
}

- (void)viewDidDisappear:(BOOL)animated {
    [super viewDidDisappear:animated];
}

#pragma mark - SETTER
- (void)setTabViewControllers:(NSArray *)tabViewControllers {
    _tabViewControllers = tabViewControllers;
    
    NSMutableArray *tabbarItems = [NSMutableArray arrayWithCapacity:0];
    for (UIViewController *viewController in tabViewControllers) {
        INSysTabbarItem *item = nil;
        if ([viewController isKindOfClass:[UINavigationController class]]) {
            item = ((UIViewController *)((UINavigationController *)viewController).viewControllers.firstObject).sysTabBarItem;
        } else {
            item = viewController.sysTabBarItem;
        }
        [tabbarItems addObject:item];
        
        // 添加子Controller
        [self addChildViewController:viewController];
    }
    
    self.sysTabbar.dataSources = tabbarItems;
    
    self.viewControllers = tabViewControllers;
}

#pragma mark - SDTabBarDelegate
- (void)tabBar:(INSysTabbar *)tabBar tabDidSelectedIndex:(NSInteger)index {
    self.selectedIndex = index;
    
    UIViewController *viewController = [self.viewControllers objectAtIndex:index];
    viewController.view.tag = index;
    viewController.view.frame = CGRectMake(0.0, 0.0, kTabScreenWidth, kTabScreenHeight);
    [self.view insertSubview:viewController.view belowSubview:self.sysTabbar];
    [self addChildViewController:viewController];
}

#pragma mark - reset
- (void)reset {
    [self.viewControllers enumerateObjectsUsingBlock:^(__kindof UIViewController * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        if ([obj isKindOfClass:[UINavigationController class]]) {
            [(UINavigationController *)obj popToRootViewControllerAnimated:NO];
        }
    }];
    
    [self.sysTabbar setSelectedIndex:K_TAB_DEFAULT_INDEX];
}

- (UINavigationController *)selectedNavigationController {
    UIViewController *viewController = [self.viewControllers objectAtIndex:self.selectedIndex];
    if (viewController.navigationController) {
        return viewController.navigationController;
    }
    
    return nil;
}

#pragma mark - didReceiveMemoryWarning
- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

@end

五、使用到的TabbarItem

TabbarItem定义显示的按钮icon,按钮标题,badge等

INSysTabbarItem.h

#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>

@interface INSysTabbarItem : NSObject

@property (nonatomic, strong) NSString *identifier;
@property (nonatomic, strong) NSString *title;
@property (nonatomic, strong) UIFont *titleFont;
@property (nonatomic, strong) UIImage *image;
@property (nonatomic, strong) UIImage *selectedImage;
@property (nonatomic, strong) UIColor *titleColor;
@property (nonatomic, strong) UIColor *selectedTitleColor;
@property (nonatomic, strong) UIColor *badgeColor;
@property (nonatomic, strong) NSString *badge;
@property (nonatomic, assign) BOOL dotShown;

/**
 赋值
 
 @param item item
 */
- (void)copyClone:(INSysTabbarItem *)item;

- (id)initWithTitle:(NSString *)title
          titleFont:(UIFont *)titleFont
              image:(UIImage *)image
      selectedImage:(UIImage *)selectedImage
         titleColor:(UIColor *)titleColor
 selectedTitleColor:(UIColor *)selectedTitleColor
         badgeColor:(UIColor *)badgeColor;

@end

INSysTabbarItem.m

#import “INSysTabbarItem.h”

@implementation INSysTabbarItem

- (id)initWithTitle:(NSString *)title
          titleFont:(UIFont *)titleFont
              image:(UIImage *)image
      selectedImage:(UIImage *)selectedImage
         titleColor:(UIColor *)titleColor
 selectedTitleColor:(UIColor *)selectedTitleColor
         badgeColor:(UIColor *)badgeColor {
    self = [super init];
    if (self) {
        self.title = title;
        self.titleFont = titleFont;
        self.image = image;
        self.selectedImage = selectedImage;
        self.titleColor = titleColor;
        self.selectedTitleColor = selectedTitleColor;
        self.badge = [[NSString alloc] init];
        self.dotShown = NO;
        self.badgeColor = badgeColor;
    }
    return self;
}

/**
 赋值
 
 @param item item
 */
- (void)copyClone:(INSysTabbarItem *)item {
    self.title = item.title;
    self.image = item.image;
    self.selectedImage = item.selectedImage;
    self.titleColor = item.titleColor;
    self.selectedTitleColor = item.selectedTitleColor;
    self.badgeColor = item.badgeColor;
}

@end

六、为UIViewController添加扩展属性sysTabBarItem

UIViewController+SysTabbarItem.h

#import <UIKit/UIKit.h>
#import "INSysTabbarItem.h"

@interface UIViewController (SysTabbarItem)

@property (nonatomic, strong) INSysTabbarItem *sysTabBarItem;

@end

UIViewController+SysTabbarItem.m

#import "UIViewController+SysTabbarItem.h"
#import <objc/runtime.h>

static const void *sysTabBarItemKey = &sysTabBarItemKey;

@implementation UIViewController (SysTabbarItem)

- (INSysTabbarItem *)sysTabBarItem {
    return objc_getAssociatedObject(self, sysTabBarItemKey);
}

- (void)setSysTabBarItem:(INSysTabbarItem *)sysTabBarItem {
    objc_setAssociatedObject(self, sysTabBarItemKey, sysTabBarItem, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

@end

七、小结

iOS开发-实现自定义Tabbar及tabbar按钮动画效果。

学习记录,每天不停进步。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/817342.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

学习记录——TransNormerLLM

Scaling TransNormer to 175 Billion Parametes 线性注意力的Transformer大模型 2023 Transformer 存在局限。首要的一点&#xff0c;它们有着对于序列长度的二次时间复杂度&#xff0c;这会限制它们的可扩展性并拖累训练和推理阶段的计算资源和时间效率。 TransNormerLLM 是首…

中小企业如何低成本实施MES管理系统

中小企业在市场竞争中需要有高效的管理体系来支持其运营和发展。中小企业MES管理系统是一种先进的管理系统&#xff0c;可以提升工厂智能化水平&#xff0c;提高生产效率&#xff0c;是中小企业必须采取的有效管理工具。然而&#xff0c;由于资金和技术的限制&#xff0c;中小企…

Java API指南:掌握常用工具类与字符串操作

文章目录 1. API简介2. Java API的使用2.1 创建和使用Java API工具类2.2 使用String类进行字符串操作 结语 导语&#xff1a; Java作为一门功能强大的编程语言&#xff0c;其成功之处不仅在于语法结构的简洁明了&#xff0c;更因为其丰富的API&#xff08;Application Programm…

面向对象中的多态性

一、权限修饰符 public, 缺省&#xff0c; protected&#xff0c;private 二、this和super关键字 this:表示当前对象 super:表示父类声明的成员 原则&#xff1a;遵循就近原则和追根溯源原则。 三、Object类 java.lang.Object类是所有java类的超类&#xff0c;即所有的J…

微信小程序测试要点

一、什么是小程序&#xff1f; 可以将小程序理解为轻便的APP&#xff0c;不用安装就可以使用的应用。用户通过扫一扫或者搜索的方式&#xff0c;就可以打开应用。 小程序最主要的特点是内嵌于微信之中&#xff0c;而使用小程序的目的是为了能够方便用户不在受下载多个APP的烦…

更好搭建负载测试环境的六个技巧

如果你如我昨天谈到的客户一样&#xff0c;花费了24到48个小时用于每个负载测试环境的搭建&#xff0c;那你的测试及构建部署能力绝对是受限的。 搭建一个仿真测试环境对于做好负载测试非常重要&#xff0c;同时它也是一个非常具有挑战性的任务&#xff0c;需要考虑技术解决、…

2023 7-31

题目1 寻找不同二叉树两节点的公共祖先 递归解法 仔细看这个解法更加容易理解: l、r 非空时,说明 p、q 分居 root 的两侧,root 就是 LCAl、r 任一为空,说明 LCA 位于另一子树或其祖先中代码 /*** Definition for a binary tree node.* struct TreeNode {* int val;* …

展锐USB充电图标更新流程

介绍 power_supply 目录下online节点是用于判断是否插入battery、ac(外部电源) 和USB 的节点&#xff0c;目录在sys/class/power_supply/battery(ac、usb)/online&#xff0c;主要用于在StatusBar 上显示充电的闪电图标。 SystemUI层介绍 流程介绍 在SystemUI 中控制充电图…

【ChatGPT辅助学Rust | 基础系列 | 基础语法】变量,数据类型,运算符,控制流

文章目录 简介&#xff1a;一&#xff0c;变量1&#xff0c;变量的定义2&#xff0c;变量的可变性3&#xff0c;变量的隐藏 二、数据类型1&#xff0c;标量类型2&#xff0c;复合类型 三&#xff0c;运算符1&#xff0c;算术运算符2&#xff0c;比较运算符3&#xff0c;逻辑运算…

Redis系列二:Clion+MAC+Redis环境搭建

1. ClionMACRedis-3.0-annotated环境搭建 参考&#xff1a; https://github.com/huangz1990/redis-3.0-annotated https://gitee.com/dumpcao/redis-3.0-annotated-cmake-in-clion https://tool.4xseo.com/a/12910.html 1.1 下载并导入Clion git clone https://gitee.com/dum…

基于SSM+JSP+LayUI的校园任务帮管理系统

校园帮项目 校园即时服务平台 用户角色 管理员 功能 登录、公告管理&#xff08;发布公告、停用公告&#xff09;、任务管理&#xff08;下架任务、删除任务&#xff09;、用户管理&#xff08;用户充值、限制用户&#xff09;、修改密码 用户角色 用户 功能 注册、登录…

Allied Telesis 证实 AR4050S-5G 路由器已成功通过 Splashtop On-Prem 快速处理现场数据

日本东京 —— Allied Telesis Inc. 和 NTT Comware Corporation 很高兴地宣布&#xff0c;Allied Telesis AR4050S-5G 路由器经证实已与 Splashtop On-Prem 解决方案成功集成&#xff0c;可安全快速地处理现场数据。根据测试结果&#xff0c;使用该方案&#xff0c;可以在实地…

dolphinscheduler switch+传参无坑版

dolphinscheduler 的前后传参有较多的坑&#xff0c;即便是3.0.5版本仍然有一些bug 下面是目前能无坑在3.0.5版本上使用的操作 前置任务 在界面上设置变量和参数名称 跟官方网站不一样&#xff0c;注意最后一行一定使用echo ${setValue(key$query)}的方式&#xff0c;注意引…

一次web网页设计实践——checkbox单选、复选功能的实现

由于工作内容原因近期做了一个网页&#xff0c;记录下。 需求&#xff1a; 写一个如下的页面&#xff0c;包括checkbox单选&#xff0c;checkbox多选&#xff0c;slect&#xff0c;text等控件 内容&#xff1a; 一、checkbox &#xff08;Wlan 开关&#xff09; 要求&#x…

基于Java+SpringBoot制作一个学生公寓管理小程序

制作一个学生公寓管理小程序,旨在优化和简化学生公寓的日常管理工作。该系统涵盖了各种功能模块,以满足学生住宿的需求,同时提供方便、高效的管理方式,该系统包含用户管理、卫生评比、来访登记、宿舍报修等模块。 一、小程序1.1 项目创建1.2 首页轮播图快捷导航iconfont图标…

git撤销提交,新建、删除分支汇总

目录 git 撤销中间某次提交&#xff0c;保留其他提交的方法git 撤销已经push的代码git 新建分支git 删除分支 git 撤销中间某次提交&#xff0c;保留其他提交的方法 git revert commit_id 通过git log 获取commit_id。 如果commit_id是merge节点的话&#xff0c;-m是指定具…

一种嵌入式LCD显示多国语言方法

简介 介绍一种嵌入式LCD显示多国语言方法&#xff0c;由于很多产品嵌入式资源有限&#xff0c;显示的字符也不多&#xff0c;所以可以自己制作一些字库&#xff0c;而不用字库芯片。 下面展示一种从字库取出字符的方法。 代码示例 #include <stdio.h> #include <s…

安装win版本的neo4j(2023最新版本)

安装win版本的neo4j 写在最前面安装 win版本的neo4j1. 安装JDK2.下载配置环境变量&#xff08;也可选择直接点击快捷方式&#xff0c;就可以不用配环境了&#xff09;3. 启动neo4j 测试代码遇到的问题及解决&#xff08;每次环境都太离谱了&#xff0c;各种问题&#xff09;连接…

八大排序算法--选择排序(动图理解)

选择排序 算法思路 每一次从待排序的数据元素中选出最小&#xff08;或最大&#xff09;的一个元素&#xff0c;存放在序列的起始位置&#xff0c;直到全部待排序的数据元素排完 。 选择排序的步骤&#xff1a; 1>首先在未排序序列中找到最小&#xff08;大&#xff09;元素…

原创 | 数字身份智能体的基本原理及应用前景展望

作者&#xff1a;张家林 本文约5700字&#xff0c;建议阅读10分钟 本文主要探讨自然人数字身份智能体的基本原理、关键技术及其应用前景的挑战。 数字身份智能体&#xff08;DIAs: digital identity agents&#xff09;是通过将一个实体的行为模式、个体特征等信息经过数据化、…