Быстрые альтернативы для UIBezierPath - PullRequest
1 голос
/ 04 апреля 2019

Я делаю приложение, которое может строить графики. Я работаю с UIBezierPath, и все было хорошо, пока я не решил сделать возможным перемещение и масштабирование графика с помощью жестов.

вот как я рисую:

var isFirstPoint = true
        color.set()
        funcPath.lineWidth = width

        for i in 0...Int(bounds.size.width * contentScaleFactor) {
            let cgX = CGFloat(i) / contentScaleFactor
            let funcX = Double ((cgX - bounds.width / 2) / scale)

            let funcY = function(funcX) * Double(scale)

            guard !funcY.isNaN else { continue }
            let cgY = -( CGFloat(funcY) - bounds.height / 2)

            if isFirstPoint {
                funcPath.move(to: CGPoint(x: cgX, y: cgY))
                isFirstPoint = false
            } else {
                if cgY > max(bounds.size.width, bounds.size.height) * 2 {
                    isFirstPoint = true
                } else {
                    funcPath.addLine(to: CGPoint(x: cgX, y: cgY) )
                }
            }
        }
funcPath.stroke()

есть ли более быстрый способ сделать это?

Ответы [ 3 ]

1 голос
/ 04 апреля 2019

Пара мыслей:

  1. Восстановление пути для каждого пикселя может быть дорогостоящим процессом. Один из способов снизить эти затраты - уменьшить количество отображаемых точек данных. Например, если вы настраиваете scale и перестраиваете весь путь, вы можете использовать коэффициент step при обновлении среднего жеста пути и заменить цикл for на:

    let step = 4
    for i in stride(from: 0, through: Int(bounds.size.width * contentScaleFactor), by: step) {
        ...
    }
    

    Затем, когда жест закончится, вы можете обновить путь еще раз, указав step из 1.

  2. Другой способ сделать это - не обновлять scale и каждый раз вызывать setNeedsDisplay, а просто обновить transform представления. (Это может привести к очень быстрому поведению при увеличении, но, очевидно, проблематично при уменьшении, так как части, исключенные из пути, не будут отображаться.)

  3. Если вы тестируете это, обязательно протестируйте релиз (т.е. оптимизированный) на физическом устройстве. Отладочные сборки со всеми проверками безопасности могут реально замедлить процесс, который выделяется чем-то столь же вычислительно интенсивным, как это.

  4. Кстати, похоже, что строительство пути похоронено в draw(rect:). Я бы отделил построение UIBezierPath от мазка пути, потому что, хотя, да, каждый раз, когда вы обновляете функцию, вы можете захотеть обновить и нарисовать путь, вы определенно не хотите заново строить путь каждый раз draw(rect:) вызывается.

  5. Как только вы это сделаете, draw(rect:) может даже не понадобиться. Вы можете просто добавить CAShapeLayer в качестве подслоя слоя вида, установить strokeColor и strokeWidth этого слоя формы, а затем обновить его path.

1 голос
/ 04 апреля 2019

Обычно перемещение и масштабирование выполняется путем применения преобразований к слоям. Посмотрите на CAShapeLayer, чтобы удержать свой путь. Как пользователь жесты, применять преобразования; затем воссоздать путь, когда жест завершится. При более сложном использовании вы можете пересчитывать фактический путь чаще, чем просто когда пользователь останавливается (например, если он делает паузу, не отпуская), но это уточнения.

0 голосов
/ 04 апреля 2019

Если вы можете описать перевод (панорамирование) и масштабирование как CGAffineTransform, то вы можете применить его к своему UIBezierPath за один раз, используя apply(_:).

...