-2

My view hierarchy

This is the View layer I created. I want to transfer all Touch Events that happen in UIView to UIScrollView, but I don't know how. I didn't put it as a SubView of ScrollView because UIView should be lock. I want to use the default Gesture in UIScrollView for up or down drag (PanGesture) that happens in UIView. I made a PanGesture in the ViewController using ResponderChain to respond, but when it is Touch State.Ended like ScrollView I couldn't implement a scroll that ends smoothly. Any way to solve this problem? Thank you for your reply.

jkey
  • 3
  • 2

1 Answers1

0

One approach that may work for you...

Use a UIView subclass for your "stationary pink view" and implement hitTest:

class MyParentView: UIView {
    override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
        let hitView = super.hitTest(point, with: event)
        if hitView == self {
            return nil
        } else {
            return hitView
        }
    }
}

that will allow the touch/drag to effectively "pass through" to the scroll view underneath, while still allowing user interaction with its own subviews.

Here's an example controller showing it in use:

class ViewController: UIViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        view.backgroundColor = UIColor(red: 0.61, green: 0.86, blue: 0.93, alpha: 1.0)
        
        let scrollView = UIScrollView()
        scrollView.backgroundColor = UIColor(red: 0.96, green: 0.93, blue: 0.79, alpha: 1.0)
        
        let pinkView = MyParentView()
        pinkView.backgroundColor = UIColor(red: 0.96, green: 0.70, blue: 0.86, alpha: 1.0)
    
        // a button and label to add to the pink view
        let btn = UIButton()
        btn.setTitle("Some Button", for: [])
        btn.setTitleColor(.white, for: .normal)
        btn.setTitleColor(.lightGray, for: .highlighted)
        btn.backgroundColor = .systemRed
        btn.layer.cornerRadius = 8
        btn.translatesAutoresizingMaskIntoConstraints = false

        let label = UILabel()
        label.text = "Some Label"

        [btn, label].forEach { v in
            v.translatesAutoresizingMaskIntoConstraints = false
            pinkView.addSubview(v)
        }

        // a vertical stack view with a bunch of labels
        //  to use as the scroll content
        let stack = UIStackView()
        stack.axis = .vertical
        stack.spacing = 40.0
        for i in 1...30 {
            let l = UILabel()
            l.text = "Label \(i)"
            stack.addArrangedSubview(l)
        }

        // add stack view to scroll view
        stack.translatesAutoresizingMaskIntoConstraints = false
        scrollView.addSubview(stack)
        
        // add scroll view to view
        scrollView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(scrollView)

        // add pink view ot view
        pinkView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(pinkView)

        let g = view.safeAreaLayoutGuide
        let cg = scrollView.contentLayoutGuide
        let fg = scrollView.frameLayoutGuide
        
        NSLayoutConstraint.activate([
            
            // constrain scroll view to all 4 sides with 20-points "padding"
            scrollView.topAnchor.constraint(equalTo: g.topAnchor, constant: 20.0),
            scrollView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
            scrollView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
            scrollView.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: -20.0),

            // constrain stack view to all 4 sides of scroll view's Content Layout Guide
            //  with 20-points "padding"
            stack.topAnchor.constraint(equalTo: cg.topAnchor, constant: 20.0),
            stack.leadingAnchor.constraint(equalTo: cg.leadingAnchor, constant: 20.0),
            stack.trailingAnchor.constraint(equalTo: cg.trailingAnchor, constant: -20.0),
            stack.bottomAnchor.constraint(equalTo: cg.bottomAnchor, constant: -20.0),
            // stack view width is scroll view's Content Layout Guide width
            //  minus 40-points (20-points on each side)
            stack.widthAnchor.constraint(equalTo: fg.widthAnchor, constant: -40.0),
            
            // constrain pink view relative to scroll view
            //  10-points from top
            //  100-points leading (so we can see the scrolling)
            //  trailing
            //  height of 320-points
            pinkView.topAnchor.constraint(equalTo: scrollView.topAnchor, constant: 10.0),
            pinkView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor, constant: 100.0),
            pinkView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor, constant: 0.0),
            pinkView.heightAnchor.constraint(equalToConstant: 320.0),

            // constrain button and label inside pink view
            btn.topAnchor.constraint(equalTo: pinkView.topAnchor, constant: 40.0),
            btn.leadingAnchor.constraint(equalTo: pinkView.leadingAnchor, constant: 20.0),
            btn.trailingAnchor.constraint(equalTo: pinkView.trailingAnchor, constant: -20.0),
            
            label.topAnchor.constraint(equalTo: btn.bottomAnchor, constant: 20.0),
            label.leadingAnchor.constraint(equalTo: pinkView.leadingAnchor, constant: 20.0),
            label.trailingAnchor.constraint(equalTo: pinkView.trailingAnchor, constant: -20.0),
            
        ])
        
        // add an action for the button so we know it can be tapped
        btn.addTarget(self, action: #selector(btnTapped(_:)), for: .touchUpInside)

        // a little "styling"
        scrollView.layer.cornerRadius = 16
        pinkView.layer.cornerRadius = 16
    }
    
    @objc func btnTapped(_ sender: Any?) {
        print("Tapped")
    }

}

The result - I inset the "pink view" from the leading edge of the scroll view to make it easy to see the scroll contents scrolling behind it:

enter image description here

Touching and dragging on the yellow OR the pink will scroll the scrollView as normal.

DonMag
  • 69,424
  • 5
  • 50
  • 86