Перетащите CGRect с помощью UIPanGestureRecognizer

Я создаю подкласс UIView и пытаюсь «перетаскивать» CGRect туда-сюда по экрану. В основном я хочу перемещать (перерисовывать) прямоугольник каждый раз, когда я перетаскиваю палец. Пока у меня есть этот код:

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)
}

Это мой первый раз, когда я использую UIPanGestureRecognizer, поэтому я не уверен во всех деталях, которые входят в это. Я установил точки останова в drawRectangle и подтвердил, что это вызывается. Однако прямоугольник на экране вообще не двигается, сколько бы раз я ни пытался его перетащить. Что случилось?

Не добавляйте UIPanGestureRecognizer в метод draw, добавьте его в метод init. Или жест будет добавлен несколько раз.

William Hu 24.06.2019 04:34
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
1
533
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Ответ принят как подходящий

Попробуйте так (проверьте комментарии через код):

@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)
    }
}

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

Чтобы нарисовать прямоугольники в вашем представлении при панорамировании, вы можете сделать следующее:

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
        }
    }
}

Пример проекта

Привет, спасибо за ответ. Я очень близок к тому, чтобы сделать эту работу (см. Мой собственный ответ), но мне все еще нужна небольшая помощь, чтобы сделать прямоугольник «анимированным».

Xcoder 25.06.2019 04:33

Я думаю, что должно быть относительно просто заставить его работать в подклассе UIView вместо контроллера. Вы можете использовать приведенный выше пример проекта в качестве отправной точки. Если у вас возникнут проблемы, дайте мне знать

Leo Dabus 25.06.2019 04:45

This is how you can do it easily. just copy paste and run this.

//
//  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)")
    }
}

вот демо,

Надеюсь, это поможет. ваше здоровье !

Привет, спасибо за ответ. Однако я хотел бы выполнить пользовательский рисунок в своем подклассе UIView. Как вы думаете, это возможно?

Xcoder 25.06.2019 01:35

@Xcoder проверьте обновленный ответ stackoverflow.com/a/56729268/2303865 и пример проекта, где вы также можете рисовать и перетаскивать dropbox.com/s/48mklm2t1jyo1x3/Draggable%20Rectangle.zip?dl=1

Leo Dabus 25.06.2019 04:01

@LeoDabus Большое спасибо, но что мне делать, если я хочу кодировать в подклассе UIView?

Xcoder 25.06.2019 04:13

Привет, спасибо за ответ. Я очень близок к тому, чтобы сделать эту работу (см. Мой собственный ответ), но мне все еще нужна небольшая помощь, чтобы сделать прямоугольник «анимированным».

Xcoder 25.06.2019 04:33

Я понял большую часть проблемы, почему прямоугольник не двигался. Оказывается, я неправильно понял, как работают геттеры и сеттеры переменных в Swift. Тем не менее, я изменил этот код:

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

    set {}
}

быть переменной lazy:

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

Причина, по которой мне понадобились get и set, заключалась в том, что я использую frame в своих вычислениях переменных, а это было недоступно до тех пор, пока само представление не было полностью создано. Я также немного подправил этот код:

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

и использовал minX вместо midX:

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

Это связано с тем, что CGRect инициализируется параметрами x и y, являющимися minX и minY.

Это хороший прогресс (по крайней мере, прямоугольник движется). Однако я не могу понять, почему прямоугольник меняется местами только после того, как я отпускаю мышь, что приводит к прерывистому движению.

Другие вопросы по теме