1
votes

Faites glisser un CGRect à l'aide de UIPanGestureRecognizer

Je sous-classe UIView et j'essaye de "faire glisser" un CGRect d'avant en arrière sur l'écran. En gros, je veux déplacer (redessiner) le rectangle chaque fois que je fais glisser mon doigt. Jusqu'à présent, j'ai ce code:

var rectangle: CGRect {
    get {
        return CGRect(x: 200,
                    y: 200,
                    width: frame.width / 6,
                    height: 15)
    }

    set {}
}

override func draw(_ rect: CGRect) {
    let gesture = UIPanGestureRecognizer(target: self, action: #selector(dragRectangle(recognizer:)))
    addGestureRecognizer(gesture)
    drawRectangle(rect)
}

func drawRectangle(_ rect: CGRect) {
    let path = UIBezierPath(rect: rectangle)
    UIColor.black.set()
    path.fill()
}

@objc func dragRectangle(recognizer: UIPanGestureRecognizer) {
    let translation = recognizer.translation(in: self)
    rectangle = CGRect(x: rectangle.midX + translation.x, y: rectangle.midY + translation.y, width: rectangle.width, height: rectangle.height)
    setNeedsDisplay()
    recognizer.setTranslation(CGPoint.zero, in: self)
}

C'est la première fois que j'utilise UIPanGestureRecognizer , donc je ne suis pas sûr de tous les détails qui entrent dans ce domaine. J'ai défini des points d'arrêt dans drawRectangle et confirmé que cela est appelé. Cependant, le rectangle à l'écran ne bouge pas du tout, peu importe le nombre de fois que j'essaye de le faire glisser. Quel est le problème?


1 commentaires

N'ajoutez pas UIPanGestureRecognizer dans la méthode draw , ajoutez-le à la méthode init. Ou le geste sera ajouté plusieurs fois.


3 Réponses :


1
votes

Essayez comme ceci (vérifiez les commentaires via le code):

import UIKit

class ViewController: UIViewController {
    var rectangles: [Rectangle] = []
    override func viewDidLoad() {
        super.viewDidLoad()
        view.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(pan)))
    }
    @objc func pan(_ gesture: UIPanGestureRecognizer) {
        switch gesture.state {
        case .began:
            let rectangle = Rectangle(frame: .init(origin: gesture.location(in: view), size: .init(width: 0, height: 0)))
            rectangle.fillColor = .red
            rectangle.strokeColor = .white
            rectangle.lineWidth = 3
            view.addSubview(rectangle)
            rectangles.append(rectangle)
        case .changed:
            let distance = gesture.translation(in: view)
            let index = rectangles.index(before: rectangles.endIndex)
            let frame = rectangles[index].frame
            rectangles[index].frame = .init(origin: frame.origin, size: .init(width: frame.width + distance.x, height: frame.height + distance.y))
            rectangles[index].setNeedsDisplay()
            gesture.setTranslation(.zero, in: view)
        case .ended:
            break
        default:
            break
        }
    }
}

extension CGPoint {
    static func +=(lhs: inout CGPoint, rhs: CGPoint) {
        lhs.x += rhs.x
        lhs.y += rhs.y
    }
}

Pour dessiner des rectangles sur votre vue lors du panoramique, vous pouvez faire comme suivez:

@IBDesignable
class Rectangle: UIView {

    @IBInspectable var color: UIColor = .clear {
        didSet { backgroundColor = color }
    }
    // draw your view using the background color
    override func draw(_ rect: CGRect) {
        backgroundColor?.set()
        UIBezierPath(rect: rect).fill()
    }
    // add the gesture recognizer to your view
    override func didMoveToSuperview() {
        addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(pan)))
    }
    // your gesture selector
    @objc func pan(_ gesture: UIPanGestureRecognizer) {
        //  update your view frame origin
        frame.origin += gesture.translation(in: self)  
        // reset the gesture translation
        gesture.setTranslation(.zero, in: self)
    }
}

Exemple de projet


2 commentaires

Salut, merci pour la réponse. Je suis très proche de faire ce travail (voir ma propre réponse), mais j'ai encore besoin d'un peu d'aide pour que le rectangle "anime".


Je pense qu'il devrait être relativement simple de le faire fonctionner dans une sous-classe UIView au lieu d'un contrôleur. Vous pouvez utiliser l'exemple de projet ci-dessus comme point de départ. Si vous rencontrez des problèmes, faites-le moi savoir



2
votes

Voici comment procéder facilement. copiez-collez et exécutez ceci.

//
//  RootController.swift
//  SampleApp
//
//  Created by Chanaka Caldera on 24/6/19.
//  Copyright © 2019 homeapps. All rights reserved.
//

import UIKit

class RootController: UIViewController {

    private var draggableView: UIView!
    private var pangesture: UIPanGestureRecognizer!

    override func viewDidLoad() {
        super.viewDidLoad()

        setdragview()

    }
}

extension RootController {
    fileprivate func setdragview() {
        draggableView = UIView()
        draggableView.translatesAutoresizingMaskIntoConstraints = false
        draggableView.backgroundColor = .lightGray
        view.addSubview(draggableView)

        let draggableviewConstraints = [draggableView.leftAnchor.constraint(equalTo: view.leftAnchor,constant: 10),
                                        draggableView.topAnchor.constraint(equalTo: view.topAnchor, constant: 64),
                                        draggableView.widthAnchor.constraint(equalToConstant: 100),
                                        draggableView.heightAnchor.constraint(equalToConstant: 40)]

        NSLayoutConstraint.activate(draggableviewConstraints)

        pangesture = UIPanGestureRecognizer()
        draggableView.isUserInteractionEnabled = true
        draggableView.addGestureRecognizer(pangesture)
        pangesture.addTarget(self, action: #selector(draggableFunction(_:)))

    }
}

extension RootController {
    @objc fileprivate func draggableFunction(_ sender: UIPanGestureRecognizer) {
        view.bringSubviewToFront(draggableView)
        let translation = sender.translation(in: self.view)
        draggableView.center = CGPoint(x: draggableView.center.x + translation.x, y: draggableView.center.y + translation.y)
        sender.setTranslation(CGPoint.zero, in: self.view)
        print("drag works : \(translation.x)")
    }
}

voici la démo,

entrez la description de l'image ici

J'espère que cela vous aidera. bravo!


4 commentaires

Salut, merci pour la réponse. Cependant, je voudrais effectuer un dessin personnalisé dans ma sous-classe de UIView . Pensez-vous que c'est possible?


@Xcoder vérifiez la réponse mise à jour stackoverflow.com/a/56729268/2303865 et un exemple de projet où vous pouvez également dessiner et faire glisser dropbox.com/s/48mklm2t1jyo1x3/Draggable%20Rectangle.zip? dl = 1


@LeoDabus Merci beaucoup, mais que ferais-je si je veux coder dans une sous-classe de UIView ?


Salut, merci pour la réponse. Je suis très proche de faire ce travail (voir ma propre réponse), mais j'ai encore besoin d'un peu d'aide pour que le rectangle "anime".



0
votes

J'ai compris la plupart du problème de savoir pourquoi le rectangle ne bougeait pas. Il s'avère que j'ai mal compris comment fonctionnent les getters et les setters variables dans Swift. Néanmoins, j'ai changé ce code:

rectangle = CGRect(x: rectangle.minX + translation.x, y: rectangle.minY + translation.y, width: rectangle.width, height: rectangle.height)

pour être une variable paresseuse :

rectangle = CGRect(x: rectangle.midX + translation.x, y: rectangle.midY + translation.y, width: rectangle.width, height: rectangle.height)

La raison pour laquelle j'avais besoin get et set en premier lieu était dû au fait que j'utilisais frame dans mes calculs de variables, et cela n'était pas disponible tant que la vue elle-même n'était pas complètement instanciée . J'ai également modifié un peu ce code:

lazy var rectangle: CGRect = {
    return CGRect(x: 200,
                         y: 200,
                         width: self.frame.width / 6,
                         height: 15)
}()

et utilisé minX au lieu de midX :

var rectangle: CGRect {
    get {
        return CGRect(x: 200,
                    y: 200,
                    width: frame.width / 6,
                    height: 15)
    }

    set {}
}

Ceci est dû au fait que CGRect est initialisé avec les paramètres x et y étant les minX et minY . p>

C'est une bonne progression (au moins le rectangle se déplace). Cependant, je ne suis pas en mesure de comprendre pourquoi le rectangle ne change de place qu'après avoir relâché ma souris, ce qui entraîne un mouvement saccadé.


0 commentaires