#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
— начальной точкой. В результате получится горизонтальная линия.
Как уже было сказано, вот несколько других наблюдений:
-
Если вы вызываете
UIGraphicsBeginImageContext
, вы должны позвонитьUIGraphicsEndImageContext
. Вы должны делать это внутриtouchesMoved
, не пытаясь привязаться к контексту за пределами этого вызова. -
Если бы вы это делали, мы обычно использовали
UIGraphicsImageRenderer
бы в настоящее время. -
Лично я бы не стал пытаться отображать изображение для каждого касания. Это довольно дорогостоящая операция. Я бы просто добавил
CAShapeLayer
, а затем обновилpath
для этого слоя. ПустьCAShapeLayer
позаботится о рендеринге пути. -
Я не совсем уверен, почему вы перебираете массив касаний. Я бы просто взял один и использовал это.
-
Я мог бы предложить использовать прогностические касания, чтобы минимизировать воспринимаемое отставание.
-
На самом
startTouch
деле это aCGPoint
, а не aUITouch
, поэтому я мог бы назвать этоstartPoint
вместо этого. -
Если вы хотите сделать снимок изображения, я бы сделал это в
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
. См. Пересмотренный ответ.