某一天清晨
产品: 来弟弟 , 我们做一个进度条吧~ 要那种顶部的.
我:顶部? 那啥 进度条顶部都是直线的 那刘海的会遮挡哇~
产品:遮挡? 这种不是到了刘海跟着系统自动弯曲下来了吗?
我: 这。。。
我当时的心情
一般来说 要在屏幕上画 线条 形状之类的基本上会用 UIBezierPath 来做.
用paintCode 绘图工具模拟画了一个不忍直视的。。
心想 ... 也就这个样子了 去对比一下看大概能不能合上
图中红色线条就是刚才画出来的 . 仔细看了之后 我发现想简单了. 苹果这个刘海并不像是一个圆角那么简单
看到这个玩意儿 又没什么参数 真要自己模拟画的话 有点搞不定 . 网上搜一下这个怪东西 .
特殊的"曲线"
苹果产品中的这个 圆 circle 比较特殊 是一种叫 连续弯曲 (我随便翻译的。。)
这个不只是刘海上 只要是涉及到苹果的产品
都可以看到 我默默的看了一下 我身边的 mac mini 。。
这里有篇文章提到 这个 连续弯曲
截取一下其中重要的信息
给人的感觉就是 弯曲到直线的时候是非常平滑的没有突兀的地方
设计中怎么实现呢 这里参考的 paintcode 的文章
这玩意儿。。 搞得这么复杂。。
有没有现成在 iOS 实现了的代码 . 还真有 是 Swift 的 我也转了下 OC
贴上
//bar的宽度
#define kLineHeight 3
/**
创建刘海屏的bezierPath
*/
-(UIBezierPath *)createNotchPath
{
NotchBarConfig *config = [NotchBarConfig new];
CGFloat lineHeight = kLineHeight;
// CGFloat containerHeight = 2 * config.smallCircleRadius + config.largeCircleRadius + lineHeight;
CGFloat screenWidth = [UIScreen mainScreen].bounds.size.width;
CGFloat leftNotchPoint = (screenWidth - config.notchWidth) / 2 + 0.5;
CGFloat rightNotchPoint = (screenWidth + config.notchWidth) / 2;
CGFloat smallCircleDiameter = 2 * config.smallCircleRadius;
UIBezierPath *bezierPath = [UIBezierPath bezierPath];
[bezierPath moveToPoint:CGPointMake(0, 0)];
// Draw line to small-circle left to `leftNotchPoint`.
[bezierPath addLineToPoint:CGPointMake(leftNotchPoint - config.smallCircleRadius, 0)];
// Draw the small circle left to the `leftNotchPoint`.
// See <https://developer.apple.com/documentation/uikit/uibezierpath/1624358-init#1965853> for the definition of the
// angles in the default coordinate system.
[bezierPath addArcWithCenter:CGPointMake(leftNotchPoint - config.smallCircleRadius, config.smallCircleRadius) radius:config.smallCircleRadius startAngle: -M_PI / 2 endAngle:0 clockwise:true];
// We're moving the the large-circles a bit up.
// This prevents having a straight line between the circles.
// See <https://medium.com/tall-west/no-cutting-corners-on-the-iphone-x-97a9413b94e>
CGFloat verticalOffsetForLargeCircle = 3;
// Draw the large circle right to the `leftNotchPoint`.
[bezierPath addArcWithCenter:CGPointMake(leftNotchPoint + config.largeCircleRadius, smallCircleDiameter - verticalOffsetForLargeCircle) radius:config.largeCircleRadius startAngle: M_PI endAngle:M_PI / 2 clockwise:false];
// Draw line to large-circle underneath and left to `rightNotchPoint`.
[bezierPath addLineToPoint:CGPointMake(rightNotchPoint - config.largeCircleRadius, smallCircleDiameter + config.largeCircleRadius - verticalOffsetForLargeCircle)];
// Draw the large circle left to the `rightNotchPoint`.
[bezierPath addArcWithCenter:CGPointMake(rightNotchPoint - config.largeCircleRadius, smallCircleDiameter - verticalOffsetForLargeCircle) radius:config.largeCircleRadius startAngle:M_PI / 2 endAngle:0 clockwise:false];
// Draw the small circle right to the `rightNotchPoint`.
[bezierPath addArcWithCenter:CGPointMake(rightNotchPoint + config.smallCircleRadius, config.smallCircleRadius) radius:config.smallCircleRadius startAngle:M_PI endAngle:M_PI + M_PI / 2 clockwise:true];
// Draw line to the end of the screen.
[bezierPath addLineToPoint:CGPointMake(screenWidth, 0)];
// And all the way back..
// Therefore we always have to offset the given `height` by the user.
// To visually align the height with the "basic" `GradientLoadingBar`, we have to add one point here.
CGFloat height = lineHeight + 1;
// Have the small-circle at the bottom-path only one third of the size of the upper-path produced visually better results.
CGFloat bottomPathSmallCircleRadius = config.smallCircleRadius / 3;
// Moving the bottom-line in the corners of the small circles just a tiny bit away from the center point to the smartphone corners,
// produced a visually more equal height for the gradient-view.
CGFloat bottomPathHorizontalOffsetForSmallCircle = 0.5;
// Moving the bottom-line just a tiny bit down here produced a visually more equal height for the gradient-view underneath
// the smartphone-frame in the ears and the notch.
CGFloat bottomPathVerticalOffsetForLargeCircle = 0.5;
// Start by moving down at the end of the screen.
[bezierPath addLineToPoint:CGPointMake(screenWidth, height)];
// Draw line to small-circle right to `rightNotchPoint`.
[bezierPath addLineToPoint:CGPointMake(rightNotchPoint + bottomPathSmallCircleRadius, height)];
// Draw the small circle right to the `rightNotchPoint`.
// We're offsetting the center-point with the given user-height here.
[bezierPath addArcWithCenter:CGPointMake(rightNotchPoint + bottomPathSmallCircleRadius + height + bottomPathHorizontalOffsetForSmallCircle, bottomPathSmallCircleRadius + height) radius:bottomPathSmallCircleRadius startAngle:-M_PI / 2 endAngle:-M_PI clockwise:false];
// Draw the large circle left to the `rightNotchPoint`.
// We're using the same center-point as the large-circle above, but with a larger radius here.
[bezierPath addArcWithCenter:CGPointMake(rightNotchPoint - config.largeCircleRadius, smallCircleDiameter - verticalOffsetForLargeCircle + bottomPathVerticalOffsetForLargeCircle) radius:config.largeCircleRadius + height startAngle:0 endAngle:M_PI / 2 clockwise:true];
// Draw line to large-circle underneath and right to `leftNotchPoint`.
[bezierPath addLineToPoint:CGPointMake(leftNotchPoint + config.largeCircleRadius + height, smallCircleDiameter + config.largeCircleRadius - verticalOffsetForLargeCircle + height + bottomPathVerticalOffsetForLargeCircle)];
// Draw the large circle right to the `leftNotchPoint`.
// We're using the same center-point as the large-circle above, but with a larger radius here.
[bezierPath addArcWithCenter:CGPointMake(leftNotchPoint + config.largeCircleRadius, smallCircleDiameter - verticalOffsetForLargeCircle + bottomPathVerticalOffsetForLargeCircle) radius:config.largeCircleRadius + height startAngle:M_PI / 2 endAngle:M_PI clockwise:true];
// Draw the small circle left to the `leftNotchPoint`.
// We're offsetting the center-point with the given user-height here.
[bezierPath addArcWithCenter:CGPointMake(leftNotchPoint - bottomPathSmallCircleRadius - height - bottomPathHorizontalOffsetForSmallCircle, bottomPathSmallCircleRadius + height) radius:bottomPathSmallCircleRadius startAngle:0 endAngle:-M_PI / 2 clockwise:false];
[bezierPath addLineToPoint:CGPointMake(0, height)];
[bezierPath closePath];
return bezierPath;
}
这个代码是实现的上面的那张图:
由多个圆组成 长短半径勾画弧 来组成这个path
附上最后的效果: