提问人:vonbecker 提问时间:11/18/2023 最后编辑:vonbecker 更新时间:11/19/2023 访问量:31
使用子视图的笔势处理程序更新 UIView
Updating a UIView with a subview's gesture handler
问:
我有一个简单的应用程序,其中包含一个包含非常小的视图层次结构:(的子类)和三个(每个子类都是 )。ViewController
DrawingView
UIView
ImageViews
DrawingView
在里面,我已经覆盖了以生成依赖于每个图形中心的图形(具体来说,一个顶点是图像中心的多边形)。同时,位置由用户通过拖动手势控制。手势处理操作是 的方法。DrawingView
draw(_:)
ImageView
ImageView
ViewController
我想随着职位的变化实时更新。为了实现这一点,我在手势处理程序中调用了。但是,这种方法只会离散地更新,并且似乎直到下一个手势开始才更新(无论调用在语句中出现在哪里)。DrawingView
ImageView
DrawingView.setNeedsDisplay()
DrawingView
switch gesture.state
我的问题是:我应该在哪里/如何打电话才能实现平滑(和实时)更新?或者有更好的方法吗?setNeedsDisplay
DrawingView
以下是我的类定义:
class ViewController: UIViewController {
@IBOutlet var drawingView: DrawingView!
@IBOutlet var majorVertex1: UIImageView!
@IBOutlet var majorVertex2: UIImageView!
@IBOutlet var majorVertex3: UIImageView!
var majorVertices: [UIImageView]!
@IBOutlet var majorVertex1XConstraint: NSLayoutConstraint!
@IBOutlet var majorVertex1YConstraint: NSLayoutConstraint!
@IBOutlet var majorVertex2XConstraint: NSLayoutConstraint!
@IBOutlet var majorVertex2YConstraint: NSLayoutConstraint!
@IBOutlet var majorVertex3XConstraint: NSLayoutConstraint!
@IBOutlet var majorVertex3YConstraint: NSLayoutConstraint!
var majorVertexXConstraints: [NSLayoutConstraint]!
var majorVertexYConstraints: [NSLayoutConstraint]!
static var majorVertexXOffsets: [Double]?
static var majorVertexYOffsets: [Double]?
override func viewDidLoad() {
super.viewDidLoad()
majorVertices = [majorVertex1, majorVertex2, majorVertex3]
majorVertexXConstraints = [majorVertex1XConstraint, majorVertex2XConstraint, majorVertex3XConstraint]
majorVertexYConstraints = [majorVertex1YConstraint, majorVertex2YConstraint, majorVertex3YConstraint]
ViewController.majorVertexXOffsets = majorVertexXConstraints.map {(constraint) -> Double in return constraint.constant}
ViewController.majorVertexYOffsets = majorVertexYConstraints.map {(constraint) -> Double in return constraint.constant}
}
@IBAction func handlePan(_ gesture: UIPanGestureRecognizer) {
guard let majorVertices = majorVertices,
let gestureView = gesture.view
else {return}
guard let parentView = gestureView.superview,
let gestureViewIndex = majorVertices.firstIndex(of: gestureView as! UIImageView)
else {return}
let translation = gesture.translation(in: parentView)
switch gesture.state {
case .began:
ViewController.majorVertexXOffsets = majorVertexXConstraints.map {(constraint) -> Double in return constraint.constant}
ViewController.majorVertexYOffsets = majorVertexYConstraints.map {(constraint) -> Double in return constraint.constant}
break
case .changed:
majorVertexXConstraints[gestureViewIndex].constant = ViewController.majorVertexXOffsets![gestureViewIndex] + translation.x
majorVertexYConstraints[gestureViewIndex].constant = ViewController.majorVertexYOffsets![gestureViewIndex] + translation.y
drawingView.setNeedsDisplay()
break
case .ended, .cancelled:
majorVertexXConstraints[gestureViewIndex].constant = gestureView.center.x - parentView.frame.size.width / 2.0
majorVertexYConstraints[gestureViewIndex].constant = gestureView.center.y - parentView.frame.size.height / 2.0
break
default:
break
}
}
}
class DrawingView: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
setupView()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setupView()
}
func setupView() {
backgroundColor = .clear
}
override func draw(_ rect: CGRect) {
super.draw(rect)
drawTriangle(rect)
}
internal func drawTriangle(_ rect: CGRect) {
guard let context = UIGraphicsGetCurrentContext(),
let majorVertexXOffsets = ViewController.majorVertexXOffsets,
let majorVertexYOffsets = ViewController.majorVertexYOffsets
else {return}
let majorVertexXCenters = majorVertexXOffsets.map {(x) -> Double in return x + rect.width / 2.0}
let majorVertexYCenters = majorVertexYOffsets.map {(y) -> Double in return y + rect.height / 2.0}
context.setStrokeColor(UIColor.lightGray.cgColor)
context.setLineWidth(3)
context.move(to: CGPoint(x: majorVertexXCenters[0], y: majorVertexYCenters[0]))
context.addLine(to: CGPoint(x: majorVertexXCenters[1], y: majorVertexYCenters[1]))
context.addLine(to: CGPoint(x: majorVertexXCenters[2], y: majorVertexYCenters[2]))
context.addLine(to: CGPoint(x: majorVertexXCenters[0], y: majorVertexYCenters[0]))
context.strokePath()
}
}
答:
你正在用 vars 做一些相当时髦的事情,这需要直接引用特定的类......而且,目前还不完全清楚您是如何设置相对于视图的约束的,但是......static
您没有看到“实时”绘制更新的原因是,您在不更新 X 和 Y 偏移数组的情况下更改了约束常量。
case .changed:
// modify constraint constants to "move" the view
majorVertexXConstraints[gestureViewIndex].constant = ViewController.majorVertexXOffsets![gestureViewIndex] + translation.x
majorVertexYConstraints[gestureViewIndex].constant = ViewController.majorVertexYOffsets![gestureViewIndex] + translation.y
// update the arrays of X/Y offsets
ViewController.majorVertexXOffsets = majorVertexXConstraints.map {(constraint) -> Double in return constraint.constant}
ViewController.majorVertexYOffsets = majorVertexYConstraints.map {(constraint) -> Double in return constraint.constant}
// NOW call setNeedsDisplay()
drawingView.setNeedsDisplay()
// because we're now updating the constraint constants on .changed
// we need to reset the gesture translation
gesture.setTranslation(.zero, in: parentView)
break
编辑 - 回应评论...
你对 vars 所采用的方法导致了所谓的“紧密耦合”——两个或多个类严重依赖彼此,这可能使更改和/或重用类变得困难。static
这个问题太深入了,无法在这里完全讨论,但举个简单的例子:
在你的班级中,你有这两行:DrawingView
let majorVertexXOffsets = ViewController.majorVertexXOffsets
let majorVertexYOffsets = ViewController.majorVertexYOffsets
假设你想在 ?您必须编辑这两行:DrawingView
SomeOtherController
let majorVertexXOffsets = SomeOtherController.majorVertexXOffsets
let majorVertexYOffsets = SomeOtherController.majorVertexYOffsets
现在不再在原来的.DrawingView
ViewController
相反,您要做的是添加 var 属性并根据需要设置/更新它们。DrawingView
因此,在删除关键字时:ViewController
static
//static var majorVertexXOffsets: [Double]?
//static var majorVertexYOffsets: [Double]?
var majorVertexXOffsets: [Double]?
var majorVertexYOffsets: [Double]?
然后删除代码中出现的所有:ViewController.
//ViewController.majorVertexXOffsets
majorVertexXOffsets
接下来,我们将两个属性添加到:DrawingView
var majorVertexXOffsets: [Double]?
var majorVertexYOffsets: [Double]?
并且,更改以下部分的第一部分:drawTriangle()
//guard let context = UIGraphicsGetCurrentContext(),
// let majorVertexXOffsets = ViewController.majorVertexXOffsets,
// let majorVertexYOffsets = ViewController.majorVertexYOffsets
//else {return}
guard let context = UIGraphicsGetCurrentContext(),
let majorVertexXOffsets = majorVertexXOffsets,
let majorVertexYOffsets = majorVertexYOffsets
else {return}
最后一步是回到:ViewController
case .changed:
// modify constraint constants to "move" the view
majorVertexXConstraints[gestureViewIndex].constant = majorVertexXOffsets![gestureViewIndex] + translation.x
majorVertexYConstraints[gestureViewIndex].constant = majorVertexYOffsets![gestureViewIndex] + translation.y
// update the arrays of X/Y offsets
majorVertexXOffsets = majorVertexXConstraints.map {(constraint) -> Double in return constraint.constant}
majorVertexYOffsets = majorVertexYConstraints.map {(constraint) -> Double in return constraint.constant}
// NEW \/
// update the X/Y offset arrays in drawingView
drawingView.majorVertexXOffsets = majorVertexXOffsets
drawingView.majorVertexYOffsets = majorVertexYOffsets
// NEW /\
// NOW call setNeedsDisplay()
drawingView.setNeedsDisplay()
// reset the gesture translation
gesture.setTranslation(.zero, in: parentView)
break
搜索以获得更深入的讨论。Tight Coupling Anti-Pattern
评论
ViewController
评论