KingHodor
KingHodor

Reputation: 547

How to Scale and Center UIBezierPath?

I am working with BezierPath and could make a basic star shape within a view successfully, it works great. However, the view's size can be changeable. And I cannot center this bezier path when the view size is changed, I managed to scale it but getting center was hard to me.

enter image description here image2

private func pointFrom(_ angle: CGFloat, radius: CGFloat, offset: CGPoint) -> CGPoint {
    return CGPoint(x: radius * cos(angle) + offset.x, y: radius * sin(angle) + offset.y)//CGPointMake(radius * cos(angle) + offset.x, radius * sin(angle) + offset.y)
}

private func generateStar(width:CGFloat, height:CGFloat, radius:CGFloat) -> CAShapeLayer{
    let path = UIBezierPath()
    var scale = width / height
    if width > height{
        scale = height / width
    }

    let starExtrusion:CGFloat = 60.0

    let center = CGPoint(x: width / 2.0, y: height / 2.0)

    let pointsOnStar = 5

    var angle:CGFloat = -CGFloat(.pi / 2.0)
    let angleIncrement = CGFloat(.pi * 2.0 / Double(pointsOnStar))
    let radius = width / 2.0

    var firstPoint = true

    for _ in 1...pointsOnStar {

        let point = pointFrom(angle, radius: radius, offset: center)
        let nextPoint = pointFrom(angle + angleIncrement, radius: radius, offset: center)
        let midPoint = pointFrom(angle + angleIncrement / 2.0, radius: starExtrusion, offset: center)

        if firstPoint {
            firstPoint = false
            path.move(to: point)
        }

        path.addLine(to: midPoint)//addLineToPoint(midPoint)
        path.addLine(to: nextPoint)//addLineToPoint(nextPoint)

        angle += angleIncrement
    }
    path.apply(CGAffineTransform(scaleX: scale, y: scale))
    path.close()

    let maskLayer = CAShapeLayer()
    maskLayer.path = path.cgPath

    return maskLayer
}

How can I manage to make that? Thanks!

Upvotes: 0

Views: 1396

Answers (2)

vacawama
vacawama

Reputation: 154563

The center of your path moves with this command:

path.apply(CGAffineTransform(scaleX: scale, y: scale))

To keep your center in the center, scale it by 1 / scale so that when it is scaled by scale, it will end up in the center of the view (since (1 / scale) * scale == 1):

Change:

let center = CGPoint(x: width / 2.0, y: height / 2.0)

to:

let center = CGPoint(x: width / 2.0 / scale, y: height / 2.0 / scale)

The two scalings cancel each other and the center of the path then ends up where it was originally calculated to be.


That said, you are shrinking your star when height != width. You might get a better result if you forget about the scaling and just choose the minimum of width and height for your radius calculation:

Get rid of scale, or set it to 1.0, then

Change:

let radius = width / 2.0

to:

let radius = min(width, height) / 2.0

That will let the star be as large as possible while still fitting fully within the view.


As you noticed, the star gets "fat" (more circular) as it gets smaller. This is because you have a hard-coded constant of 60.0 for starExtrusion. That value should be based upon the radius.

Set starExtrusion after calculating radius:

let radius = min(width, height) / 2.0
let starExtrusion = radius * 0.4

You can vary the 0.4 multiplier to alter the shape of the star.

Upvotes: 2

duan
duan

Reputation: 8845

UIView's layoutSubviews got called when resized, you can reset the mask by overriding that method.

Upvotes: 1

Related Questions