在 ScrollView.frame 的部分禁用 UIScrollView 的滚动
Disable Scrolling of UIScrollView on part of the ScrollView.frame
你好希望每个人都安全。我有一个包含 UIScrollView(scrlView) 的 ViewController(vc)。 scrlView 包含另外两个 ViewControllers(vc1 & vc2)。在 vc1 上,我有一个按钮,按下它会向 vc1 添加一个子视图(subViewVc1)。为了不显示在委托开始拖动的其他scrlView页面上,我删除了subviewVc1。我遇到的问题是我无法停用 subViewVc1 框架所在的 scrlView 的滚动。
我尝试了多种方法,将滚动视图子类化为修改 touchesBegan,但 touchesBegan 可以识别触摸,如果用户执行即使是很小的拖动,手势也不再被识别。我试图添加一个滑动手势识别器,但我意识到它会干扰滚动视图手势。
有人知道该怎么做吗?
这是实现此目的的一种方法,您可以禁用特定视图上的交互,而不是禁用特定框架:
- 子类
UIView
您在点击按钮时添加的子类,没有真正的实现,只是为了识别 - 如果您愿意,也可以使用视图标签
- 子类
UIScrollView
并实现 touchesShouldCancel
以在与特定视图交互时取消滚动
- 设置
scrollView.delaysContentTouches = false
实施
首先,我尝试重现您的情况,我创建了子视图控制器 class,带有一个将进入滚动视图的按钮:
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)
])
}
}
然后我用滚动视图创建了容器视图控制器,并将子 vc 嵌入到视图控制器中:
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)
])
}
}
执行此操作后,您将得到以下结果:
没什么不同,即使在新添加的视图顶部滑动,用户也可以滚动到任何地方。
所以我做了这些改变:
- 创建一个自定义视图子class 这样我就可以识别我拖过哪个视图
class CustomView: UIView
{
}
- 创建自定义滚动视图以禁用特定视图的交互
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)
}
}
- 使用滚动视图在容器视图控制器中进行更改
// Replace private let scrollView = UIScrollView() with
private let scrollView = CustomScrollView()
// Add this as well
scrollView.delaysContentTouches = false
- 更改子视图控制器以在点击按钮时使用自定义视图
@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)
])
}
现在,当您在视图上滑动时,它会阻止您滚动
根据 Mihai(OP)的评论更新
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
滚动视图可以将 UIView 添加到它的视图层次结构中,因此您只能检查您正在滚动的视图是否是特定类型的视图。
但是,如果您想在按钮点击而不是 UIView 上添加视图控制器,我仍然认为该解决方案可行。
- 保持自定义视图不变:
fileprivate class CustomView: UIView
{
}
- 创建视图控制器子class并将默认
view
更改为自定义视图子class
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
}
}
- 在点击按钮而不是 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)
])
}
- 自定义滚动视图没有变化,因为它仍应检测作为视图控制器内主视图的自定义视图
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)
}
}
你好希望每个人都安全。我有一个包含 UIScrollView(scrlView) 的 ViewController(vc)。 scrlView 包含另外两个 ViewControllers(vc1 & vc2)。在 vc1 上,我有一个按钮,按下它会向 vc1 添加一个子视图(subViewVc1)。为了不显示在委托开始拖动的其他scrlView页面上,我删除了subviewVc1。我遇到的问题是我无法停用 subViewVc1 框架所在的 scrlView 的滚动。
我尝试了多种方法,将滚动视图子类化为修改 touchesBegan,但 touchesBegan 可以识别触摸,如果用户执行即使是很小的拖动,手势也不再被识别。我试图添加一个滑动手势识别器,但我意识到它会干扰滚动视图手势。 有人知道该怎么做吗?
这是实现此目的的一种方法,您可以禁用特定视图上的交互,而不是禁用特定框架:
- 子类
UIView
您在点击按钮时添加的子类,没有真正的实现,只是为了识别 - 如果您愿意,也可以使用视图标签 - 子类
UIScrollView
并实现touchesShouldCancel
以在与特定视图交互时取消滚动 - 设置
scrollView.delaysContentTouches = false
实施
首先,我尝试重现您的情况,我创建了子视图控制器 class,带有一个将进入滚动视图的按钮:
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)
])
}
}
然后我用滚动视图创建了容器视图控制器,并将子 vc 嵌入到视图控制器中:
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)
])
}
}
执行此操作后,您将得到以下结果:
没什么不同,即使在新添加的视图顶部滑动,用户也可以滚动到任何地方。
所以我做了这些改变:
- 创建一个自定义视图子class 这样我就可以识别我拖过哪个视图
class CustomView: UIView
{
}
- 创建自定义滚动视图以禁用特定视图的交互
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)
}
}
- 使用滚动视图在容器视图控制器中进行更改
// Replace private let scrollView = UIScrollView() with
private let scrollView = CustomScrollView()
// Add this as well
scrollView.delaysContentTouches = false
- 更改子视图控制器以在点击按钮时使用自定义视图
@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)
])
}
现在,当您在视图上滑动时,它会阻止您滚动
根据 Mihai(OP)的评论更新
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
滚动视图可以将 UIView 添加到它的视图层次结构中,因此您只能检查您正在滚动的视图是否是特定类型的视图。
但是,如果您想在按钮点击而不是 UIView 上添加视图控制器,我仍然认为该解决方案可行。
- 保持自定义视图不变:
fileprivate class CustomView: UIView
{
}
- 创建视图控制器子class并将默认
view
更改为自定义视图子class
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
}
}
- 在点击按钮而不是 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)
])
}
- 自定义滚动视图没有变化,因为它仍应检测作为视图控制器内主视图的自定义视图
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)
}
}