Retrieving Specific Subviews from Touch Events in SwiftUI Using Subclassing Views and Coordinate Space Conversion

Grab View from Touch Event

In this article, we will explore how to retrieve a specific subview from a touch event in SwiftUI. We will dive deep into the details of touch events, view hierarchy, and subclassing views to achieve our goal.

Touch Events in SwiftUI

When working with SwiftUI views, it’s essential to understand how touch events work. When a user touches your app, the operating system sends a touch event to your app, which can be caught using a @StateVariable or a delegate method.

However, when dealing with complex view hierarchies, it can be challenging to determine which specific subview was touched. This is where subclassing views and converting touch coordinates comes into play.

Subclassing Views

To solve the problem of retrieving a specific subview from a touch event, we need to subclass our child views. By doing so, we gain access to their own view hierarchy and can calculate the touch position relative to each individual view.

Here’s an example of how you might do this:

// Create a new view class that inherits from `View`
struct Subview: View {
    let superViewWidth: CGFloat = 300.0
    let superViewHeight: CGFloat = 400.0
    
    var body: some View {
        // Your subview content here...
        
        // Track touch events using the `onTapGesture` method
        onTapGesture {
            // Calculate the touch position relative to this view
            guard let location = $0.location else { return }
            
            // Convert the touch position from coordinate space to super-view's coordinate space
            let pointInSuperView = CGPoint(x: (location.x * self.superViewWidth) / UIScreen.main.bounds.width,
                                           y: (location.y * self.superViewHeight) / UIScreen.main.bounds.height)
            
            // Use this converted point to determine which subview was touched
            print(pointInSuperView)
        }
    }
}

By subclassing our child view and using the onTapGesture method, we can track touch events and calculate the touch position relative to each individual view.

Converting Touch Coordinates

To convert touch coordinates from coordinate space to super-view’s coordinate space, we use a simple scaling factor. The idea is that the subview’s origin (0, 0) is at the top-left corner of the super-view, and the subview’s width/height matches the super-view’s width/height.

We apply this scaling factor by dividing the touch location’s coordinates by the super-view’s bounds’ width and height. This gives us the equivalent point in the super-view’s coordinate space.

Converting Touch Coordinates Formula

Given a touch location at (x, y) with respect to the subview, we can calculate its equivalent point in the super-view’s coordinate space using the following formula:

let pointInSuperView = CGPoint(x: (x * self.superViewWidth) / UIScreen.main.bounds.width,
                               y: (y * self.superViewHeight) / UIScreen.main.bounds.height)

Using This Formula

Now that we have a way to convert touch coordinates, let’s see how we can use this information to determine which subview was touched.

When you create your child views using the Subview class, make sure each view has its own unique identifier. You can do this by creating a new constant for each view:

struct Subview1: View {
    // ...
}

struct Subview2: View {
    // ...
}

Then, when you receive a touch event, use the converted point to determine which subview was touched. Here’s an example:

// Track touch events using the `onTapGesture` method
onTapGesture {
    // Calculate the touch position relative to this view
    guard let location = $0.location else { return }
    
    // Convert the touch position from coordinate space to super-view's coordinate space
    let pointInSuperView = CGPoint(x: (location.x * self.superViewWidth) / UIScreen.main.bounds.width,
                                   y: (location.y * self.superViewHeight) / UIScreen.main.bounds.height)
    
    // Use this converted point to determine which subview was touched
    if pointInSuperView.x < self.subview1Offset {
        print("Subview 1 was touched")
    } else if pointInSuperView.x >= self.subview1Offset + self.superViewWidth {
        print("Subview 2 was touched")
    }
}

By using this formula to convert touch coordinates and determining which subview was touched based on the converted point, we can achieve our goal of retrieving a specific subview from a touch event.

Conclusion

In this article, we explored how to retrieve a specific subview from a touch event in SwiftUI. By subclassing views, tracking touch events using onTapGesture, and converting touch coordinates, we can determine which subview was touched.

Remember, when working with complex view hierarchies, understanding the subtleties of view hierarchy and coordinate space conversion is crucial for achieving your design goals. With practice and patience, you’ll become proficient in using these techniques to solve challenging problems in iOS development.


Last modified on 2024-02-23