iOS 圆角'几'字形按钮

前言

新项目UI很喜欢这种类型的按钮,还设计了动画,只能绘制,问了一圈,没有现成的,只能自己手搓一个,话不多说,先看效果图。


效果图1
效果图2

3.jpg
4.jpg

准备工作

1、会贝塞尔曲线,能画圆弧和直线;
2、理解奇偶、非零填充填充规则;
项目中用的是JXCategoryView,因此,本次封装的indicatorView继承JXCategoryIndicatorComponentView。
下面直接上源码。

代码

//
//  WMSpaceIndicatorView.h
//  demo
//
//  Created by jing on 2023/9/4.
//  Copyright © 2023 jing. All rights reserved.
//

#import <JXCategoryView/JXCategoryView.h>

NS_ASSUME_NONNULL_BEGIN

@interface WMSpaceIndicatorView : JXCategoryIndicatorComponentView

///是否显示底部指示器
@property (nonatomic, assign) BOOL isShowBottomIndicator;

@property (nonatomic, weak) JXCategoryTitleView *categoryView;

@end

NS_ASSUME_NONNULL_END

//
//  WMSpaceIndicatorView.m
//  demo
//
//  Created by jing on 2023/9/4.
//  Copyright © 2023 jing. All rights reserved.
//

#import "WMSpaceIndicatorView.h"

@interface WMSpaceIndicatorView ()

@property (nonatomic, strong) CAShapeLayer *pathLayer;
@property (nonatomic, strong) CALayer *indicatorLayer;
@property (nonatomic, assign) NSInteger selectIndex;

@end

static CGFloat const radius = 12;
@implementation WMSpaceIndicatorView

- (instancetype)init {
    self = [super init];
    if (self) {
        self.isShowBottomIndicator = YES;
    }
    return self;
}


