【问题标题】:Why does my UITapGestureRecognizer does not get called when animating the UIView?为什么我的 UITapGestureRecognizer 在设置 UIView 动画时没有被调用?
【发布时间】:2021-08-20 06:14:28
【问题描述】:

我制作了一个带有UIImageView 的 toast 视图,我想做的是每次用户点击图像视图时,toast 都会自行消失。自然地,我为我的图像视图配置了一个UITapGestureRecognizer,但没有调用选择器函数。这是我所做的:

class ToastView: UIView {

    private var icon: UIImageView!

    init() {
        super.init(frame: .zero)
        setupViews()
    }

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

    override func layoutSubviews() {
        super.layoutSubviews()
        setupConstraints()
    }

    private func setupViews() {
        translatesAutoresizingMaskIntoConstraints = false
        layer.cornerRadius = 8
        isUserInteractionEnabled = true
  
        icon = UIImageView()
        icon.translatesAutoresizingMaskIntoConstraints = false
        icon.image = UIImage(named: "someImage")
        icon.isUserInteractionEnabled = true
        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(iconHandler))
        icon.addGestureRecognizer(tapGesture)
        addSubview(icon)
    }

    private func setupConstraints() {
        NSLayoutConstraint.activate([
            topAnchor.constraint(equalTo: superview!.topAnchor, constant: 55),
            leadingAnchor.constraint(equalTo: superview!.leadingAnchor, constant: 16),
            trailingAnchor.constraint(equalTo: superview!.trailingAnchor, constant: -16),
            icon.heightAnchor.constraint(equalToConstant: 16),
            icon.widthAnchor.constraint(equalToConstant: 16),
            icon.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -16),
            icon.centerYAnchor.constraint(equalTo: centerYAnchor),
        ])
    }

    @objc private func iconHandler(_ sender: UITapGestureRecognizer) {
        // This function is not called
        print("handle icon")
    }
}

经过一番研究,我尝试给ToastView 一个手势识别器,而不是图像视图。因此,在我的自定义 UIViewController 类中显示吐司时,我确实提供了点击手势识别器,如下所示:

class CustomViewController: UIViewController {
    private var isShowingToast: Bool = false
    private lazy var toast: ToastView = {
        let toast = ToastView()
        toast.isUserInteractionEnabled = true
        toast.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(dismissToast)))
        return toast
    }()

    func showToastWithMessage() {
        if !isShowingToast {
            view.addSubview(toast)
            UIView.animate(withDuration: 0.5, delay: 0.0, usingSpringWithDamping: 1.0, initialSpringVelocity: 1.0, options: .curveEaseInOut, animations: { [weak self] in
                self?.toast.alpha = 1
                self?.toast.frame.origin.y += 10
                self?.isShowingToast = true
            }, completion: { _ in
                UIView.animate(withDuration: 0.5, delay: 5.0, usingSpringWithDamping: 1.0, initialSpringVelocity: 1.0, options: .curveEaseInOut, animations: { [weak self] in
                    self?.toast.alpha = 0
                    self?.toast.frame.origin.y -= 10
                }, completion: { [weak self] _ in
                    self?.isShowingToast = false
                    self?.toast.removeFromSuperview()
                })
            })
        }
    }
    
    @objc private func dismissToast() {
        // This functions does not get called as well
        print("dismiss")
    }
}

不幸的是,dismiss 函数不会打印到控制台。有没有办法解决这个问题?

【问题讨论】:

  • 如果从dismissToast 中删除关键字private 会怎样?你能检查一下这是否解决了它吗?谢谢。
  • 这是什么? icon = UIImageView() 没有边框?
  • @matt 我刚刚尝试过,不幸的是该函数仍然没有被调用
  • 看看我的这篇文章,它涵盖了常见的原因:biteinteractive.com/…
  • @llehcram 在动画时,它不会进行任何用户交互。这将是问题所在。在开始第二个动画之前放一些延迟。

标签: ios swift


【解决方案1】:

看起来这是因为您的动画而发生的。视图始终处于动画状态并阻止点击手势。您可以尝试延迟调用它,而不是为您的动画添加延迟。

