10.4 C
Canberra
Tuesday, June 2, 2026

ios – Go faucet gestures by way of a view with different gestures


I’ve two overlapping UIViews.

The again view has a UITapGestureRecognizer, and the entrance view has one other gesture recognizer, for instance a UILongPressGestureRecognizer or a UIPanGestureRecognizer.

What I need is:

  • if the person performs the gesture dealt with by the entrance view, the entrance view ought to deal with it;
  • if the person performs one other gesture, equivalent to a faucet, the view behind ought to obtain it;
  • the entrance view mustn’t block gestures it doesn’t deal with.

I initially had the difficulty with a pan gesture, however the identical drawback additionally occurs with an extended press gesture.

I attempted setting cancelsTouchesInView = false, and I additionally tried utilizing UIGestureRecognizerDelegate, however the entrance view nonetheless wins hit-testing. Because of this, the faucet gesture on the view behind is just not triggered when tapping contained in the overlapping space.

Here’s a minimal instance utilizing an extended press gesture on the entrance view:

import SwiftUI
import UIKit

struct TestGestureConflict: UIViewControllerRepresentable {
    func makeUIViewController(context: Context) -> TestGestureConflictViewController {
        TestGestureConflictViewController()
    }

    func updateUIViewController(_ uiViewController: TestGestureConflictViewController, context: Context) {}
}

ultimate class TestGestureConflictViewController: UIViewController {
    personal let tapView = UIView()
    personal let longPressView = UIView()

    override func viewDidLoad() {
        tremendous.viewDidLoad()

        view.backgroundColor = .systemBackground
        configureTapView()
        configureLongPressView()
        configureGestures()
    }

    personal func configureTapView() {
        tapView.translatesAutoresizingMaskIntoConstraints = false
        tapView.backgroundColor = UIColor.systemBlue.withAlphaComponent(0.35)
        tapView.layer.borderColor = UIColor.systemBlue.cgColor
        tapView.layer.borderWidth = 2

        view.addSubview(tapView)

        NSLayoutConstraint.activate([
            tapView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            tapView.centerYAnchor.constraint(equalTo: view.centerYAnchor),
            tapView.widthAnchor.constraint(equalToConstant: 120),
            tapView.heightAnchor.constraint(equalToConstant: 120)
        ])
    }

    personal func configureLongPressView() {
        longPressView.translatesAutoresizingMaskIntoConstraints = false
        longPressView.backgroundColor = UIColor.systemOrange.withAlphaComponent(0.35)
        longPressView.layer.borderColor = UIColor.systemOrange.cgColor
        longPressView.layer.borderWidth = 2

        view.addSubview(longPressView)

        NSLayoutConstraint.activate([
            longPressView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            longPressView.centerYAnchor.constraint(equalTo: view.centerYAnchor),
            longPressView.widthAnchor.constraint(equalToConstant: 220),
            longPressView.heightAnchor.constraint(equalToConstant: 220)
        ])
    }

    personal func configureGestures() {
        let tapGesture = UITapGestureRecognizer(goal: self, motion: #selector(handleTap))
        tapGesture.delegate = self
        tapView.addGestureRecognizer(tapGesture)

        let longPressGesture = UILongPressGestureRecognizer(goal: self, motion: #selector(handleLongPress))
        longPressGesture.cancelsTouchesInView = false
        longPressGesture.delegate = self
        longPressView.addGestureRecognizer(longPressGesture)
    }

    @objc personal func handleTap(_ gestureRecognizer: UITapGestureRecognizer) {
        guard gestureRecognizer.state == .ended else { return }
        print("faucet")
    }

    @objc personal func handleLongPress(_ gestureRecognizer: UILongPressGestureRecognizer) {
        change gestureRecognizer.state {
        case .started:
            print("lengthy press started")

        case .ended, .cancelled:
            print("lengthy press ended")

        default:
            break
        }
    }
}

extension TestGestureConflictViewController: UIGestureRecognizerDelegate {
    func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive contact: UITouch) -> Bool {
        // That is referred to as for the lengthy press gesture, however returning true or false
        // doesn't make the tapView behind obtain the faucet within the overlapping space.
        return true
    }
}

On this instance, longPressView is added above tapView.

Anticipated conduct:

  • lengthy press contained in the orange view → dealt with by longPressView;
  • faucet contained in the overlapping blue/orange space → dealt with by tapView.

Precise conduct:

  • lengthy press works on the entrance view;
  • faucet doesn’t attain the view behind;
  • cancelsTouchesInView = false doesn’t remedy the issue as a result of the entrance view remains to be the results of hit-testing.

My understanding is that this isn’t actually about UIPanGestureRecognizer or UILongPressGestureRecognizer, however about UIKit hit-testing: as soon as the entrance view is chosen because the hit-tested view, the view behind doesn’t take part in contact supply.

What’s the appropriate UIKit strategy for this?

Ought to I remedy this with:

  • customized hitTest(_:with:) / level(inside:with:),
  • a UIGestureRecognizerDelegate,
  • manually forwarding touches,
  • attaching the gesture recognizer to a typical dad or mum view,
  • or altering the view hierarchy?

What’s the really helpful technique to let a entrance view deal with solely a particular gesture whereas permitting different gestures, like faucets, to be dealt with by views behind it?

Related Articles

LEAVE A REPLY

Please enter your comment!
Please enter your name here

[td_block_social_counter facebook="tagdiv" twitter="tagdivofficial" youtube="tagdiv" style="style8 td-social-boxed td-social-font-icons" tdc_css="eyJhbGwiOnsibWFyZ2luLWJvdHRvbSI6IjM4IiwiZGlzcGxheSI6IiJ9LCJwb3J0cmFpdCI6eyJtYXJnaW4tYm90dG9tIjoiMzAiLCJkaXNwbGF5IjoiIn0sInBvcnRyYWl0X21heF93aWR0aCI6MTAxOCwicG9ydHJhaXRfbWluX3dpZHRoIjo3Njh9" custom_title="Stay Connected" block_template_id="td_block_template_8" f_header_font_family="712" f_header_font_transform="uppercase" f_header_font_weight="500" f_header_font_size="17" border_color="#dd3333"]
- Advertisement -spot_img

Latest Articles