Нажмите «Жест» при анимации UIView, который не работает.

У меня есть жест касания на UILabel, перевод которого анимируется. Всякий раз, когда вы нажимаете на метку во время анимации, жест касания не реагирует.

Вот мой код:

    label.addGestureRecognizer(tapGesture)

    label.userInteractionEnabled = true
    label.transform = CGAffineTransformMakeTranslation(0, 0)

    UIView.animateWithDuration(12, delay: 0, options: UIViewAnimationOptions.AllowUserInteraction, animations: { () -> Void in
        label.transform = CGAffineTransformMakeTranslation(0, 900)
        }, completion: nil)

Код жеста:

func setUpRecognizers() {
    tapGesture = UITapGestureRecognizer(target: self, action: "onTap:")
}
func onTap(sender : AnyObject) {
    print("Tapped")
}

Есть идеи? Спасибо :)


Добавлено примечание на 2021 год:

В наши дни это очень просто, вы просто переопределяете hitTest.


person mlevi    schedule 14.03.2016    source источник
comment
Возможно, это поможет вам: /26210318/   -  person owlswipe    schedule 15.03.2016
comment
Покажите свой код инициализации жеста касания.   -  person sschale    schedule 15.03.2016
comment
для тех, кто гуглит здесь, будьте осторожны, ответ с галочкой НЕ РАБОТАЕТ, и в эти дни вы потерпите крах. теперь это очень легко сделать (я дал ответ, показывающий, как). к сожалению, это пример очень устаревшего ответа на SO. поскольку первоначальный автор вопроса исчез, никто не может снять отметку с отмеченного ответа.   -  person Fattie    schedule 15.03.2021
comment
@Fattie, большое спасибо! Это очень помогло мне после нескольких часов разочарования. Интересно, почему это не решает Apple.   -  person Niyog Ray    schedule 27.03.2021
comment
@NiyogRay - да, это одна из тех вещей, которые разочаровывают в Apple. И есть реальная проблема на SO .. ответ с галочкой здесь в настоящее время совершенно, совершенно неправильный .. но проблема в том, что пользователь, задавший вопрос, исчез пять лет назад, и с тех пор его никто не видел. Так что галочка никогда не будет удалена.   -  person Fattie    schedule 27.03.2021
comment
Даже ап человек, который пишет отмеченный ответ, не может снять галочку   -  person Fattie    schedule 27.03.2021


Ответы (6)


Вы не сможете добиться того, что вы делаете, после использования тапа по одной серьезной причине. Жест касания связан с рамкой этикетки. Финальный кадр метки меняется мгновенно при запуске анимации, и вы просто смотрите поддельный фильм (анимацию). Если бы вы могли коснуться (0,900) на экране, он сработал бы как обычно, пока происходит анимация. Однако есть способ сделать это немного по-другому. Лучше всего использовать touchesBegan. Вот расширение, которое я только что написал, чтобы проверить свою теорию, но его можно адаптировать в соответствии с вашими потребностями. Например, вы можете использовать свой фактический подкласс и получить доступ к свойствам метки без необходимости циклов.

extension UIViewController{

public override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
    guard let touch = touches.first else{return}
    let touchLocation = touch.locationInView(self.view)

    for subs in self.view.subviews{
        guard let ourLabel = subs as? UILabel else{return}
        print(ourLabel.layer.presentationLayer())

        if ourLabel.layer.presentationLayer()!.hitTest(touchLocation) != nil{
            print("Touching")
            UIView.animateWithDuration(0.4, animations: {
                self.view.backgroundColor = UIColor.redColor()
                }, completion: {
                    finished in
                    UIView.animateWithDuration(0.4, animations: {
                        self.view.backgroundColor = UIColor.whiteColor()
                        }, completion: {
                            finished in
                    })
                })
            }
        }

    }
}

Вы можете видеть, что он проверяет координаты CALayer.presentationLayer(). Это то, что я называл фильмами. Честно говоря, я до сих пор не полностью понял уровень представления и то, как он работает.

person agibson007    schedule 15.03.2016
comment
НЕ ДЕЛАЙТЕ ЭТОГО! agibson007, вы на правильном пути, но вы просто используете уровень представления! - person Fattie; 14.10.2019

Как обнаружить касания в движущемся представлении

override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {

    let pf = layer.presentation()!.frame
    // note, that is in the space of our superview

    let p = self.convert(point, to: superview!)

    if pf.contains(p) { return self }
    return nil
}

Это так просто

Связанный совет -

Не забывайте, что в большинстве случаев, если анимация запущена, вы, конечно же, почти наверняка захотите ее отменить. Итак, скажем, есть «движущаяся цель», и вы хотите иметь возможность схватить ее пальцем и переместить куда-нибудь еще, естественно, в этом случае ваш код в вашем контроллере представления будет выглядеть примерно так..

   func sliderTouched() {
       if alreadyMoving {
          yourPropertyAnimator?.stopAnimation(true)
          yourPropertyAnimator = nil
       }
       etc ...
   }
