UIPanGestureRecognizer не вызывает действие - PullRequest
0 голосов
/ 06 сентября 2018

Я пытаюсь создать подкласс UIView, чтобы позволить расширить представление с панорамой по нему. Это должно работать следующим образом: если пользователь делает панорамирование к вершине, высота представления уменьшается, а если панорамирование направлено к дну, оно должно увеличиваться. Для достижения этой функциональности я пытаюсь добавить UIPanGestureRecognizer в представление, но, похоже, оно не работает. Я сделал это так:

Первый фрагмент - это объявление подкласса uiView

class ExpandibleView: UIView {
    //Here I create the reference to the recognizer
    let panGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(handlePan(_:)))

    //And here I set its minimumNumberOfTouches and maximumNumberOfTouches properties and add it to the view
    func initialize() {
        panGestureRecognizer.minimumNumberOfTouches = 1
        panGestureRecognizer.maximumNumberOfTouches = 1
        self.addGestureRecognizer(panGestureRecognizer)
    }

    //here's the function that should handle the pan but who instead doesn't seem to been called at all
    @objc func handlePan(_ sender:UIPanGestureRecognizer) {
        //Here's I handle the Pan
    }
}

Второй вариант - реализация внутри View Controller.

class MapViewController: UIViewController {
    @IBOutlet weak var profileView: ExpandibleView!

    //MARK: - ViewController Delegate Methods

    override func viewDidLoad() {
        super.viewDidLoad()

        //Here I set the View
        profileView.isUserInteractionEnabled = true
        profileView.initialize()
        profileView.minHeight = 100
        profileView.maxHeight = 190
    }

}

Я установил внутри раскадровки класс представления как созданный мной подкласс, но распознаватель не запускает обработчик вообще.

Ответы [ 2 ]

0 голосов
/ 06 сентября 2018

Проблема, с которой вы столкнулись, связана с определением переменной panGestureRecognizer в определении класса здесь:

//Here I create the reference to the recognizer
let panGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(handlePan(_:)))

Вы можете инициализировать его таким образом, но кажется, что self не настроен при создании этой переменной. Таким образом, ваше действие никогда не регистрируется.

Есть несколько способов исправить это с помощью вашего кода, вы можете продолжить инициализировать его как переменную экземпляра, но вам нужно будет настроить цель / действие в вашей функции initialize()

let panGestureRecognizer = UIPanGestureRecognizer()

func initialize() {
    // add your target/action here
    panGestureRecognizer.addTarget(self, action: #selector(handlePan(_:)))
    panGestureRecognizer.minimumNumberOfTouches = 1
    panGestureRecognizer.maximumNumberOfTouches = 1
    addGestureRecognizer(panGestureRecognizer)
}

Или вы можете просто инициализировать ваш распознаватель жестов в вашей функции инициализации и не использовать переменную экземпляра

func initialize() {
    let panGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(handlePan(_:)))
    panGestureRecognizer.minimumNumberOfTouches = 1
    panGestureRecognizer.maximumNumberOfTouches = 1
    addGestureRecognizer(panGestureRecognizer)
}

Мой оригинальный ответ

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

Пример с ограничениями

import UIKit

// Delegate protocol for managing constraint updates if needed
protocol MorphDelegate: class {

    // tells the delegate to change its size constraints
    func morph(x: CGFloat, y: CGFloat)
}

class ExpandableView: UIView {

    var delegate: MorphDelegate?

    init() {
        // frame is set later if needed by creator when using this init method
        super.init(frame: CGRect.zero)
        configureGestureRecognizers()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        configureGestureRecognizers()
    }

    override init(frame: CGRect) {
        super.init(frame: frame)
        configureGestureRecognizers()
    }

