我一直在申请一段时间,但由于NDA,我无法提出这个问题。
我的ViewController加载了UIPageViewController。视图控制器具有由PageViewControllers手势识别器覆盖的按钮。例如,我在viewcontroller的右侧有一个按钮,当你按下按钮时,PageViewController接管并更改页面。
如何让按钮接收触摸并取消PageViewController中的手势识别器?
我认为PageViewController使我的ViewController成为其视图的子视图。
我知道我可以关闭所有的手势,但这不是我想要的效果。
我更喜欢不要将PageViewController子类化,因为苹果称这个类不是要子类化的。
答案 0 :(得分:54)
这是另一种解决方案,可以在Xcode模板的viewDidLoad
部分后面的self.view.gestureRecognizers = self.pageViewController.gestureRecognizers
模板中添加。它避免弄乱手势识别器的内容或处理其代表。它只是从视图中删除了轻击手势识别器,只留下滑动识别器。
self.view.gestureRecognizers = self.pageViewController.gestureRecognizers;
// Find the tap gesture recognizer so we can remove it!
UIGestureRecognizer* tapRecognizer = nil;
for (UIGestureRecognizer* recognizer in self.pageViewController.gestureRecognizers) {
if ( [recognizer isKindOfClass:[UITapGestureRecognizer class]] ) {
tapRecognizer = recognizer;
break;
}
}
if ( tapRecognizer ) {
[self.view removeGestureRecognizer:tapRecognizer];
[self.pageViewController.view removeGestureRecognizer:tapRecognizer];
}
现在要在页面之间切换,您必须滑动。点击现在只适用于页面视图顶部的控件(这就是我所追求的)。
答案 1 :(得分:50)
您可以覆盖
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
shouldReceiveTouch:(UITouch *)touch
更好地控制PageViewController何时应该接收触摸而不是。请参阅Dev API Gesture Recognizers
中的“防止手势识别器分析”我的解决方案在UIPageViewController的RootViewController中看起来像这样:
在viewDidLoad中:
//EDITED Need to take care of all gestureRecogizers. Got a bug when only setting the delegate for Tap
for (UIGestureRecognizer *gR in self.view.gestureRecognizers) {
gR.delegate = self;
}
覆盖:
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
//Touch gestures below top bar should not make the page turn.
//EDITED Check for only Tap here instead.
if ([gestureRecognizer isKindOfClass:[UITapGestureRecognizer class]]) {
CGPoint touchPoint = [touch locationInView:self.view];
if (touchPoint.y > 40) {
return NO;
}
else if (touchPoint.x > 50 && touchPoint.x < 430) {//Let the buttons in the middle of the top bar receive the touch
return NO;
}
}
return YES;
}
不要忘记将RootViewController设置为UIGestureRecognizerDelegate。
(仅供参考,我只是在横向模式下。)
编辑 - 上面的代码翻译成Swift 2:
在viewDidLoad中:
for gr in self.view.gestureRecognizers! {
gr.delegate = self
}
让页面视图控制器继承UIGestureRecognizerDelegate
,然后添加:
func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldReceiveTouch touch: UITouch) -> Bool {
if let _ = gestureRecognizer as? UITapGestureRecognizer {
let touchPoint = touch .locationInView(self.view)
if (touchPoint.y > 40 ){
return false
}else{
return true
}
}
return true
}
答案 2 :(得分:4)
我遇到了同样的问题。示例和文档在loadView或viewDidLoad中执行此操作:
self.view.gestureRecognizers = self.pageViewController.gestureRecognizers;
这将使用UIPageViewController的gestureRecognizers替换UIViewControllers视图中的手势识别器。现在,当触摸发生时,它们首先通过pageViewControllers手势识别器发送 - 如果它们不匹配,则将它们发送到子视图。
取消注释该行,一切都按预期工作。
菲利普
答案 3 :(得分:3)
将gestureRecognizers委托设置为viewController,如下所示,不再适用于ios 6
for (UIGestureRecognizer *gR in self.view.gestureRecognizers) {
gR.delegate = self;
}
答案 4 :(得分:3)
在较新版本中(我在Xcode 7.3中定位iOS 8.1+),这些解决方案似乎都不起作用。
接受的答案会因错误而崩溃:
UIScrollView的内置平移手势识别器必须将其滚动视图作为其委托。
目前排名最高的答案(来自Pat McG)不再有效,因为UIPageViewController
的scrollview似乎正在使用您无法检查的奇怪手势识别器子类。因此,语句if ( [recognizer isKindOfClass:[UITapGestureRecognizer class]] )
永远不会执行。
我选择将每个识别器上的cancelsTouchesInView设置为false,这样也允许UIPageViewController
的子视图接收触摸。
在viewDidLoad
:
guard let recognizers = self.pageViewController.view.subviews[0].gestureRecognizers else {
print("No gesture recognizers on scrollview.")
return
}
for recognizer in recognizers {
recognizer.cancelsTouchesInView = false
}
答案 5 :(得分:2)
我用过
for (UIScrollView *view in _pageViewController.view.subviews) {
if ([view isKindOfClass:[UIScrollView class]]) {
view.delaysContentTouches = NO;
}
}
允许点击进入UIPageViewController内的按钮
答案 6 :(得分:0)
如果您正在使用已经创建子类的按钮,则可以覆盖touchesBegan,touchesMoved和touchesEnded,根据需要调用您自己的程序化页面转向,但不要调用super并将通知链传递给通知链。
答案 7 :(得分:0)
也可以使用这个(感谢您的帮助,关于委托说):
// add UIGestureRecognizerDelegate
NSPredicate *tp = [NSPredicate predicateWithFormat:@"self isKindOfClass: %@", [UITapGestureRecognizer class]];
UITapGestureRecognizer *tgr = (UITapGestureRecognizer *)[self.pageViewController.view.gestureRecognizers filteredArrayUsingPredicate:tp][0];
tgr.delegate = self; // tap delegating
NSPredicate *pp = [NSPredicate predicateWithFormat:@"self isKindOfClass: %@", [UIPanGestureRecognizer class]];
UIPanGestureRecognizer *pgr = (UIPanGestureRecognizer *)[self.pageViewController.view.gestureRecognizers filteredArrayUsingPredicate:pp][0];
pgr.delegate = self; // pan delegating
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
CGPoint touchPoint = [touch locationInView:self.view];
if (UIInterfaceOrientationIsPortrait([[UIApplication sharedApplication] statusBarOrientation]) && touchPoint.y > 915 ) {
return NO; // if y > 915 px in portrait mode
}
if (UIInterfaceOrientationIsLandscape([[UIApplication sharedApplication] statusBarOrientation]) && touchPoint.y > 680 ) {
return NO; // if y > 680 px in landscape mode
}
return YES;
}
完美地为我工作:)
答案 8 :(得分:0)
只需在RootViewController中创建一个子视图(链接到新的IBOutlet gesturesView),并将手势分配给这个新视图。此视图覆盖了您希望手势启用的屏幕部分。
在viewDidLoad中更改:
self.view.gestureRecognizers = self.pageViewController.gestureRecognizers;
到:
self.gesturesView.gestureRecognizers = self.pageViewController.gestureRecognizers;
答案 9 :(得分:0)
这是最适合我的解决方案我尝试了JRAMER的答案很好,除了我在分页超出界限时会出现错误(第-1页或第23页)
PatMCG解决方案没有给我足够的灵活性,因为它取消了所有的taprecognizer,我仍然想要点击但不在我的标签内
在我的UILabel中,我只是按照以下方式覆盖,仅取消了我的标签
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
if ([gestureRecognizer isKindOfClass:[UITapGestureRecognizer class]]) {
return NO;
} else {
return YES;
}
}
答案 10 :(得分:0)
我定期创建pageviewcontrollers,因为我的用户会跳转,卷曲并滑动到各种不同的页面视图。在创建新的pageviewcontroller的例程中,我使用了上面显示的优秀代码的稍微简单版本:
UIPageViewController *npVC = [[UIPageViewController alloc]
initWithTransitionStyle:UIPageViewControllerTransitionStylePageCurl
navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal
options: options];
...
// Find the pageView tap gesture recognizer so we can remove it!
for (UIGestureRecognizer* recognizer in npVC.gestureRecognizers) {
if ( [recognizer isKindOfClass:[UITapGestureRecognizer class]] ) {
UIGestureRecognizer* tapRecognizer = recognizer;
[npVC.view removeGestureRecognizer:tapRecognizer];
break;
}
}
现在水龙头按照我的意愿工作(左右水龙头跳跃页面,卷发工作正常。
答案 11 :(得分:0)
用于删除点击识别器的Swift 3扩展程序:
import UIKit
extension UIPageViewController {
func removeTapRecognizer() {
let gestureRecognizers = self.gestureRecognizers
var tapGesture: UIGestureRecognizer?
gestureRecognizers.forEach { recognizer in
if recognizer.isKind(of: UITapGestureRecognizer.self) {
tapGesture = recognizer
}
}
if let tapGesture = tapGesture {
self.view.removeGestureRecognizer(tapGesture)
}
}
}
答案 12 :(得分:0)
在我的情况下,我想禁用点击UIPageControl并让屏幕上的另一个按钮接收点击。滑动仍然有效。我尝试了很多方法,我相信这是最简单的工作解决方案:
for (UIPageControl *view in _pageController.view.subviews) {
if ([view isKindOfClass:[UIPageControl class]]) {
view.enabled = NO;
}
}
这是从UIPageController子视图获取UIPageControl视图并禁用用户交互。
答案 13 :(得分:0)
我在这里找到了一个简单的,全能的方式来响应UIPageViewController中我的子视图控制器上的点击。在我的RootViewController.swift
(与Xcode的“基于页面的应用程序”模板相同的结构)中,我的解决方案的核心(Swift 4,Xcode 9)就像这样简单了:
override func viewDidLoad() {
super.viewDidLoad()
...
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(pageTapped(sender:)))
self.pageViewController?.view.subviews[0].addGestureRecognizer(tapGesture)
}
...
@objc func pageTapped(sender: UITapGestureRecognizer) {
print("pageTapped")
}
(我还利用this answer让我跟踪实际点击的页面,即当前页面。)
答案 14 :(得分:0)
我制定了一个可行的解决方案。
将另一个UIGestureRecognizer添加到UIPageViewController并实现下面提供的委托方法。
在必须解决哪个手势应被锁定或进一步传递的每时每刻,都会调用此方法。请记住要提供对confictingView
的引用,在我的例子中是UITableView,它也可以识别平移手势。该视图放置在UIPageViewController内,因此可以两次或只是随机地识别一个摇动手势。现在,在此方法中,我检查平移手势是否同时在UITableView和UIPageViewController中,并确定UIPanGestureRecognizer是主要对象。
此方法不会直接覆盖任何其他手势识别器,因此我们不必担心提到的“ NSInvalidArgumentException”。
请记住,该模式实际上未得到Apple的认可:)
var conflictingView:UIView?
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
if otherGestureRecognizer.view === pageViewController?.view {
if let view = conflictingView {
var point = otherGestureRecognizer.location(in: self.view)
if view.frame.contains(point) {
print("Touch in conflicting view")
return false
}
}
print("Touch outside conficting view")
return true
}
print("Another view passed out")
return true
}