нарисуйте прямую линию с начальной точкой касания

#swift #uibezierpath #cgrect #touchesbegan #uigraphicscontext

#swift #uibezierpath #cgrect #касания начались #uigraphicscontext

Вопрос:

Я хочу, чтобы мой swift-код рисовал прямую горизонтальную линию. Прямо сейчас он выбирает точку, и пользователь расширяет линию, привязанную к первой точке. Я просто хочу, чтобы пользователь мог рисовать линию влево или вправо. Я пробовал чередовать bezier.AddLine(to: CGPoint(x:startTouch!.x, y:startTouch!.y)) но это, похоже, не имеет никакого эффекта.

 import UIKit

class ViewController2: UIViewController {

    @IBOutlet weak var drawingPlace: UIImageView!

    var startTouch : CGPoint?
    var secondTouch : CGPoint?
    var currentContext : CGContext?
    var prevImage : UIImage?


    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .systemOrange
        drawingPlace.backgroundColor = .gray
    }


    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        let touch = touches.first
        startTouch = touch?.location(in: drawingPlace)
    }

    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {

        for touch in touches{
            secondTouch = touch.location(in: drawingPlace)

            if(self.currentContext == nil){
                UIGraphicsBeginImageContext(drawingPlace.frame.size)
                self.currentContext = UIGraphicsGetCurrentContext()
            }else{
                self.currentContext?.clear(CGRect(x: 0, y: 0, width: drawingPlace.frame.width, height: drawingPlace.frame.height))
            }

            self.prevImage?.draw(in: self.drawingPlace.bounds)

            let bezier = UIBezierPath()

            bezier.move(to: startTouch!)
            bezier.addLine(to: secondTouch!)
            
            bezier.addLine(to: CGPoint(x:startTouch!.x, y:startTouch!.y))
            bezier.close()

            UIColor.blue.set()

            self.currentContext?.setLineWidth(4)
            self.currentContext?.addPath(bezier.cgPath)
            self.currentContext?.strokePath()
            let img2 = self.currentContext?.makeImage()
            drawingPlace.image = UIImage.init(cgImage: img2!)

        }
    }


    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {

        self.currentContext = nil
        self.prevImage = self.drawingPlace.image
    }

}
  

Ответ №1:

Если вы хотите нарисовать горизонтальную линию, создайте CGPoint линию, которая x является местоположением касания, а чья y — начальной точкой. В результате получится горизонтальная линия.


Как уже было сказано, вот несколько других наблюдений:

  1. Если вы вызываете UIGraphicsBeginImageContext , вы должны позвонить UIGraphicsEndImageContext . Вы должны делать это внутри touchesMoved , не пытаясь привязаться к контексту за пределами этого вызова.

  2. Если бы вы это делали, мы обычно использовали UIGraphicsImageRenderer бы в настоящее время.

  3. Лично я бы не стал пытаться отображать изображение для каждого касания. Это довольно дорогостоящая операция. Я бы просто добавил CAShapeLayer , а затем обновил path для этого слоя. Пусть CAShapeLayer позаботится о рендеринге пути.

  4. Я не совсем уверен, почему вы перебираете массив касаний. Я бы просто взял один и использовал это.

  5. Я мог бы предложить использовать прогностические касания, чтобы минимизировать воспринимаемое отставание.

  6. На самом startTouch деле это a CGPoint , а не a UITouch , поэтому я мог бы назвать это startPoint вместо этого.

  7. Если вы хотите сделать снимок изображения, я бы сделал это в touchesEnded , а не в touchesMoved .

Например:

 class ViewController: UIViewController {
    @IBOutlet weak var imageView: UIImageView!
    
    var startPoint: CGPoint?

    let shapeLayer: CAShapeLayer = {
        let shapeLayer = CAShapeLayer()
        shapeLayer.lineWidth = 4
        shapeLayer.fillColor = UIColor.clear.cgColor
        shapeLayer.strokeColor = UIColor.blue.cgColor
        return shapeLayer
    }()

    override func viewDidLoad() {
        super.viewDidLoad()
        imageView.backgroundColor = .systemOrange
        imageView.layer.addSublayer(shapeLayer)
    }

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        let touch = touches.first
        startPoint = touch?.location(in: imageView)
    }

    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
        guard var touch = touches.first else { return }

        if let predicted = event?.predictedTouches(for: touch)?.last {
            touch = predicted
        }

        updatePath(in: imageView, to: touch)
    }

    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
        guard let touch = touches.first else { return }
        updatePath(in: imageView, to: touch)
        let image = UIGraphicsImageRenderer(bounds: imageView.bounds).image { _ in
            imageView.drawHierarchy(in: imageView.bounds, afterScreenUpdates: true)
        }
        shapeLayer.path = nil
        imageView.image = image
    }
}

private extension ViewController {
    func updatePath(in view: UIView, to touch: UITouch) {
        let point = touch.location(in: view)
        guard view.bounds.contains(point) else { return }

        let bezier = UIBezierPath()

        bezier.move(to: startPoint!)
        bezier.addLine(to: CGPoint(x: point.x, y: startPoint!.y))

        shapeLayer.path = bezier.cgPath
    }
}
  

Это просто отображает текущую линию как a CAShapeLayer , но когда обводка выполнена, создается снимок вида изображения (постоянный захват обводки в изображении), удаляется контур слоя фигуры и обновляется вид изображения image .

Но, надеюсь, это ответит на вопрос о том, как сделать линию горизонтальной.

Комментарии:

1. можете ли вы обновить свой код, чтобы я мог рисовать в @IBOutlet слабая переменная drawingPlace: UIImageView! Я могу нарисовать что угодно в вашем коде, и я попытался добавить представление изображения, но это вызывает некоторые ошибки, на которые я не знаю, как реагировать.

2. Просто добавьте слой формы в представление изображения. И, если хотите, захватите изображение touchesEnded . См. Пересмотренный ответ.