This is the fourth day of my participation in the More text Challenge. For details, see more text Challenge

In UIKit, when a parent control of a scrollView (or a subclass of it) contains a scrollView or a subclass of it, that is, nested scrollView, when the scrollView slides to the top or bottom, and then up or down, The parent control of type scrollView is linked to it.

This, for the most part, maintains the continuity of the animation. But some cases need to prohibit this linkage, such as live class App, the outer layer for a TableView in the show is live broadcast room, in the broadcast room will also have a message display TableView, at this time you need to prohibit scrolling IM messages prohibit the outer TableView scrolling.

How do you cancel this linkage?

Scenario 1: Sending notifications when the child ScrollView starts scrolling and stops (failed)

The first thing that comes to mind is listening for scroll events in the child scrollView, sending notifications when the child scrollView starts and ends scrolling, changing the isScrollEnabled property of the parent scrollView, and unscrolling. The code is as follows:

func scrollViewDidScroll(_ scrollView: UIScrollView) {
    // Send the start scrolling notification, and have the parent class disable scrolling
}

func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
    let scrollToScrollStop = !scrollView.isTracking && !scrollView.isDragging && !scrollView.isDecelerating
    if (scrollToScrollStop) {
        didEndScroll()
    }
}

func scrollViewDidEndDragging(_ scrollView: UIScrollView.willDecelerate decelerate: Bool) {
    if (!decelerate) {
        let scrollToScrollStop = !scrollView.isTracking && !scrollView.isDragging && !scrollView.isDecelerating;
        if (scrollToScrollStop) {
            didEndScroll()
        }
    }
}

func didEndScroll(a) {
    // Send a stop scrolling notification to the parent class to start scrolling
}
Copy the code

But it turns out that scrolling on the scrollable edge of a child ScrollView doesn’t trigger the scrollViewDidScroll method, so that doesn’t work.

Option 2: Override the child ScrollView’s hitTest method

Because every event in the parent control is passed to the child control through hitTest, you can get the parent scrollView control through the responder chain, modify its isScrollEnabled property, and unlinkage.

override func hitTest(_ point: CGPoint.with event: UIEvent?). -> UIView? {
    var responder = next
    // The parent control is a scrollView control
    var superScrollView: UIScrollView?

    // Look up the control whose parent control is scrollView through the next responder
    / / when superScrollView! = nil indicates that the parent control object of the scrollView or its subclass is obtained conditionally
    while responder ! = nil && superScrollView = = nil {
        if let scrollView = responder as? UIScrollView {
            superScrollView = scrollView
        } else {
            responder = responder?.next
        }
    }

    let view = super.hitTest(point, with: event)

    // If view! = nil, indicating that the gesture was issued by the current scrollView/tableView,
    // Disable scrolling of the superScrollView;
    // Otherwise the opposite is true.
    if view ! = nil {
        superScrollView?.isScrollEnabled = false
    } else {

        superScrollView?.isScrollEnabled = true
    }

    return view
}
Copy the code

The experiment shows that this scheme is feasible.

other

When you change the isScrollEnabled of a tableView, the contentOffset of the tableView is changed, causing the offset of the tableView to jitter. This is caused by the automatic setting of scrollView margins, which can be turned off by modifying the following properties:

// Before iOS 11, modify the ViewController properties
automaticallyAdjustsScrollViewInsets = false
// After iOS 11, modify the properties of ScrollView
tableView.contentInsetAdjustmentBehavior = .never
Copy the code