简体   繁体   中英

Why does my UITapGestureRecognizer does not get called when animating the UIView?

I made a toast view with a UIImageView in it, what I want to do is everytime the user taps the image view, the toast dismisses itself. Naturally I configured a UITapGestureRecognizer to my image view, but the selector function is not getting called. Here's what I did:

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

After some research, I tried to give the ToastView a gesture recognizer instead of the image view. So I did give the tap gesture recognizer when showing the toast in my custom UIViewController class like this:

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

Unfortunately the dismiss function does not print to the console. Is there anyway to resolve this?

Looks like this occurs because of your animation. View is all the time in animation status and block tap gesture. U can try call it with delay instead of adding delay for your animation.

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

    }
}

This way view going to animate status after 5 sec not with 5 sec delay.

This is how your controller look like:

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

And this is your class:

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

this is the result:

在此处输入图像描述

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM