[英]What's the trick to pass an event to the next responder in the responder chain?
苹果真的很有趣。 我的意思是,他们说这有效:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch* touch = [touches anyObject];
NSUInteger numTaps = [touch tapCount];
if (numTaps < 2) {
[self.nextResponder touchesBegan:touches withEvent:event];
} else {
[self handleDoubleTap:touch];
}
}
我有一个视图控制器。 如您所知,视图控制器继承自 UIResponder。 该视图控制器创建一个继承自 UIView 的 MyView 对象,并将其作为子视图添加到它自己的视图中。
所以我们有:
视图控制器 > 有一个视图(自动) > 有一个 MyView(这是一个 UIView)。
现在在 MyView 里面,我把上面的代码和一个打印“touched MyView”的 NSLog 放在一起。 但是我将事件转发给下一个响应者,就像上面一样。 在 ViewController 中,我有另一个 touchesBegan 方法,它只是打印一个 NSLog 一个“触摸视图控制器”。
现在猜猜看:当我触摸 MyView 时,它会打印“touched MyView”。 当我触摸 MyView 外部时,即 VC 的视图,我得到一个“触摸视图控制器”。 所以两者都有效! 但不起作用的是转发事件。 因为现在,实际上下一个响应者应该是视图控制器,因为中间没有其他东西。 但是VC的事件处理方法在我转发的时候从来没有被调用过。
%$&!§!!
想法?
找出奇怪的东西MyView 的下一个响应者是视图控制器的视图。 这是有道理的,因为 MyView 是它的子视图。 但是我没有从视图控制器修改这个 UIView。 这没什么好习惯的。 它没有实现任何触摸事件处理。 消息不应该传递给视图控制器吗? 我怎么可能让它过去? 如果我删除 MyView 中的事件处理代码,那么事件就会很好地到达视图控制器。
根据一个类似的问题,你的方法应该有效。
这让我认为您的视图的nextResponder
实际上并不是您怀疑的ViewController
。
我会在您的转发代码中添加一个快速NSLog
来检查您的nextResponder
真正指向的内容:
if (numTaps < 2) {
NSLog(@"nextResponder = %@", self.nextResponder);
[self.nextResponder touchesBegan:touches withEvent:event];
}
您还可以更改其他 NSLog 消息,以便它们输出类型和地址信息:
NSLog(@"touched %@", self);
touched <UIView 0x12345678>
这应该让您开始诊断问题。
遇到了这个问题,因为我的自定义视图在视图层次结构中更深。 相反,我爬上了响应者链,直到它找到一个UIViewController
;
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
// Pass to top of chain
UIResponder *responder = self;
while (responder.nextResponder != nil){
responder = responder.nextResponder;
if ([responder isKindOfClass:[UIViewController class]]) {
// Got ViewController
break;
}
}
[responder touchesBegan:touches withEvent:event];
}
如果您查看UIResponder
的文档,说明默认情况下是这样的:
UIView 通过返回管理它的 UIViewController 对象(如果有)或它的超视图(如果没有)来实现这个方法;
如果您的视图没有返回视图控制器,那么它必须返回其超级视图(如您所见)。
UIApplication 实际上有一个方法可以准确地处理这个用例:
- (BOOL)sendAction:(SEL)action
to:(id)target
from:(id)sender
forEvent:(UIEvent *)event
由于您没有对目标的引用,您可以通过将目标值设置为nil
来在响应者链中向上工作,因为根据文档:
如果 target 为 nil,应用程序将消息发送给第一响应者,从那里它沿着响应者链向上推进,直到它被处理。
您当然可以使用其类方法[UIApplication sharedApplication]
访问[UIApplication sharedApplication]
更多信息: UIApplication 文档
这是额外的,但如果你绝对需要访问视图控制器来做一些更复杂的事情,你可以手动沿着响应者链向上工作,直到通过调用nextResponder
方法到达视图控制器,直到它返回一个视图控制器。 例如:
UIResponder *responder = self;
while ([responder isKindOfClass:[UIView class]])
responder = [responder nextResponder];
然后你可以把它作为你的视图控制器,然后继续做你想做的任何事情:
UIViewController *parentVC = (UIViewController *)responder
但请注意,这种破坏了 MVC 模式,并且可能意味着您正在以“hacky”方式做某事
要引用视图控制器,您需要使用
[[self nextResponder] nextResponder]
因为[self nextResponder]
表示视图控制器的视图,而[[self nextResponder] nextResponder]
表示视图控制器本身,现在您可以获取触摸事件
是的,它应该在普通情况下 100% 工作。 我刚刚尝试过测试项目,一切似乎都没问题。
我已经创建了基于视图的应用程序。 使用 Interface Builder 将新的 UIView 放到当前的 View 中。 然后使用 UIView 的子类创建新文件,并为我的新视图选择刚刚创建的类。 (Interface Builder->Class identity->Class->MyViewClass)
为 MyViewClass 和 UIViewController 添加触摸处理函数。
//我的视图类
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
NSLog(@"myview touches");
[self.nextResponder touchesBegan:touches withEvent:event];
}
//视图控制器
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
NSLog(@"controller touches");
}
按 MyViewClass 时,我会看到两个 NSLog。 在加载 ViewController 或使用 loadView 函数以编程方式设置视图时,您是否使用了 Interface Builder 和 XIB 文件?
今天我找到了一个更好的方法来解决这个问题或这样的问题。
在你的 UITableViewCell 中,你可以添加一个委托来监听触摸事件,如下所示:
@protocol imageViewTouchDelegate
-(void)selectedFacialView:(NSInteger)row item:(NSInteger)rowIndex;
@end
@property(nonatomic,assign)id<imageViewTouchDelegate>delegate;
然后在你的 UITableViewController: 你可以这样做:
@interface pressionTable : UITableViewController<facialViewDelegate>
在 .m 文件中,您可以实现此委托接口,例如:
-(void)selectedFacialView:(NSInteger)row item:(NSInteger)rowIndex{
//TO DO WHAT YOU WANT}
注意:当你初始化单元格时,你必须设置委托,否则委托无效,你可以这样做
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = @"Cell";
pressionCell *cell = (pressionCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[pressionCell alloc]initWithFrame:CGRectMake(0, 0, 240, 40)];
}
cell.delegate = self;
}
PS:我知道这个DOC是旧的,但我仍然添加我的方法来解决这个问题,所以当人们遇到同样的问题时,他们会从我的答案中得到帮助。
我知道这篇文章很旧,但我想分享一下,因为我有类似的经历。 对于自动创建的视图控制器的视图,我通过将 userInteractionEnabled 设置为 NO 来修复它。
用这个
[super.nextResponder touchesBegan:touches withEvent:event];
在您的代码中的这一行之前
[self.nextResponder touchesBegan:touches withEvent:event];
它的意思是
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch* touch = [touches anyObject];
NSUInteger numTaps = [touch tapCount];
if (numTaps < 2) {
[super.nextResponder touchesBegan:touches withEvent:event];
[self.nextResponder touchesBegan:touches withEvent:event];
} else {
[self handleDoubleTap:touch];
}
}
**Swift 4 版本 **
func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) {
let touch = touches?.first as? UITouch
let numTaps: Int? = touch?.tapCount
if (numTaps ?? 0) < 2 {
if let aTouches = touches as? Set<UITouch> {
super.next?.touchesBegan(aTouches, with: event)
next?.touchesBegan(aTouches, with: event)
}
} else {
handleDoubleTap(touch)
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.