Home > Blockchain >  Disable Scrolling of UIScrollView on part of the ScrollView.frame
Disable Scrolling of UIScrollView on part of the ScrollView.frame

Time:03-15

Hello hope everyone is safe. I have a ViewController(vc) which contains a UIScrollView(scrlView). The scrlView contains two other ViewControllers(vc1 & vc2). On the vc1 I have a button which pressed adds a subview(subViewVc1) to vc1. In order to not be shown on the other scrlView page on delegation begin dragging I remove the subviewVc1. The problem I have is that I can't deactivate scrolling of the scrlView where the subViewVc1 frame is.

I have tried multiple ways as subclassing the scrollview as modifying touchesBegan, but touchesBegan recognises a touch, if the user perform even a small drag the gesture is not recognised anymore. I have tried to add a swipe gesture recogniser but I realised it interfere with the scrollview gesture. Anybody has any idea on what to do?

CodePudding user response:

Here is one way this can be achieved, instead of disabling a specific frame, you can disable interaction on over a specific view:

  1. SubClass the UIView you add on button tap with no real implementation but just for recognition - you can also use view tags if you prefer
  2. SubClass the UIScrollView and implement touchesShouldCancel to cancel scrolling when interacting with a specific view
  3. Set scrollView.delaysContentTouches = false

Implementation

First I tried to recreate your situation, I created the child view controller class with a button which will go into the scrollview:

class ChildVC: UIViewController
{
    let button = UIButton(type: .system)
    
    override func viewDidLoad()
    {
        super.viewDidLoad()
        view.backgroundColor = .yellow
        configureButton()
    }
    
    private func configureButton()
    {
        button.setTitle("Add View", for: .normal)
        button.translatesAutoresizingMaskIntoConstraints = false
        button.addTarget(self,
                         action: #selector(didTapAddView),
                         for: .touchUpInside)
        view.addSubview(button)
        
        view.addConstraints([
        
            button.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            button.bottomAnchor.constraint(equalTo: view.bottomAnchor),
            button.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            button.heightAnchor.constraint(equalToConstant: 50)
        
        ])
    }
    
    @objc
    private func didTapAddView()
    {
        let customView = UIView()
        customView.backgroundColor = .white
        customView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(customView)
        
        view.addConstraints([
        
            customView.leadingAnchor.constraint(equalTo: view.leadingAnchor,
                                           constant: 16),
            customView.topAnchor.constraint(equalTo: view.topAnchor,
                                           constant: 16),
            customView.trailingAnchor.constraint(equalTo: view.trailingAnchor,
                                           constant: -16),
            customView.bottomAnchor.constraint(equalTo: button.topAnchor,
                                           constant: -16)
        
        ])
    }
}

Then I created your container view controller with the scroll view and embedded the child vc into the view controller:

class ScrollTestViewController: UIViewController
{
    private let scrollView = UIScrollView()
    private let childVC1 = ChildVC()
    private let childVC2 = ChildVC()
    
    private let childVCHeight: CGFloat = 250
    private let childVCWidth: CGFloat = UIScreen.main.bounds.width
    
    override func viewDidLoad()
    {
        super.viewDidLoad()
        view.backgroundColor = .white
        title = "Scroll Test"
        configureScrollView()
        configureChildVC1()
        configureChildVC2()
    }
    
    override func viewDidLayoutSubviews()
    {
        super.viewDidLayoutSubviews()
        scrollView.contentSize = CGSize(width: childVC1.view.bounds.width * 2,
                                        height: scrollView.bounds.height)
    }
    
    private func configureScrollView()
    {
        scrollView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(scrollView)
        
        view.addConstraints([
        
            scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            scrollView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
            scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
        
        ])
    }
    
    private func configureChildVC1()
    {
        addChild(childVC1)
        childVC1.view.translatesAutoresizingMaskIntoConstraints = false
        scrollView.addSubview(childVC1.view)
        
        view.addConstraints([
        
            childVC1.view.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor),
            childVC1.view.centerYAnchor.constraint(equalTo: scrollView.centerYAnchor),
            childVC1.view.widthAnchor.constraint(equalToConstant: childVCWidth),
            childVC1.view.heightAnchor.constraint(equalToConstant: childVCHeight)
        
        ])
    }
    
    private func configureChildVC2()
    {
        addChild(childVC2)
        childVC2.view.translatesAutoresizingMaskIntoConstraints = false
        scrollView.addSubview(childVC2.view)
        
        view.addConstraints([
        
            childVC2.view.leadingAnchor.constraint(equalTo: childVC1.view.trailingAnchor),
            childVC2.view.centerYAnchor.constraint(equalTo: scrollView.centerYAnchor),
            childVC2.view.widthAnchor.constraint(equalToConstant: childVCWidth),
            childVC2.view.heightAnchor.constraint(equalToConstant: childVCHeight)
        
        ])
    }
}

After doing this, you will get this result:

Child View controller swift iOS embed view controller in UIScrollView

No different, user can scroll anywhere even when swiping on top of the newly added view.

So I made these changes:

  1. Create a custom view subclass so I can recognize which view I am dragging over
class CustomView: UIView
{
    
}
  1. Create a custom scrollview to disable interaction over specific views
fileprivate class CustomScrollView: UIScrollView
{
    override func touchesShouldCancel(in view: UIView) -> Bool
    {
        // Just for demo, I added this
        if view is CustomView
        {
            print("Scroll disabled, tapping custom view")
        }
        else
        {
            print("Scroll enabled")
        }
        
        // You only need this line
        return !(view is CustomView)
    }
}
  1. Make the changes in the container view controller with the scroll view
// Replace private let scrollView = UIScrollView() with
private let scrollView = CustomScrollView()

// Add this as well
scrollView.delaysContentTouches = false
  1. Make changes in the child view controllers to use Custom view when tapping the button
@objc
private func didTapAddView()
{
    let customView = CustomView()
    customView.backgroundColor = .white
    customView.translatesAutoresizingMaskIntoConstraints = false
    view.addSubview(customView)
    
    view.addConstraints([
    
        customView.leadingAnchor.constraint(equalTo: view.leadingAnchor,
                                       constant: 16),
        customView.topAnchor.constraint(equalTo: view.topAnchor,
                                       constant: 16),
        customView.trailingAnchor.constraint(equalTo: view.trailingAnchor,
                                       constant: -16),
        customView.bottomAnchor.constraint(equalTo: button.topAnchor,
                                       constant: -16)
    
    ])
}

Now when you swipe over the view, it will prevent you from scrolling

Prevent Disable scroll in UIScrollView when swiping scrolling in UIView swift iOS

  • Related