还记得大学刚学iOS那会,从学校图书馆借了本iOS开发的书,有一章节介绍了Quartz2D,当时看得一头雾水,感觉这画画线,画画圆有什么用呢🤔️?工作一段时间后,遇到了一些需求,终于可以把咸丰年学的Quartz2D知识拿出来用一用了💪。下面总结下这些年做项目用到Quartz2D的7个例子。
一.单纯画个圈。
有个项目要模仿呆萌价APP顶部轮播图,然后轮播图后面需要有个圆弧:
一看到这需求,不简单吗?找UI小姐姐切个圆弧图片就好啦😄。
但是细细一看。wtf🙀。圆弧颜色是随着轮播图一起改变的:
也就是圆弧会根据录播图的主色调而改变颜色,或许我们可以麻烦UI小姐姐提供多个颜色的切图。但是这样只能是有限个颜色值了。而且图片多了。。。还增加了包大小🤣🤣。
此时只要用Quartz2D,简单几句代码就解决问题啦,寥寥十几行代码还几乎不增加包大小。
1.新建HYBgView,继承View
2.重写drawRect:方法
-(void)drawRect:(CGRect)rect{
CGContextRef context = UIGraphicsGetCurrentContext();
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(0, 0)];
[path addLineToPoint:CGPointMake(0, 60)];
[path addQuadCurveToPoint:CGPointMake(kScreenWidth, 60) controlPoint:CGPointMake(kScreenWidth*0.5, 120)];
[path addLineToPoint:CGPointMake(kScreenWidth, 0)];
[path closePath];
CGContextAddPath(context, path.CGPath);
CGContextSetFillColorWithColor(context, [UIColor colorWithHexString:self.hyColor].CGColor);
CGContextFillPath(context);
}
其中hyColor是我们定义的HYBgView的属性,类型为UIColor:
@interface HYBgView : UIView
@property(nonatomic,strong) NSString *hyColor;
@end
3.重写hyColor的set方法,在修改颜色时让系统进行重绘:
-(void)setHyColor:(NSString *)hyColor{
_hyColor = hyColor;
[self setNeedsDisplay];
}
二.带透明窗口的引导图
在老东家太平洋网络做的项目,如下图的一键海报的新手指引图,需求是遮罩层中间有个镂空的矩形,把第一张海报显示出来,其它地方是50%透明度,当时最终实现效果如图:
我们通过UIBezierPath和CAShapeLayer绘制一张中间镂空(完全透明),其它部分50%透明的遮罩层。
1、新建类PCOnePosterGuide,继承UIView
2、重写drawRect:方法:
- (void)drawRect:(CGRect)rect {
[super drawRect:rect];
//整个view(如下图,蓝色部分,为方便解释上的颜色,实际效果为50%透明,下同)
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:0];
//中间空的部分(如下图:粉红色部分,实际效果为镂空透明)
UIBezierPath *maskPath;
CGFloat maskW = (IOS_VERSION>=10.)?64.:50.;
CGFloat padding = (SCREEN_WIDTH>375)?6.:2.;
maskPath = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(SCREEN_WIDTH-maskW-padding, PCSTATUSBAR_HEIGHT+4, maskW, 36.)
byRoundingCorners:UIRectCornerAllCorners
cornerRadii:CGSizeMake(2, 2)];
[path appendPath:maskPath];
//使用奇偶性原则,设置填充部分为除去粉色的蓝色部分
[path setUsesEvenOddFillRule:YES];
CAShapeLayer *fillLayer = [CAShapeLayer layer];
fillLayer.path = path.CGPath;
fillLayer.fillRule = kCAFillRuleEvenOdd;
fillLayer.fillColor = [UIColor blackColor].CGColor;
fillLayer.opacity = 0.5;
[self.layer addSublayer:fillLayer];
}
为了方便解释,上的颜色,实际为透明
三、排序按钮
如图中的排序按钮(正序/倒序),完全使用Core Graphic进行绘制,可以方便地设置颜色、大小、字体等,而不需要找设计同事拿切图哦😊:
绘制代码:
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
if (_title.length > 0) {
//绘制文字
CGSize textSize = [_title sizeForFont:[UIFont systemFontOfSize:14] size:CGSizeMake(200, 40) mode:NSLineBreakByClipping];
CGFloat textW = textSize.width;
CGFloat textH = textSize.height;
UIColor *selectColor = self.selectColor?:[UIColor colorWithRed:137/255.0 green:67/255.0 blue:167/255.0 alpha:1.0];
UIColor *unselectColor = self.unselectColor?:[UIColor colorWithRed:102/255.0 green:102/255.0 blue:102 /255.0 alpha:1.0];
UIColor *textColor = self.type==HYRankTypeNone?unselectColor:selectColor;
[_title drawInRect:CGRectMake((self.width-textW)/2.0, (self.height-textH)/2.0, textW, self.height) withAttributes:@{NSFontAttributeName:[UIFont systemFontOfSize:14],NSForegroundColorAttributeName:textColor}];
CGFloat triangleW = self.triangleW>0?self.triangleW:10;
CGFloat trianglePadding = 4;
//绘制上三角形
if (self.type == HYRankTypeNone || self.type == HYRankTypeDown) {
CGContextSetFillColorWithColor(context, unselectColor.CGColor);
}else{
CGContextSetFillColorWithColor(context, selectColor.CGColor);
}
CGFloat x1 = (self.width+textW)/2.0+trianglePadding+triangleW/2.0;
CGFloat y1 = self.height/2.0 - triangleW/2.0 - 1;
CGContextMoveToPoint(context, x1, y1);
CGFloat x2 = x1+triangleW/2.0;
CGFloat y2 = y1+triangleW/2.0;
CGContextAddLineToPoint(context, x2, y2);
CGFloat x3 = x1-triangleW/2.0;
CGFloat y3 = y2;
CGContextAddLineToPoint(context, x3, y3);
CGContextClosePath(context);
CGContextFillPath(context);
//绘制下三角形
if (self.type == HYRankTypeNone || self.type == HYRankTypeUp) {
CGContextSetFillColorWithColor(context, unselectColor.CGColor);
}else{
CGContextSetFillColorWithColor(context, selectColor.CGColor);
}
CGFloat a1 = x1;
CGFloat b1 = y1 + triangleW + 2;
CGContextMoveToPoint(context, a1, b1);
CGFloat a2 = x2;
CGFloat b2 = y2 + 2;
CGContextAddLineToPoint(context, a2, b2);
CGFloat a3 = x3;
CGFloat b3 = y3 + 2;
CGContextAddLineToPoint(context, a3, b3);
CGContextClosePath(context);
CGContextFillPath(context);
}
}
排序按钮我简单地封装到了HYRankView,完整代码及详细使用方法点我
4.继续画画:卡片背景
如图中的卡片背景,也是可以通过Quartz2D快速画出来,又为APP包大小立下功劳~
绘制代码:
- (void)drawRect:(CGRect)rect {
[super drawRect:rect];
CGContextRef context = UIGraphicsGetCurrentContext();
//左边半圆
UIBezierPath *leftPath = [UIBezierPath bezierPath];
CGFloat y = 158;
[leftPath addArcWithCenter:CGPointMake(0, y) radius:15 startAngle:3/2.0*M_PI endAngle:1/2.0*M_PI clockwise:YES];
[leftPath closePath];
CGContextAddPath(context, leftPath.CGPath);
CGContextSetFillColorWithColor(context, UIColorHex(#F9F8FF).CGColor);
CGContextFillPath(context);
//右边半圆
UIBezierPath *rightPath = [UIBezierPath bezierPath];
CGFloat rightX = kScreenWidth-32;
[rightPath addArcWithCenter:CGPointMake(rightX, y) radius:15 startAngle:3/2.0*M_PI endAngle:1/2.0*M_PI clockwise:NO];
[rightPath closePath];
CGContextAddPath(context, rightPath.CGPath);
CGContextFillPath(context);
//中间分割虚线
CGContextMoveToPoint(context, 30, y);
CGContextAddLineToPoint(context, kScreenWidth-32-30, y);
CGContextSetLineWidth(context, 1);
CGContextSetStrokeColorWithColor(context, UIColorHex(#666666).CGColor);
CGFloat lengths[] = {3,3};
CGContextSetLineDash(context, 0, lengths,2);
CGContextDrawPath(context, kCGPathStroke);
}
5.贝塞尔曲线+动画
这个demo是业余模仿美人相机的,还没用到实际项目上。效果:
我的另一篇博客写的很详细了,这里就不再累赘:
6.环形进度条
如图。是设计姐姐给的下载进度设计图。
其实就只画两个圈,一个灰色的圆环和一个根据下载百分比动态变化的黄色圆环。代码:
-(void)drawRect:(CGRect)rect{
CGContextRef ctx = UIGraphicsGetCurrentContext();//获取上下文
CGContextSetLineWidth(ctx, _progressWidth); //设置线条宽度
CGPoint center = CGPointMake(rect.size.width / 2, rect.size.height / 2); //设置圆心位置
CGFloat radius = (rect.size.width - 2 * _progressWidth) * 0.5; //设置半径
CGFloat startA = - M_PI_2; //圆起点位置
CGFloat endA = -M_PI_2 + M_PI * 2 * _progress; //圆终点位置
UIBezierPath *bgCirclePath = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:0 endAngle:2*M_PI clockwise:YES];
[[UIColor lightGrayColor] setStroke];//设置描边颜色
CGContextAddPath(ctx, bgCirclePath.CGPath);//把路径添加到上下文
CGContextStrokePath(ctx);//渲染
UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:startA endAngle:endA clockwise:YES];
[UIColorHex(#f3b132) setStroke];
CGContextAddPath(ctx, path.CGPath);
CGContextStrokePath(ctx);
}
在改变进度时,请求重新绘制:
-(void)setProgress:(float)progress
{
_progress = progress;
[self setNeedsDisplay];
}
七、长度动画
业余接的一个美术学院学生的iPad项目(iPad搜索“傣纸的故事”),需要动态展示一些物件的大小尺寸,也不用麻烦设计,直接Quartz2D+Core Animation搞掂。最终效果如下图:
核心代码:
1.画出完整的虚线
- (void)drawRect:(CGRect)rect {
//获得处理的上下文
CGContextRef context = UIGraphicsGetCurrentContext();
//线条宽
CGContextSetLineWidth(context, 2.0);
//线条颜色
CGContextSetStrokeColorWithColor(context, ColorForDardTheme.CGColor);
//画虚线
CGFloat dashArray[] = {3, 1};//表示先画3个实点再画1个虚点,即实点多虚点少表示虚线点大且间隔小,实点少虚点多表示虚线点小且间隔大
CGContextSetLineDash(context, 1, dashArray, 1);//最后的参数1代表排列的个数
//起点坐标
CGContextMoveToPoint(context, 0, rect.size.height/2);
//终点坐标
CGContextAddLineToPoint(context, rect.size.width, rect.size.height/2);
//绘制路径
CGContextStrokePath(context);
}
2.加一个遮罩动画就动起来啦
-(void)showAnimation:(HYPointLineDirect)direct{
CGFloat originFrameX,endPositionX;
switch (direct) {
case HYPointLineDirectLeft:
originFrameX = self.bounds.size.width;
endPositionX = self.bounds.size.width/2;
break;
case HYPointLineDirectRight:
originFrameX = -self.bounds.size.width;
endPositionX = self.bounds.size.width/2;
break;
default:
originFrameX = self.bounds.size.width;
endPositionX = self.bounds.size.width/2;
break;
}
UIView *maskView = [[UIView alloc] initWithFrame:CGRectMake(originFrameX, 0, self.frame.size.width, self.frame.size.height)];
maskView.backgroundColor = [UIColor colorWithRed:0.5 green:0.5 blue:0.5 alpha:1.0];
[self setMaskView:maskView];
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position.x"];
animation.toValue = @(endPositionX);
animation.duration = 1.0;
animation.removedOnCompletion = NO;
animation.fillMode = kCAFillModeBoth;
[maskView.layer addAnimation:animation forKey:nil];
}
暂时就记得这几个例子。后面有再更新,
总结就是三步:
1.新建类继承UIView
2.重写drawRect:方法,在方法内画画
3.在需要刷新时调用setNeedsDisplay进行重绘
可怜的我辛苦敲代码之余还抽空写博客✍️,兄弟底部点个赞鼓励下再走嘛🥺~