在创建的路径顶部添加形状

Add shapes on top of created path

具有使用以下代码绘制自定义形状的代码:

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
    }
}

如下所示:

我想在创建的路径顶部添加形状,理想情况下,我希望有一些机制看起来像这样,路径顶部的圆圈是可交互的:

不知道如何在不修改原始路径的情况下实现这一点,也许有更优雅的解决方案可以在自定义路径上绘制形状?

类似问题 -

注意:虽然这是对上面提供的自定义路径的特定问题 - 我正在尝试想出一种方法,通过遵循它们的路径在任何形状之上绘制自定义形状并制作他们可以互动。

简单的说,比如一个点就是一个imageView

分而治之

add shapes on top of the created 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,

使用上面的辅助方法

你可以很容易地得到两条路径,原始路径和圆点路径

冷静一下,你就会得到

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

在您给出的示例中,答案非常简单,因为您已经拥有所需的所有数据。您正在从一组点创建路径,这些点也是您的圈子的位置,因此您只需要保存这些位置。一种方法是将生成点的代码移到 ProgressPath 的定义之外,并修改 ProgressPath,使其只接受任何点数组。然后你可以生成一次数组,用它来创建一个 ProgressPath,然后使用相同的数组来创建你的 circles.

where circles on top of the path are interactable

不清楚您想要什么样的互动。如果您只想 click/tap 在圆圈上,比如说,转到相应的游戏关卡,您将需要创建一个新的视图类型,它可以绘制圆圈并存储您将要执行的数据或操作需要完成交互。例如,您可以创建一个 struct LevelButton 将自身绘制为一个圆并包含一个关卡 ID。如果您希望能够像手柄一样使用圆圈来拖动路径的一部分,或者像串珠一样沿着路径滑动圆圈,事情会变得比我认为我们需要在此处介绍的更复杂。

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

A Path 包含一个 CGPath,其中包含路径元素列表,因此可以编写遍历段列表并提取定位圆圈所需的点的代码。但是,对于一般解决方案,您需要考虑几种不同类型的路径段:不仅是直线段,还有曲线和移动。有关完整列表,请参阅 CGPathElementType。 CGPath 还提供了一些函数,让您可以将自己的函数应用于每个元素,因此您可以编写一个函数来为每个元素类型收集适当的点,这样您最终会得到一个点列表,您可以使用这些点来定位您的圆圈。