- (void)drawLayerPath {
    UIBezierPath *path = [UIBezierPath bezierPath];
    [path moveToPoint:CGPointMake(0, self.height)];
    
    if (self.selectIndex == 0) {
        [path addLineToPoint:CGPointMake(0, radius)];
        [path addArcWithCenter:CGPointMake(radius, radius) radius:radius startAngle:M_PI endAngle:M_PI * 3 / 2 clockwise:YES];
        
        [path moveToPoint:CGPointMake(radius, 0)];
        [path addLineToPoint:CGPointMake(self.width - radius * 2, 0)];
        
        [path addArcWithCenter:CGPointMake(self.width - radius * 2, radius) radius:radius startAngle:M_PI * 3 / 2 endAngle:M_PI * 2 clockwise:YES];
        
        [path moveToPoint:CGPointMake(self.width - radius, radius)];
        [path addLineToPoint:CGPointMake(self.width - radius, self.height - radius)];
        
        [path moveToPoint:CGPointMake(self.width, self.height)];
        [path addArcWithCenter:CGPointMake(self.width, self.height - radius) radius:radius startAngle:M_PI * 0.5 endAngle:M_PI clockwise:YES];
        
        [path moveToPoint:CGPointMake(0, self.height)];
        [path addLineToPoint:CGPointMake(radius, 0)];
        [path addLineToPoint:CGPointMake(self.width - radius, radius)];

        [path moveToPoint:CGPointMake(0, self.height)];
        [path addLineToPoint:CGPointMake(self.width - radius, radius)];
        [path addLineToPoint:CGPointMake(self.width - radius, self.height - radius)];

        [path moveToPoint:CGPointMake(0, self.height)];
        [path addLineToPoint:CGPointMake(self.width - radius, self.height - radius)];
        [path addLineToPoint:CGPointMake(self.width, self.height)];
    } else if (self.selectIndex == self.categoryView.titles.count - 1) {
        [path addArcWithCenter:CGPointMake(0, self.height - radius) radius:radius startAngle:M_PI * 2 endAngle:M_PI * 0.5 clockwise:YES];
        [path moveToPoint:CGPointMake(0, self.height)];
        [path addLineToPoint:CGPointMake(radius, self.height)];
        [path addLineToPoint:CGPointMake(radius, self.height - radius)];
        
        [path moveToPoint:CGPointMake(radius, self.height - radius)];
        [path addLineToPoint:CGPointMake(radius, radius)];
        
        [path addArcWithCenter:CGPointMake(radius * 2, radius) radius:radius startAngle:M_PI endAngle:M_PI * 3 / 2 clockwise:YES];
        
        [path moveToPoint:CGPointMake(radius * 2, 0)];
        [path addLineToPoint:CGPointMake(self.width - radius, 0)];
        
        [path addArcWithCenter:CGPointMake(self.width - radius, radius) radius:radius startAngle:M_PI * 3 / 2 endAngle:M_PI * 2 clockwise:YES];
        
        
        [path moveToPoint:CGPointMake(radius, self.height - radius)];
        [path addLineToPoint:CGPointMake(radius * 2, 0)];
        [path addLineToPoint:CGPointMake(self.width, radius)];

        [path moveToPoint:CGPointMake(radius , self.height - radius)];
        [path addLineToPoint:CGPointMake(self.width, radius)];
        [path addLineToPoint:CGPointMake(self.width, self.height - radius)];

        [path moveToPoint:CGPointMake(radius , self.height)];
        [path addLineToPoint:CGPointMake(radius, self.height - radius)];
        [path addLineToPoint:CGPointMake(self.width, self.height - radius)];
        [path addLineToPoint:CGPointMake(self.width, self.height)];
        [path addLineToPoint:CGPointMake(radius, self.height)];
    } else {
        [path addArcWithCenter:CGPointMake(0, self.height - radius) radius:radius startAngle:M_PI * 2 endAngle:M_PI * 0.5 clockwise:YES];
        [path moveToPoint:CGPointMake(0, self.height)];
        [path addLineToPoint:CGPointMake(radius, self.height)];
        [path addLineToPoint:CGPointMake(radius, self.height - radius)];
        
        [path moveToPoint:CGPointMake(radius, self.height - radius)];
        [path addLineToPoint:CGPointMake(radius, radius)];
        
        [path addArcWithCenter:CGPointMake(radius * 2, radius) radius:radius startAngle:M_PI endAngle:M_PI * 3 / 2 clockwise:YES];
        
        [path moveToPoint:CGPointMake(radius * 2, 0)];
        [path addLineToPoint:CGPointMake(self.width - radius * 2, 0)];
        
        [path addArcWithCenter:CGPointMake(self.width - radius * 2, radius) radius:radius startAngle:M_PI * 3 / 2 endAngle:M_PI * 2 clockwise:YES];
        
        [path moveToPoint:CGPointMake(self.width - radius, radius)];
        [path addLineToPoint:CGPointMake(self.width - radius, self.height - radius)];
        
        [path moveToPoint:CGPointMake(self.width, self.height)];
        [path addArcWithCenter:CGPointMake(self.width, self.height - radius) radius:radius startAngle:M_PI * 0.5 endAngle:M_PI clockwise:YES];
        
        [path moveToPoint:CGPointMake(self.width, self.height)];
        [path addLineToPoint:CGPointMake(self.width - radius, self.height)];
        [path addLineToPoint:CGPointMake(self.width - radius, self.height - radius)];
        
        [path moveToPoint:CGPointMake(radius * 2, 0)];
        [path addLineToPoint:CGPointMake(radius, self.height - radius)];
        [path addLineToPoint:CGPointMake(self.width - radius, radius)];

        [path moveToPoint:CGPointMake(radius , self.height - radius)];
        [path addLineToPoint:CGPointMake(self.width - radius, radius)];
        [path addLineToPoint:CGPointMake(self.width - radius, self.height - radius)];
        
        [path moveToPoint:CGPointMake(radius , self.height)];
        [path addLineToPoint:CGPointMake(radius, self.height - radius)];
        [path addLineToPoint:CGPointMake(self.width - radius, self.height - radius)];
        [path addLineToPoint:CGPointMake(self.width - radius, self.height)];
        [path addLineToPoint:CGPointMake(radius, self.height)];
    }
    
    [self.pathLayer removeFromSuperlayer];
    self.pathLayer = nil;
    
    self.pathLayer = CAShapeLayer.new;
    self.pathLayer.borderWidth = 0.f;
    self.pathLayer.fillColor = UIColor.whiteColor.CGColor;
    self.pathLayer.strokeColor = UIColor.clearColor.CGColor;
    self.pathLayer.fillRule = kCAFillRuleEvenOdd;
    [self.layer insertSublayer:self.pathLayer atIndex:0];
    self.pathLayer.path = path.CGPath;
    
    if (self.isShowBottomIndicator) [self createIndicatorLayerPath];
}


