image.png

当前View里面有两个View,绿色的bigView和红色的smallView,smallView在bigView里面。现在让bigView执行一段移动动画,然后给bigView添加点击事件,发现点击事件无效。

因为iOS动画中的View点击事件无效

原因是iOS里几乎所有的View动画是都基于layer实现的,frame在动画开始会直接变成终点状态。动画过程中bigView的frame不会变化,也不能接收点击事件。添加点击事件可以在bigView的父view也就是当前view中重写touchesBegan() 方法,判断点击位置在哪个view里面。

添加完touchesBegan(),会发现点击bigView动画这个方法并不会触发,原因是动画中的layer会屏蔽触摸事件。这时有两种方法(效果相同):

  • 给UIView.animate() 方法添加options: .allowUserInteraction参数
  • 设置bigView的isUserInteractionEnabled = false

这样点击事件就会传递给当前view

  1. class OurView: UIView {
  2. lazy var bigView = UIView(frame: CGRect(x: 100, y: 200, width: 200, height: 200))
  3. lazy var smallView = UIView(frame: CGRect(x: 100, y: 100, width: 100, height: 100))
  4. override init(frame: CGRect) {
  5. super.init(frame: frame)
  6. backgroundColor = .white
  7. bigView.backgroundColor = .green
  8. addSubview(bigView)
  9. smallView.backgroundColor = .red
  10. bigView.addSubview(smallView)
  11. let tap = UITapGestureRecognizer(target: self, action: #selector(doClick))
  12. bigView.isUserInteractionEnabled = true
  13. bigView.addGestureRecognizer(tap)
  14. UIView.animate(withDuration: 100, delay: 0, options: .allowUserInteraction) {
  15. self.bigView.transform = CGAffineTransform(translationX: 0, y: 300)
  16. } completion: { _ in
  17. }
  18. }
  19. @objc func doClick() {
  20. print("clicked")
  21. }
  22. required init?(coder: NSCoder) {
  23. fatalError("init(coder:) has not been implemented")
  24. }
  25. }

接下来判断点击位置在哪个View里。
touch.location(in: self)获取触摸点在当前view中的位置;用bigView.layer.presentation()?.frame可以获取动画中的layer的frame。

  • 如果要判断是否点击了bigView,直接用movingFrame.contains(point)即可。
  • 如果要判断是否点击了smallView,也就是动画中的view的子view,那么需要进行坐标转换才能得到smallView的实时frame:用offsetBy方法转换smallView.frame,最后用smallFrame.contains(point)判断:
  1. override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
  2. for touch in touches {
  3. var point = touch.location(in: self)
  4. if let movingFrame = bigView.layer.presentation()?.frame {
  5. let f = smallView.frame
  6. var smallFrame = f.offsetBy(dx: movingFrame.minX, dy: movingFrame.minY)
  7. let contains = smallFrame.contains(point)
  8. print("smallView touches: \(contains)")
  9. }
  10. break;
  11. }
版权声明:本文为rome753原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/rome753/p/16491397.html