    // setup UIPanGestureRecognizer
    internal func configureGestureRecognizers() {
        let panGR = UIPanGestureRecognizer.init(target: self, action: #selector(didPan(_:)))
        addGestureRecognizer(panGR)
    }

    @objc func didPan(_ panGR: UIPanGestureRecognizer) {

        // get the translation
        let translation = panGR.translation(in: self).applying(transform)

        if let delegate = delegate {

            // tell delegate to change the constraints using this translation
            delegate.morph(x: translation.x, y: translation.y)

        } else {

            // If you want the view to expand/contract in opposite direction
            // of drag then swap the + and - in the 2 lines below
            let newOriginY = frame.origin.y + translation.y
            let newHeight = frame.size.height - translation.y

            // expand self via frame manipulation
            let newFrame = CGRect(x: frame.origin.x, y: newOriginY, width: frame.size.width, height: newHeight)
            frame = newFrame
        }

        // reset translation
        panGR.setTranslation(CGPoint.zero, in: self)
    }

}

Если вы хотите использовать этот класс, определив его в раскадровке и манипулируя его ограничениями, вы должны сделать это следующим образом.

Сначала определите ваш вид в раскадровке и ограничьте его ширину и высоту. Для демонстрации, которую я ограничил, это позиция x к центру вида, а снизу к SafeArea.bottom

Создайте IBOutlet для представления, а также ограничение по высоте для вашего файла ViewController.

Для этого примера я установил синий цвет фона.

storyboard setup

В контроллере представления я определил функцию для настройки представления (в настоящее время только устанавливает делегат для обратных вызовов манипулирования ограничениями), а затем определил расширение для обработки вызовов делегатов для обновления ограничений.

class ViewController: UIViewController {

    @IBOutlet weak var expandingView: ExpandableView!
    @IBOutlet weak var constraint_expViewHeight: NSLayoutConstraint!


    override func viewDidLoad() {
        super.viewDidLoad()

        // call to configure our expanding view
        configureExpandingView()
    }

    // this sets the delegate for an expanding view defined in the storyboard
    func configureExpandingView() {
        expandingView.delegate = self
    }
}

// setup the delegate callback to handle constraint manipulation
extension ViewController: MorphDelegate {

    func morph(x: CGFloat, y: CGFloat) {

        // this will update the view's height based on the amount
        // you drag your finger in the view. You can '+=' below if
        // you want to reverse the expanding behavior based on pan
        // movements.
        constraint_expViewHeight.constant -= y
    }
}

Выполнение этого через ограничения дает мне это при запуске проекта:

animation with constraints

Пример с манипулированием кадрами

Чтобы использовать это и манипулировать высотой, используя только свойство frame, контроллер вида может реализовать создание вида примерно так:

override func viewDidLoad() {
    super.viewDidLoad()

    setupExpandingView()
}

func setupExpandingView() {

    let newView = ExpandableView()
    newView.backgroundColor = .red
    newView.frame = CGRect(x: 20, y: 200, width: 100, height: 100)
    view.addSubview(newView)
}

Используя только манипуляции с кадрами, я получаю это:

Demo Gif

0 голосов
/ 06 сентября 2018

Попробуйте изменить это

class ExpandibleView: UIView {

    func initialize() {


    }

    //here's the function that should handle the pan but who instead doesn't seem to been called at all
     func handlePan() {
        //your function
    }
}

class MapViewController: UIViewController {
    @IBOutlet weak var profileView: ExpandibleView!

    //MARK: - ViewController Delegate Methods

    override func viewDidLoad() {
        super.viewDidLoad()
     let panGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(handlePan(_:)))
     panGestureRecognizer.minimumNumberOfTouches = 1
        panGestureRecognizer.maximumNumberOfTouches = 1
     profileView.addGestureRecognizer(panGestureRecognizer)
        //Here I set the View
        profileView.isUserInteractionEnabled = true
        profileView.initialize()
        profileView.minHeight = 100
        profileView.maxHeight = 190
    }

   @objc func handlePan(_ sender:UIPanGestureRecognizer) {
       profileView.handlePan()
    }
}

Или вы можете создать делегата для вызова в другом представлении.

удачи!

...