以编程方式绘制iOS 7风格的squircle

我正试图找到一种方法,使用核心图形以编程方式绘制iOS 7风格的图标’squircle’形状。 我不是问如何绘制圆角矩形 。 一个松鼠是一个超级椭圆:

方圆形

这与常规的圆角矩形略有不同: 圆角矩形与squircle

在squircle的音乐图标

它的确切公式很容易获得 。 但是,我无法弄清楚如何使用例如CGPath绘制它,更不用说填充它,并且能够相当容易地调整它。 所有这些都与公式完全一致。

来自维基百科的报价: Superellipse

特别地,对于n = 1/2,四个弧中的每一个是由两个轴限定的二次贝塞尔曲线 ; 结果,每个弧都是抛物线的一部分。

那么为什么不尝试使用Bezier曲线来逼近Squircle呢? 两条曲线(Bezier和Squircle)由参数方程定义。

UIBezierPath类有方法: addCurveToPoint:controlPoint1:controlPoint2: controlPoint1 : addCurveToPoint:controlPoint1:controlPoint2:

将三次Bézier曲线附加到接收器的路径。

注意:使用addQuadCurveToPoint:controlPoint:方法会产生更糟糕的结果 – 经过测试。

我使用了这种方法,这就是结果:

red line – 圆角矩形, blue line – 来自四个贝塞尔曲线的矩形

圆角矩形与立方贝塞尔曲线

如果对此结果感兴趣 – 请在下面绘制代码。

注意:为了实现更精确的匹配,可以要求贝塞尔曲线来改变corner points的坐标(现在它们对应于刻在图中的矩形的角度)。

 CGContextRef context = UIGraphicsGetCurrentContext(); CGContextSaveGState(context); //set rect size for draw float rectSize = 275.; CGRect rectangle = CGRectMake(CGRectGetMidX(rect) - rectSize/2, CGRectGetMidY(rect) - rectSize/2, rectSize, rectSize); //Rounded rectangle CGContextSetStrokeColorWithColor(context, [UIColor redColor].CGColor); UIBezierPath* roundedPath = [UIBezierPath bezierPathWithRoundedRect:rectangle cornerRadius:rectSize/4.7]; [roundedPath stroke]; //Rectangle from Fours Bezier Curves CGContextSetStrokeColorWithColor(context, [UIColor blueColor].CGColor); UIBezierPath *bezierCurvePath = [UIBezierPath bezierPath]; //set coner points CGPoint topLPoint = CGPointMake(CGRectGetMinX(rectangle), CGRectGetMinY(rectangle)); CGPoint topRPoint = CGPointMake(CGRectGetMaxX(rectangle), CGRectGetMinY(rectangle)); CGPoint botLPoint = CGPointMake(CGRectGetMinX(rectangle), CGRectGetMaxY(rectangle)); CGPoint botRPoint = CGPointMake(CGRectGetMaxX(rectangle), CGRectGetMaxY(rectangle)); //set start-end points CGPoint midRPoint = CGPointMake(CGRectGetMaxX(rectangle), CGRectGetMidY(rectangle)); CGPoint botMPoint = CGPointMake(CGRectGetMidX(rectangle), CGRectGetMaxY(rectangle)); CGPoint topMPoint = CGPointMake(CGRectGetMidX(rectangle), CGRectGetMinY(rectangle)); CGPoint midLPoint = CGPointMake(CGRectGetMinX(rectangle), CGRectGetMidY(rectangle)); //Four Bezier Curve [bezierCurvePath moveToPoint:midLPoint]; [bezierCurvePath addCurveToPoint:topMPoint controlPoint1:topLPoint controlPoint2:topLPoint]; [bezierCurvePath moveToPoint:midLPoint]; [bezierCurvePath addCurveToPoint:botMPoint controlPoint1:botLPoint controlPoint2:botLPoint]; [bezierCurvePath moveToPoint:midRPoint]; [bezierCurvePath addCurveToPoint:topMPoint controlPoint1:topRPoint controlPoint2:topRPoint]; [bezierCurvePath moveToPoint:midRPoint]; [bezierCurvePath addCurveToPoint:botMPoint controlPoint1:botRPoint controlPoint2:botRPoint]; [bezierCurvePath stroke]; CGContextRestoreGState(context); 

