Here is one way this can be achieved, instead of disabling a specific frame, you can disable interaction on over a specific view:
- 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
- SubClass the
UIScrollView and implement touchesShouldCancel to cancel scrolling when interacting with a specific view
- 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:

No different, user can scroll anywhere even when swiping on top of the newly added view.
So I made these changes:
- Create a custom view subclass so I can recognize which view I am dragging over
class CustomView: UIView
{
}
- 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)
}
}
- 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
- 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

Update based on Mihai's (OP's) comments
Do you have any idea how I could do it for a UIViewController instead
of UIView, Im asking because the view is currently UIViewController
and I lose some specifications about it in transforming it to an
UIView.
So on the button press the view will add a UIViewController instead of
a UIView
A scrollview can add UIViews to it's view hierarchy therefore you can only check if the view you are scrolling on is a specific type of view.
However, I still think the solution can work if you want to add a view controller on a button tap instead of a UIView.
- Keep the custom view as it is:
fileprivate class CustomView: UIView
{
}
- Create a view controller subclass and change the default
view to be the custom view subclass
fileprivate class PurpleVC: UIViewController
{
override func loadView()
{
super.loadView()
// Change the default view from a UIView
// to be of type CustomView
view = CustomView()
view.backgroundColor = .purple
}
}
- Add the new view controller on button tap instead of UIView
@objc
private func didTapAddView()
{
let customVC = PurpleVC()
customVC.view.translatesAutoresizingMaskIntoConstraints = false
addChild(customVC)
view.addSubview(customVC.view)
view.addConstraints([
customVC.view.leadingAnchor.constraint(equalTo: view.leadingAnchor,
constant: 16),
customVC.view.topAnchor.constraint(equalTo: view.topAnchor,
constant: 16),
customVC.view.trailingAnchor.constraint(equalTo: view.trailingAnchor,
constant: -16),
customVC.view.bottomAnchor.constraint(equalTo: button.topAnchor,
constant: -16)
])
}
- No change to custom scroll view as it should still detect the custom view which is the main view inside your view controller
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)
}
}