Search code examples
iosswiftuigesturerecognizer

Glitching When Swift UIView is Dragged


I have a view in an iOS project that is meant to be dragged within the main view. The view. I've attached a pan gesture to that view (paletteView), and moving it around works fine.

But, the "glitches" when moves begin. This doesn't happen on the first move, but on subsequent ones. I think what is happening is related to the constraints on the paletteView. It starts located at 100,100 in the main view, but of course whenever a drag ends, unless I change that, it will snap back to that location. So whenever the pan begins, I grab location of the palette, and update the constraints. This also works, but it seems to produce a flash when the next pangesture begins. Here's the main code:

    @objc func palettePan(_ sender: UIPanGestureRecognizer) {
        let translation = sender.translation(in: view)
        switch sender.state {
        case .began:
            paletteLeading?.constant = paletteView!.frame.origin.x
            paletteTop?.constant = paletteView!.frame.origin.y
            paletteView?.setNeedsUpdateConstraints()
        case .changed:
            paletteView!.transform = CGAffineTransform(translationX: translation.x, y: translation.y)
        case .ended:
            print("done \(paletteView!.frame)")
        default:
            _ = true
        }
    }

Again, this works fine, but for the brief "flash" of the palette, out of position. I don't know where this would be coming from.


Solution

  • You need to reset the transform at the same time as you update the constraints, at the moment you are updating the constraints to to the new position, but the view is still transformed by the final translation of the previous pan until the first changed pan gesture event comes through.

    So your palettePan function would become:

    @objc func palettePan(_ sender: UIPanGestureRecognizer) {
        let translation = sender.translation(in: view)
        switch sender.state {
        case .began:
            paletteLeading?.constant = paletteView!.frame.origin.x
            paletteTop?.constant = paletteView!.frame.origin.y
            paletteView?.setNeedsUpdateConstraints()
            paletteView?.transform = .identity // Reset the transform
        case .changed:
            paletteView!.transform = CGAffineTransform(translationX: translation.x, y: translation.y)
        case .ended:
            print("done \(paletteView!.frame)")
        default:
            _ = true
        }
    }
    

    Though I would actually update the constraints and transform when the pan gesture ends, rather than when a new one begins:

    @objc func palettePan(_ sender: UIPanGestureRecognizer) {
            let translation = sender.translation(in: view)
            switch sender.state {
            case .changed:
                paletteView?.transform = CGAffineTransform(translationX: translation.x, y: translation.y)
            case .ended, .cancelled, .failed:
                paletteLeading?.constant = paletteView!.frame.origin.x
                paletteTop?.constant = paletteView!.frame.origin.y
                paletteView?.setNeedsUpdateConstraints()
                paletteView?.transform = .identity // Reset the transform
            default:
                _ = true
            }
        }