已接受答案的填充版本,也移植到Swift:

 override func draw(_ rect: CGRect) { super.draw(rect) guard let context = UIGraphicsGetCurrentContext() else { return } context.saveGState() let rect = self.bounds let rectSize: CGFloat = rect.width let rectangle = CGRect(x: rect.midX - rectSize / 2, y: rect.midY - rectSize / 2, width: rectSize, height: rectSize) let topLPoint = CGPoint(x: rectangle.minX, y: rectangle.minY) let topRPoint = CGPoint(x: rectangle.maxX, y: rectangle.minY) let botLPoint = CGPoint(x: rectangle.minX, y: rectangle.maxY) let botRPoint = CGPoint(x: rectangle.maxX, y: rectangle.maxY) let midRPoint = CGPoint(x: rectangle.maxX, y: rectangle.midY) let botMPoint = CGPoint(x: rectangle.midX, y: rectangle.maxY) let topMPoint = CGPoint(x: rectangle.midX, y: rectangle.minY) let midLPoint = CGPoint(x: rectangle.minX, y: rectangle.midY) let bezierCurvePath = UIBezierPath() bezierCurvePath.move(to: midLPoint) bezierCurvePath.addCurve(to: topMPoint, controlPoint1: topLPoint, controlPoint2: topLPoint) bezierCurvePath.addCurve(to: midRPoint, controlPoint1: topRPoint, controlPoint2: topRPoint) bezierCurvePath.addCurve(to: botMPoint, controlPoint1: botRPoint, controlPoint2: botRPoint) bezierCurvePath.addCurve(to: midLPoint, controlPoint1: botLPoint, controlPoint2: botLPoint) context.setFillColor(UIColor.lightGray.cgColor) bezierCurvePath.fill() context.restoreGState() } 

非常适合在UIView子类中使用。

这不是一个很好的答案,因为它没有触及您所询问的内容,如何以编程方式绘制超椭圆**,但您可以:

  1. 在这里下载适用于iOS7图标形状的SVG: http ://dribbble.com/shots/1127699-iOS-7-icon-shape-PSD
  2. 将其导入Xcode项目
  3. 将PocketSVG添加到您的项目: https : //github.com/arielelkin/PocketSVG
  4. 加载SVG,并将其转换为UIBezierPath,然后您可以根据需要进行缩放和转换:

     PocketSVG *myVectorDrawing = [[PocketSVG alloc] initFromSVGFileNamed:@"iOS_7_icon_shape"]; UIBezierPath *myBezierPath = myVectorDrawing.bezier; // Apply your transforms here: [myBezierPath applyTransform:CGAffineTransformMakeScale(2.5, 2.5)]; [myBezierPath applyTransform:CGAffineTransformMakeTranslation(10, 50)]; CAShapeLayer *myShapeLayer = [CAShapeLayer layer]; myShapeLayer.path = myBezierPath.CGPath; myShapeLayer.strokeColor = [[UIColor redColor] CGColor]; myShapeLayer.lineWidth = 2; myShapeLayer.fillColor = [[UIColor clearColor] CGColor]; [self.view.layer addSublayer:myShapeLayer]; 

**也许值得注意的是,形状可能不是一个精确的超椭圆: http ://i.imgur.com/l0ljVRo.png

使用着色器在OpenGL ES中这很容易做到。 只需绘制四边形并将x和y作为顶点属性传递。 在片段着色器中,将x和y插入等式中。 如果结果<= 1,则片段在形状内。 如果我可以获得一些空闲时间,我可以试试这个并在这里发布。

如果你想使用CGPath,我认为关键是用t来表示x和y,它从0到2π。 然后定期评估x和y。 我也会在空闲时间尝试解决这个问题,但我的数学有点生疏。

顺便说一句,我确定Apple 没有使用这个公式。 请参阅@millimoose发布的链接: http ://blog.mikeswanson.com/post/62341902567/unleashing-genetic-algorithms-on-the-ios-7-icon

Interesting Posts