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

Rem*_*hem 21 math core-graphics cgpath ios

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

方圆形

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

在squircle的音乐图标

它的确切公式随时可用.但是,我无法弄清楚如何使用例如CGPath绘制它,更不用说填充它,并且能够相当容易地调整它.所有这一切,同时完全符合公式.

Rus*_*nko 15

来自维基百科的报价:Superellipse

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

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

UIBezierPath类有方法: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);
Run Code Online (Sandbox Code Playgroud)


Sun*_*kas 6

接受答案的填充版本,也移植到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()
}
Run Code Online (Sandbox Code Playgroud)

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


Ted*_*Ted 6

在iOS 13 / Xcode 11中,您现在可以使用CALayerCornerCurve

yourLayer.cornerCurver = CALayerCornerCurve.continuous
Run Code Online (Sandbox Code Playgroud)

来源:https : //developer.apple.com/documentation/quartzcore/calayercornercurve

  • 繁荣。这应该是公认的答案。这就是我们 2020 年做事的方式。 (4认同)
  • 爱爱爱❤️ (2认同)
  • 这。变化。一切。XD (2认同)

Nic*_*ari 5

基于上面 Ruslan 和 Sunkas 的答案,我创建了一条将超椭圆“角”与直线段连接起来的路径;即规则圆角矩形的超椭圆模拟(就像在 iPhone X 模拟器边缘看到的遮罩):

extension UIBezierPath {

    static func superellipse(in rect: CGRect, cornerRadius: CGFloat) -> UIBezierPath {

        // (Corner radius can't exceed half of the shorter side; correct if
        // necessary:)
        let minSide = min(rect.width, rect.height)
        let radius = min(cornerRadius, minSide/2)

        let topLeft = CGPoint(x: rect.minX, y: rect.minY)
        let topRight = CGPoint(x: rect.maxX, y: rect.minY)
        let bottomLeft = CGPoint(x: rect.minX, y: rect.maxY)
        let bottomRight = CGPoint(x: rect.maxX, y: rect.maxY)

        // The two points of the segment along the top side (clockwise):
        let p0 = CGPoint(x: rect.minX + radius, y: rect.minY)
        let p1 = CGPoint(x: rect.maxX - radius, y: rect.minY)

        // The two points of the segment along the right side (clockwise):
        let p2 = CGPoint(x: rect.maxX, y: rect.minY + radius)
        let p3 = CGPoint(x: rect.maxX, y: rect.maxY - radius)

        // The two points of the segment along the bottom side (clockwise):
        let p4 = CGPoint(x: rect.maxX - radius, y: rect.maxY)
        let p5 = CGPoint(x: rect.minX + radius, y: rect.maxY)

        // The two points of the segment along the left side (clockwise):
        let p6 = CGPoint(x: rect.minX, y: rect.maxY - radius)
        let p7 = CGPoint(x: rect.minX, y: rect.minY + radius)

        let path = UIBezierPath()
        path.move(to: p0)
        path.addLine(to: p1)
        path.addCurve(to: p2, controlPoint1: topRight, controlPoint2: topRight)
        path.addLine(to: p3)
        path.addCurve(to: p4, controlPoint1: bottomRight, controlPoint2: bottomRight)
        path.addLine(to: p5)
        path.addCurve(to: p6, controlPoint1: bottomLeft, controlPoint2: bottomLeft)
        path.addLine(to: p7)
        path.addCurve(to: p0, controlPoint1: topLeft, controlPoint2: topLeft)

        return path
    }
}
Run Code Online (Sandbox Code Playgroud)

p0代码中的要点p7可以如下图所示:

在此输入图像描述

如果您传递的矩形实际上是正方形并且角半径等于或大于边长的一半,则直线段会折叠(与、等p0“合并” ),并且您会得到标准的超椭圆。p1p2p3