Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get all views in hierarchy in my touch position - Swift

Tags:

ios

uikit

swift

How to get all UIViews at my touch position?

In image below I need to get all UIViews in touches with the yellow line.

Snippet from UI hierarchy:

enter image description here

I looking for a similar function to SpriteKit where we use to get all nodes at point with

self.nodes(at: touch.location(in: self))
like image 334
Saranjith Avatar asked Mar 05 '18 13:03

Saranjith


People also ask

How do I get touch position in Swift?

Basic Swift Code for iOS AppsStep 2 − Open Main. storyboard and add one label as shown below. On this label we will show the touch position. Step 3 − Create @IBOutlet for the label, name it touchPositionLabel.

How do I get Subviews in UIView Swift?

If you need a quick way to get hold of a view inside a complicated view hierarchy, you're looking for viewWithTag() – give it the tag to find and a view to search from, and this method will search all subviews, and all sub-subviews, and so on, until it finds a view with the matching tag number.

What is UITransitionView?

UITransitionView: Another private Apple class, this view is constrained by the LayoutContainerView and handles transitions between views by adding their wrapper. UIViewControllerWrapperView: This view does what it sounds like, even if the purpose of it doesn't seem obvious.

What is a view in Swift?

Views are the fundamental building blocks of your app's user interface, and the UIView class defines the behaviors that are common to all views. A view object renders content within its bounds rectangle, and handles any interactions with that content.


2 Answers

You have to traverse all views in the hierachy like:

extension UIView {
    func allViews(for touch: UITouch) -> [UIView] {
        return self.allViews(at: touch.location(in: self))
    }

    func allViews(at point: CGPoint) -> [UIView] {
        var stack = [UIView]()
        var result = [UIView]()

        stack.append(self)
        while let view = stack.popLast() {
            let localPoint = view.convert(point, from: self)

            if view.bounds.contains(localPoint) {
                result.append(view)
            }
            stack.append(contentsOf: view.subviews)
        }
        return result
    }
}

You can either start with any super view which contains all appropriate views (e.g the view of a view controller) or the window. The point for the second method must be relative to the bounds of the view which is represented by self.

like image 104
clemens Avatar answered Sep 19 '22 13:09

clemens


This can be achieved in two steps:

  1. get all views starting from a certain view

    func descendants(of view: UIView) -> [UIView] {
        return view.subviews + view.subviews.flatMap(descendants(of:))
    }
    
  2. filter the view that contain the touch point:

    let touchLocation = touchGesture.location(in: nil)
    let matchingViews = descendants(of: UIApplication.shared.keyWindow!)
        .filter { $0.convert($0.bounds, to: nil).contains(touchLocation) }
    
like image 24
Cristik Avatar answered Sep 20 '22 13:09

Cristik