Add shapes on top of created path

Viewed 275

Having code that draws custom shape with the following code:

struct ProgressPath: Shape {
    var numberOfPoints: Int = 2

    var animatableData: CGFloat {
        get { CGFloat(numberOfPoints) }
        set { numberOfPoints = Int(newValue) }
    }

    var sectionsByItems: [IndexPath] {
        var result: [IndexPath] = []
        for index in 0..<numberOfPoints {
            guard let lastItem = result.last?.row else {
                result.append(IndexPath(item: 0,
                                        section: 0))
                continue
            }

            let currentSection = Int(index / 2)

            if index % 2 == 0 {
                result.append(IndexPath(item: lastItem,
                                        section: currentSection))
            } else {
                result.append(IndexPath(item: lastItem == 1 ? 0 : 1,
                                        section: currentSection))
            }
        }

        return result
    }

    func point(indexPath: IndexPath,
               rect: CGRect)-> CGPoint {
        let distance = rect.width - 40
        return CGPoint(x: indexPath.row == 0 ? distance : 40,
                       y: (rect.height - 20) - 50 * (CGFloat(indexPath.section)))
    }

    func path(in rect: CGRect) -> Path {
        var path = Path()

        sectionsByItems
            .forEach { indexPath in
                let newPoint = point(indexPath: indexPath,
                                     rect: rect)

                if indexPath.section == 0 &&
                    indexPath.row == 0 {
                    path.move(to: newPoint)
                } else {
                    path.addLine(to: newPoint)
                }
            }

        return path
    }
}

Which looks like the following:

I want to add shapes on top of the created path, ideally, I want to have some mechanism that should look something like this where circles on top of the path are interactable:

Little lost how this can be achieved without modifying the original path, maybe there's a more elegant solution that can draw shapes over a custom path?

Similar questions - How to create game map for iOS?

Notes: While this is a specific question to the custom path provided above - I'm trying to come up with a method to draw custom shapes on top of any shape by following their path and make them interactable.

2 Answers

Put it simple, for example, a dot is an imageView

Let's divide and conquer

add shapes on top of the created path,

the key point is the center of the dots.

If you got the center of the dots, you know their frames which have the same size,

Once you knew their frames, just put the dots above the path

struct ProgressPath: Shape {

    func centers(in rect: CGRect) -> [CGPoint] {
        var ctrs = [CGPoint]()

        sectionsByItems
            .forEach { indexPath in
                let newPoint = point(indexPath: indexPath,
                                     rect: rect)

                ctrs.append(newPoint)
            }

        return ctrs
    }
}

add shapes on top of the created path,

with the helper method above

you can easily get two path, the original one and the dots one

Just take a chill, and you'll get it

Little lost how this can be achieved without modifying the original path,

The answer is pretty simple in the example you've given because you already have all the data you need. You're creating the path from a set of points that are also the locations for your circles, so all you need is to save those locations. One approach would be to move the code that generates the points outside the definition of ProgressPath, and modify ProgressPath such that it just takes any array of points. Then you can generate the array once, use it to create a ProgressPath, and then use the same array to create your circles.

where circles on top of the path are interactable

It's not clear what kind of interaction you have in mind. If you just want to click/tap on the circles and, say, go to the corresponding game level, you'll want to create a new view type that can draw the circle and also store the data or action that you'll need to accomplish the interaction. For example, you could create a struct LevelButton that draws itself as a circle and contains a level ID. If you want to be able to use the circles like handles to drag part of the path, or maybe slide the circles along the path like beads on a string, things get more complex than I think we need to cover here.

maybe there's a more elegant solution that can draw shapes over a custom path?

A Path contains a CGPath, which contains a list of path elements, so it's possible to write code that walks the list of segments and extracts the points you need to position your circles. However, there are several different types of path segments that you'd need to consider for a general solution: not just straight line segments, but also curves and moves. See CGPathElementType for the complete list. CGPath also provides functions that let you apply your own function to each element, so you could write a function that collects the appropriate points for each element type, so that you end up with a list of points that you can use to position your circles.

Related