- (void)createIndicatorLayerPath {
    [self.indicatorLayer removeFromSuperlayer];
    self.indicatorLayer = nil;
    self.indicatorLayer = CALayer.new;
    self.indicatorLayer.frame = CGRectMake(self.width * 0.5 - 11, self.height - 9, 22, 7);
    [self.layer addSublayer:self.indicatorLayer];
    
    //6 mid:11
    UIBezierPath *linePath = [UIBezierPath bezierPath];
    linePath.lineCapStyle = kCGLineCapRound;
    linePath.lineJoinStyle = kCGLineJoinRound;
    [linePath moveToPoint:CGPointMake(0, self.indicatorLayer.frame.size.height - 1)];
    [linePath addLineToPoint:CGPointMake(8, self.indicatorLayer.frame.size.height - 1)];
    [linePath addLineToPoint:CGPointMake(11, 0)];
    [linePath addLineToPoint:CGPointMake(14, self.indicatorLayer.frame.size.height - 1)];
    [linePath addLineToPoint:CGPointMake(22, self.indicatorLayer.frame.size.height - 1)];
    
    CAShapeLayer *lineLayer = [CAShapeLayer new];
    lineLayer.path = linePath.CGPath;
    lineLayer.lineWidth = 1.0f;
    lineLayer.strokeColor = [UIColor colorWithHexString:@"#247CFC" alpha:0.3].CGColor;
    lineLayer.fillColor = UIColor.clearColor.CGColor;
    [self.indicatorLayer addSublayer:lineLayer];
    
    UIBezierPath *trianglePath = [UIBezierPath bezierPath];
    [trianglePath moveToPoint:CGPointMake(9, self.indicatorLayer.frame.size.height)];
    [trianglePath addLineToPoint:CGPointMake(11, 3)];
    [trianglePath addLineToPoint:CGPointMake(13, self.indicatorLayer.frame.size.height)];
    [trianglePath addLineToPoint:CGPointMake(9, self.indicatorLayer.frame.size.height)];
    
    CAShapeLayer *triangleLayer = CAShapeLayer.new;
    triangleLayer.path = trianglePath.CGPath;
    triangleLayer.lineWidth = 1.0f;
    triangleLayer.fillColor = [UIColor colorWithHexString:@"#247CFC" alpha:1].CGColor;
    [self.indicatorLayer addSublayer:triangleLayer];
}


- (CGRect)getIndicatorFrameWithIndex:(NSInteger)selectedIndex {
    self.selectIndex = selectedIndex;
    CGRect selectedFrame = [self.categoryView getTargetCellFrame:selectedIndex];
    NSString *title = self.categoryView.titles[selectedIndex];
    CGFloat textW = [self getWidthBaseOnHeight:22 Font:[UIFont boldSystemFontOfSize:16] text:title] + 20 + radius * 4;
    CGFloat leftW = radius;
    CGFloat rightW = radius * 3;
    CGRect indicatorFrame = CGRectZero;
    CGFloat totoalW = 0;
    if (selectedIndex == 0) {
        totoalW = leftW + textW + rightW ;
        indicatorFrame = CGRectMake(0, 0, totoalW, selectedFrame.size.height);
    } else if (selectedIndex == self.categoryView.titles.count - 1) {
        totoalW = leftW + textW + rightW;
        indicatorFrame = CGRectMake(self.categoryView.collectionView.contentSize.width - totoalW, 0, totoalW, selectedFrame.size.height);
    } else {
        rightW = leftW = radius * 2;
        totoalW = leftW + textW + rightW;
        CGFloat x = CGRectGetMidX(selectedFrame) - totoalW / 2;
        indicatorFrame = CGRectMake(x, 0, totoalW, selectedFrame.size.height);
    }
    
    return indicatorFrame;
}


- (CGFloat)getWidthBaseOnHeight:(CGFloat)height Font:(UIFont *)stringFont text:(NSString *)str {
    CGSize textSize = CGSizeMake(CGFLOAT_MAX, height);
    CGRect calSize = [str boundingRectWithSize:textSize options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName: stringFont} context:nil];
    return ceil(calSize.size.width);
}


#pragma mark - JXCategoryIndicatorProtocol

- (void)jx_refreshState:(JXCategoryIndicatorParamsModel *)model {
    self.frame = [self getIndicatorFrameWithIndex:model.selectedIndex];
    if (self.selectIndex == 0) {
        [self drawLayerPath];
    }
}


- (void)jx_selectedCell:(JXCategoryIndicatorParamsModel *)model {
    CGRect toFrame = [self getIndicatorFrameWithIndex:model.selectedIndex];
    if (self.isScrollEnabled) {
        [UIView animateWithDuration:self.scrollAnimationDuration delay:0 options:UIViewAnimationOptionCurveLinear animations:^{
            self.frame = toFrame;
            self.pathLayer.frame = toFrame;
            [self drawLayerPath];
        } completion:^(BOOL finished) {
        }];
    } else {
        self.frame = toFrame;
        self.pathLayer.frame = toFrame;
        [self drawLayerPath];
    }
}

@end

暂时先这样,以后有时间再优化吧。

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容