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




准备工作
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
暂时先这样,以后有时间再优化吧。
网友评论