我需要在我的应用程序中实现一项功能,该功能允许用户只需触摸和拖动即可在imageView
中选择一个区域。
我尝试使用touchesBegan
,但是由于我是Swift新手,所以遇到了一些困难。
我该怎么做?
我到了,但是现在呢?
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
touch = touches.first
lastPoint = touch.location(in: imageView)
for touch in touches {
print(touch.location(in: imageView))
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
touch = touches.first
currentPoint = touch.location(in: imageView)
self.imageView.setNeedsDisplay()
lastPoint = currentPoint
}
答案 0 :(得分:2)
如果要在选择完图像的一部分后想做些事情,例如,实施touchesEnded
。
例如,假设您要在拖动时显示预期区域的矩形,并希望在完成拖动后为所选部分制作图像快照。然后,您可以执行以下操作:
@IBOutlet weak var imageView: UIImageView!
var startPoint: CGPoint?
let rectShapeLayer: CAShapeLayer = {
let shapeLayer = CAShapeLayer()
shapeLayer.strokeColor = UIColor.black.cgColor
shapeLayer.fillColor = UIColor.clear.cgColor
shapeLayer.lineWidth = 3
return shapeLayer
}()
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
startPoint = nil
guard let touch = touches.first else { return }
startPoint = touch.location(in: imageView)
// you might want to initialize whatever you need to begin showing selected rectangle below, e.g.
rectShapeLayer.path = nil
imageView.layer.addSublayer(rectShapeLayer)
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let touch = touches.first, let startPoint = startPoint else { return }
let currentPoint: CGPoint
if let predicted = event?.predictedTouches(for: touch), let lastPoint = predicted.last {
currentPoint = lastPoint.location(in: imageView)
} else {
currentPoint = touch.location(in: imageView)
}
let frame = rect(from: startPoint, to: currentPoint)
// you might do something with `frame`, e.g. show bounding box
rectShapeLayer.path = UIBezierPath(rect: frame).cgPath
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let touch = touches.first, let startPoint = startPoint else { return }
let currentPoint = touch.location(in: imageView)
let frame = rect(from: startPoint, to: currentPoint)
// you might do something with `frame`, e.g. remove bounding box but take snapshot of selected `CGRect`
rectShapeLayer.removeFromSuperlayer()
let image = imageView.snapshot(rect: frame, afterScreenUpdates: true)
// do something with this `image`
}
private func rect(from: CGPoint, to: CGPoint) -> CGRect {
return CGRect(x: min(from.x, to.x),
y: min(from.y, to.y),
width: abs(to.x - from.x),
height: abs(to.y - from.y))
}
在其中具有用于创建快照图像的UIView
扩展名的地方:
extension UIView {
/// Create image snapshot of view.
///
/// - Parameters:
/// - rect: The coordinates (in the view's own coordinate space) to be captured. If omitted, the entire `bounds` will be captured.
/// - afterScreenUpdates: A Boolean value that indicates whether the snapshot should be rendered after recent changes have been incorporated. Specify the value false if you want to render a snapshot in the view hierarchy’s current state, which might not include recent changes.
/// - Returns: The `UIImage` snapshot.
func snapshot(rect: CGRect? = nil, afterScreenUpdates: Bool = true) -> UIImage {
return UIGraphicsImageRenderer(bounds: rect ?? bounds).image { _ in
drawHierarchy(in: bounds, afterScreenUpdates: afterScreenUpdates)
}
}
}
现在,如果要执行其他操作而不是捕获图像快照,则可以执行任何您想执行的操作。但这说明了基本思想。
在我上面的示例中有几处小事:
请注意,我将ivars限制在我绝对需要的那些东西上。例如。当前的touch
可能应该是局部变量,而不是ivar。我们应始终将变量限制在尽可能窄的范围内,以免造成意外后果等。
我为您的touchesMoved
添加了一些细微的改进,以使用预测性触摸。这不是必需的,但可以帮助最大程度地减少手指拖曳时的感觉到的拖延。
我完全不确定您为什么致电setNeedsDisplay
。除非您有其他打算,否则似乎没有必要。
我不确定您用于图像视图的内容模式。例如,如果您正在使用“纵横比拟合”并想要对其进行快照,则可以选择其他快照算法,例如https://stackoverflow.com/a/54191120/1271826中概述的算法。