ios 使用子视图的手势处理程序更新UIView

jucafojl  于 2023-11-19  发布在  iOS
关注(0)|答案(1)|浏览(111)

我有一个简单的应用程序,它有一个ViewController,包含一个非常小的视图层次结构:DrawingViewUIView的子类)和三个ImageViews(它们都是DrawingView的子级)。
DrawingView中,我重写了draw(_:),以生成依赖于每个ImageView中心的图形(具体来说,一个顶点为图像中心的多边形)。同时,ImageView的位置由用户通过拖动手势控制。手势处理动作是ViewController的方法。
我希望DrawingView能够随着ImageView位置的变化而实时更新。为了实现这一点,我在手势处理程序中调用了DrawingView.setNeedsDisplay()。然而,这种方法只会离散地更新DrawingView,而且似乎直到下一个手势开始时才更新(无论调用出现在switch gesture.state语句中的何处)。
x1c 0d1x的数据

我的问题:我应该在哪里/如何调用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()
        
    }
    
}

字符串

c7rzv4ha

c7rzv4ha1#

你正在用static vars做一些相当时髦的事情,这需要直接引用特定的类.
您看不到“实时”绘制更新的原因是因为您更改了约束常量,而没有更新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

字符串

编辑- * 回应评论... *

您对static vars所采用的方法导致了所谓的“紧耦合”--两个或多个类严重依赖于彼此,这使得更改和/或重用这些类变得困难。
这个问题有太多的深度,无法在这里充分讨论,但一个快速的例子:
DrawingView类中,有以下两行:

let majorVertexXOffsets = ViewController.majorVertexXOffsets
let majorVertexYOffsets = ViewController.majorVertexYOffsets


假设你想在SomeOtherController中使用DrawingView?你必须编辑这两行:

let majorVertexXOffsets = SomeOtherController.majorVertexXOffsets
let majorVertexYOffsets = SomeOtherController.majorVertexYOffsets


现在DrawingView不再在原来的ViewController中工作。
相反,您需要做的是向DrawingView添加var属性,并根据需要设置/更新它们。
因此,在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以获得更深入的讨论。

相关问题