person Fattie    schedule 14.10.2019
comment
Можете ли вы предоставить больше информации о том, где находится этот код? Нужно ли переопределять UIView? Можно ли это разместить в расширении? Нужно ли мне вставлять какой-либо код в контроллер представления? - person JCutting8; 04.07.2021
comment
Привет! Первый пример кода, да, 10000%, вы переопределяете представление. Во всех демонстрациях, кроме самых тривиальных, вы переопределяете UIView (или любой другой компонент, основанный на представлении). Наслаждаться! Не стесняйтесь, если нужно спросить больше. Второй пример кода, как он говорит в вашем контроллере представления... - person Fattie; 04.07.2021

Я застрял на этой проблеме в течение нескольких часов и не мог понять, почему нажатие не работает на анимированной этикетке, которая выскальзывает из экрана после 3-секундной задержки.

Хорошо сказано agibson007, что анимация работает как воспроизведение поддельного фильма, 3-секундная задержка контролирует окупаемость фильма, тем не менее, рамка метки меняется, как только начинается анимация, без задержки. Таким образом, постукивание (которое зависит от рамки этикетки в исходном положении) не сработает.

Мое решение заключалось в том, чтобы изменить 3-секундную задержку на функцию тайм-аута, например:

DispatchQueue.main.asyncAfter(deadline: .now() + delay) {
   [weak self] in
   self?.hideLabel()           
}

Таким образом, нажатие будет работать во время задержки, а анимация будет запускаться внутри вызова hideLabel() после задержки.

person Rick Lee    schedule 29.02.2020

Если вы хотите увидеть анимацию, вам нужно поместить ее в обработчик onTap.

let gesture = UITapGestureRecognizer(target: self, action: #selector(onTap(sender:))
gesture.numberOfTapsRequired = 1
label.addGestureRecognizer(gesture)
label.userInteractionEnabled = true

label.transform = CGAffineTransformMakeTranslation(0, 0)
    
UIView.animateWithDuration(12, delay: 3, options: [.allowUserInteraction], animations: { () -> Void in
      label.transform = CGAffineTransformMakeTranslation(0, 900)
      }, completion: nil)


func onTap(sender: AnyObject)
{
    print("Tapped")
}
person Christian Abella    schedule 14.03.2016
comment
Мне нужно, чтобы анимация происходила до нажатия. Мне нужно уметь распознавать касание во время движения представления. - person mlevi; 15.03.2016
comment
если это так, просто измените задержку в исходном коде на 2-3 секунды. Я обновил свой ответ. - person Christian Abella; 15.03.2016
comment
Похоже, что если анимация включает изменение координат объекта, жест не может обнаружить объект. Если ваша анимация меняет только альфа-канал метки, флаг AllowUserInteraction будет работать. - person Christian Abella; 15.03.2016
comment
это совершенно не связано с проблемой - person Fattie; 14.10.2019

Чтобы ваш жест касания работал, вы должны установить количество касаний. Добавьте эту строку:

tapGesture.numberOfTapsRequired = 1

(Я предполагаю, что tapGesture это тот же самый, с которым вы называете label.addGestureRecognizer(tapGesture))

person sschale    schedule 14.03.2016
comment
Что если убрать ':' в onTap:? - person sschale; 15.03.2016
comment
Это приведет к сбою приложения. : сообщает жесту касания, что он может передавать данные sender. - person mlevi; 15.03.2016
comment
Да, я видел что-то в своем коде, что, как мне кажется, на самом деле не реализовано, цепляясь за соломинку. Одна вещь — я не знаю, видел ли я когда-нибудь, чтобы вы звонили setUpRecognizers() — я бы зарегистрировал это, чтобы убедиться, что он настроен (и что, когда вы его добавите, он настроен. - person sschale; 15.03.2016
comment
хм, может быть, супервизор должен разрешить взаимодействие с пользователем? - person sschale; 15.03.2016
comment
Это совершенно не связано - person Fattie; 14.10.2019

Ниже приведен более общий ответ, основанный на ответе @agibson007 в Swift 3.

Это не сразу решило мою проблему, потому что у меня были дополнительные подвиды, закрывающие мое представление. Если у вас возникли проблемы, попробуйте изменить тип расширения и написать операторы печати для touchLocation, чтобы узнать, когда срабатывает функция. Описание в принятом ответе хорошо объясняет проблему.

extension UIViewController {

    open override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {

        guard let touch = touches.first else { return }
        let touchLocation = touch.location(in: self.view)

        for subview in self.view.subviews {

            if subview.tag == VIEW_TAG_HERE && subview.layer.presentation()?.hitTest(touchLocation) != nil {

                print("[UIViewController] View Touched!")
                // Handle Action Here

            }

        }

    }

}
person Matthew Barker    schedule 26.07.2017
comment
Мэтью! вы просто не делаете этого в потоке касаний (в любом случае это было бы намного, намного сложнее). Вы просто переопределяете hitTest !! - person Fattie; 14.10.2019