func showToastWithMessage() {
    if !isShowingToast {
        view.addSubview(toast)
        UIView.animate(withDuration: 0.5, delay: 0.0, usingSpringWithDamping: 1.0, initialSpringVelocity: 1.0, options: .curveEaseInOut, animations: { [weak self] in
            self?.toast.alpha = 1
            self?.toast.frame.origin.y += 10
            self?.isShowingToast = true
        }, completion: { _ in
            print("Completion")
        })

        DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
            UIView.animate(withDuration: 0.5, delay: 0.0, usingSpringWithDamping: 1.0, initialSpringVelocity: 1.0, options: .curveEaseInOut, animations: { [weak self] in
                self?.toast.alpha = 0
                self?.toast.frame.origin.y -= 10
            }, completion: { [weak self] _ in
                self?.isShowingToast = false
                self?.toast.removeFromSuperview()
            })
        }

    }
}

这种方式视图将在 5 秒后进入动画状态,而不是延迟 5 秒。

【讨论】:

    【解决方案2】:

    这是你的控制器的样子:

    class CustomViewController: UIViewController {
    
    private var isShowingToast: Bool = false
     lazy var toast: ToastView = {
        let toast = ToastView()
        toast.isUserInteractionEnabled = true
        toast.backgroundColor = .red
        toast.translatesAutoresizingMaskIntoConstraints = false 
        toast.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(dismissToast)))
        return toast
    }()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Add Toast constraints
        view.addSubview(toast)
        toast.heightAnchor.constraint(equalToConstant: 200).isActive = true
        toast.widthAnchor.constraint(equalTo: toast.heightAnchor).isActive = true
        toast.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
        toast.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
    }
    
    func showToastWithMessage() {
        if !isShowingToast {
            view.addSubview(toast)
            UIView.animate(withDuration: 0.5, delay: 0.0, usingSpringWithDamping: 1.0, initialSpringVelocity: 1.0, options: .curveEaseInOut, animations: { [weak self] in
                self?.toast.alpha = 1
                self?.toast.frame.origin.y += 10
                self?.isShowingToast = true
            }, completion: { _ in
                UIView.animate(withDuration: 0.5, delay: 5.0, usingSpringWithDamping: 1.0, initialSpringVelocity: 1.0, options: .curveEaseInOut, animations: { [weak self] in
                    self?.toast.alpha = 0
                    self?.toast.frame.origin.y -= 10
                }, completion: { [weak self] _ in
                    self?.isShowingToast = false
                    self?.toast.removeFromSuperview()
                })
            })
        }
    }
    
    @objc private func dismissToast() {
        // This functions does not get called as well
        print("dismiss")
     }
    }
    

    这是你的课:

    class ToastView: UIView {
    
    private var icon = UIImageView()
    
    init() {
        super.init(frame: .zero)
        setupViews()
    }
    
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        setupViews()
    }
    
    override func layoutSubviews() {
        super.layoutSubviews()
        setupConstraints()
    }
    
    private func setupViews() {
        translatesAutoresizingMaskIntoConstraints = false
        layer.cornerRadius = 8
        isUserInteractionEnabled = true
    
        icon.translatesAutoresizingMaskIntoConstraints = false
        icon.image = UIImage(named: "profilo")?.withRenderingMode(.alwaysOriginal) //put your image here
        icon.isUserInteractionEnabled = true
        icon.layer.cornerRadius = 8
        icon.layer.masksToBounds = true //set image masked round corner
        icon.clipsToBounds = true
        icon.contentMode = .scaleAspectFill //set image content mode
        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(iconHandler))
        icon.addGestureRecognizer(tapGesture)
    }
    
    private func setupConstraints() {
        // Setup the constraints for the subviews
        addSubview(icon)
        icon.topAnchor.constraint(equalTo: topAnchor).isActive = true
        icon.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true
        icon.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true
        icon.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
    }
    
    @objc private func iconHandler(_ sender: UITapGestureRecognizer) {
        // This function is not called
        print("handle icon")
     }
    }
    

    这是结果:

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-07-07
      • 1970-01-01
      • 1970-01-01
      • 2013-05-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多