Search code examples
iosswiftuiviewcalayerhittest

How does one get the front-most layer instead of the deepest layer using HitTest


Well, I just wasted 2 days on that.
Simplest example : I have 2 UIView's who's being animated from the top of the screen to the bottom. After a lot of researching i found out that the only way you can "touch/trigger" touch event when UIView is being animated , is via HitTest using presentationLayer property.

Inside touchBegan method

    x as! UIView

   if x.layer.presentationLayer()!.hitTest(touchlocation) != nil {
    x.alpha = 0
    //Layer touched
    }

Situation : During the animation,the first UIView overlaps\upon the second UIView. At that point i'm "touching" the topest UIView.
Problem : HitTest return the lowest\deepest layer , So the bottom UIView layer will be the recognized one.
Question : How do i get the first/frontmost UIView layer instead of the deepest?


Solution

  • Based on our chat we found out what was actually happening.

    Basically, to detect the touches, you were iterating over all the subviews in the view controller and performing a hitTest on each of their presentationLayers to detect the touch during an animation.

    There were a few problems we discovered:

    1. hitTest was returning a sublayer of the current subview's (x) layer and was unknowingly being conflated with another view (y).
    2. The for loop used to iterate over the subviews wasn't utilizing the break statement once it found its target, resulting in the confusion in 1 above.
    3. By iterating over the subviews in normal order, the views were being searched back-to-front because of the way that addSubview and subviews are structured.

    Solution

    Ultimately the solution came from fixing these issues by:

    • Iterating over the subviews in reverse order using for x in self.view.subviews.reverse()
    • Testing to see if the layer returned by hitTest was a sublayer of the current view that was being tested (pseudo-code) if layerThatWasHit.superlayer == x.presentationLayer()
    • Adding the break statement inside of the if where the view hit was detected to terminate the for loop and prevent multiple executions of the code that results in a tap on one of the animated views.