34

我正在尝试在我的应用程序中创建一个类似跳板的界面。我正在尝试使用添加到 UIScrollView 的 UIButtons。我遇到的问题是按钮没有将任何触摸传递给 UIScrollView - 如果我尝试轻弹/滑动并碰巧按下按钮,它不会注册 UIScrollView,但是如果我轻弹之间的空间按钮它将起作用。如果我触摸它们,按钮会单击/工作。

是否存在强制按钮将触摸事件发送到其父级(超级视图)的属性或设置?在添加 UIScrollView 之前,是否需要将按钮添加到其他内容?

这是我的代码:

//init scrolling area
UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, 480, 480)];
scrollView.contentSize = CGSizeMake(480, 1000);
scrollView.bounces = NO;
scrollView.delaysContentTouches = NO;

//create background image
UIImageView *rowsBackground = [[UIImageView alloc] initWithImage:[self scaleAndRotateImage:[UIImage imageNamed:@"mylongbackground.png"]]];
rowsBackground.userInteractionEnabled = YES;

//create button
UIButton *btn = [[UIButton buttonWithType:UIButtonTypeCustom] retain];
btn.frame = CGRectMake(100, 850, 150, 150);
btn.bounds = CGRectMake(0, 0, 150.0, 150.0);
[btn setImage:[self scaleAndRotateImage:[UIImage imageNamed:@"basicbutton.png"]] forState:UIControlStateNormal];
[btn addTarget:self action:@selector(buttonClick) forControlEvents:UIControlEventTouchUpInside];

//add "stuff" to scrolling area
[scrollView addSubview:rowsBackground];
[scrollView addSubview:btn];

//add scrolling area to cocos2d
//this is just a UIWindow
[[[Director sharedDirector] openGLView] addSubview:scrollView];

//mem-mgmt
[rowsBackground release];
[btn release];
[scrollView release];
4

7 回答 7

44

对我有用的解决方案包括:

  1. 设置canCancelContentTouches为。UIScrollView_YES
  2. 扩展UIScrollView覆盖touchesShouldCancelInContentView:(UIView *)view以返回YESwhenview是一个UIButton.

根据文档touchesShouldCancelInContentView返回“YES取消更多触摸消息以查看,NO让视图继续接收这些消息。默认返回值是YES如果视图不是UIControl对象;否则,它返回NO。”

Since UIButton is a UIControl the extension is necessary to get canCancelContentTouches to take effect which enables scrolling.

于 2010-08-04T13:44:20.380 回答
28

为了UIScrollView确定通过其内容视图的点击与变成滑动或捏合的触摸之间的区别,它需要延迟触摸并查看您的手指是否在该延迟期间移动。通过在上面的示例中设置delaysContentTouchesNO,您可以防止这种情况发生。因此,滚动视图总是将触摸传递给按钮,而不是在用户执行滑动手势时取消它。尝试设置delaysContentTouchesYES.

从结构上讲,将要在滚动视图中托管的所有视图添加到公共内容视图并仅使用该视图作为滚动视图的子视图也可能是一个好主意。

于 2009-03-18T15:56:18.410 回答
5

我有一个类似的情况,即 UIScrollView 上有许多按钮,我想滚动这些按钮。一开始,我继承了 UIScrollView 和 UIButton。但是,我注意到我的子类 UIScrollView 没有收到 touchesEnded 事件,所以我改为仅子类 UIButton。


@interface MyPhotoButton : UIButton {
}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event;
@end

@implementation MyPhotoButton

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
    [super touchesMoved:touches withEvent:event];
    [self setEnabled:NO];
    [self setSelected:NO];
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
    [super touchesEnded:touches withEvent:event];
    [self setEnabled:YES];
}

- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
    [super touchesCancelled:touches withEvent:event];
    [self setEnabled:YES];
}

@end
于 2010-04-27T09:11:58.950 回答
1

UIScrollView 自己处理很多事件。您需要手动处理 touchesDidEnd 并点击测试 UIScrollView 内的按钮。

于 2009-03-16T14:15:38.207 回答
1

好的,这是你的答案:

子类 UIButton。(注意:在每次覆盖开始时调用 [super ....]。

  • 添加属性。BOOL 类型之一(称为 enableToRestore)
  • 添加属性。CGPoint 类型之一(称为 startTouchPosition)
  • 在 awakeFromNib 和 initWithFrame 中,将 enableToRestore 设置为 isEnabled 属性)
  • 覆盖“touchesBegan:withEvent:”以存储触摸位置的开始。
  • 覆盖 "touchesMoved: withEvent:" 以检查是否有水平移动。
  • 如果是,则将启用设置为 NO 并选择为 NO。

示例代码:

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event 
{
    UITouch *touch = [touches anyObject];

    [super touchesBegan:touches withEvent:event];
    [self setStartTouchPosition:[touch locationInView:self]];
}


//
// Helper Function
//
- (BOOL)isTouchMovingHorizontally:(UITouch *)touch 
{
    CGPoint currentTouchPosition = [touch locationInView:self];
    BOOL      rValue = NO;

    if (fabsf([self startTouchPosition].x - currentTouchPosition.x) >= 2.0) 
    {
        rValue = YES;
    }

    return (rValue);
}

//
// This is called when the finger is moved.  If the result is a left or right
// movement, the button will disable resulting in the UIScrollView being the
// next responder.  The parrent ScrollView will then re-enable the button
// when the finger event is ended of cancelled.
//
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event 
{
    [super touchesMoved:touches withEvent:event];
    if ([self isTouchMovingHorizontally:[touches anyObject]]) 
    {
        [self setEnabled:NO];
        [self setSelected:NO];
    } 
}

这将激活 UIScrollView。

子类 UIScrollView。(注意:在每次覆盖开始时调用 [super ....]。

  • 覆盖 "touchesEnded: withEvent:" 和 "touchesCancelled: withEvent:"
  • 在覆盖中,重置所有子视图(及其子视图)启用标志。
  • 注意:使用类别并将方法添加到 UIView:

.

- (void) restoreAllEnables
{
    NSArray   *views = [self subviews];

    for (UIView *aView in views)
    {
        if ([aView respondsToSelector:@selector(restoreEnable)])
        {
            [aView restoreEnable];
        }
    }
}

- (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
    [super touchesEnded:touches withEvent:event];
    [self restoreAllEnables];
}

- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
    [super touchesEnded:touches withEvent:event];
    [self restoreAllEnables];
}
  • 在类别中:

.

-(void) restoreEnable
{
    NSArray   *views = [self subviews];

    if ([self respondsToSelector:@selector(enableToRestore)])
    {
        [self setEnabled:[self enableToRestore]];
    }

    for (UIView *aView in views)
    {
        if ([aView respondsToSelector:@selector(restoreEnable)])
        {
            [aView restoreEnable];
        }
    }
}

编辑注意:我从来没有得到答案 3 的工作。同样: setDelaysContentTouches:NO (在视图控制器或某个地方设置)将设置为答案 4 的最佳结果。这提供了对按钮的非常快速的响应。设置 setDelaysContentTouches:YES 会对按钮的响应时间产生严重影响(150 毫秒),并使轻快的触摸变得不可能。

于 2010-02-26T02:55:46.600 回答
0

另一种方法是:
1. 用简单的自定义 UIView 替换 de 按钮
2. 放置标志“userInterationEnable = yes;” 关于 init 方法
3. 在视图中覆盖 UIResponder 方法“touchesEnded”,您可以在此处触发您需要的操作,如按钮。

于 2009-05-30T17:55:50.750 回答
0

以我的经验,第一个答案,即简单地设置delaysContentTouchesYES,并不会改变与问题有关的任何内容。这些按钮仍然不会将跟踪结果传递到滚动视图。第三个答案既简单又非常有用。谢谢西罗奥吉!

但是,要使第三个答案起作用,您还需要 delaysContentTouches设置为YES. 否则,该方法touchesEnded也将被调用以在视图中进行跟踪。因此我可以通过以下方式解决问题:

  1. 用一个简单的自定义 UIView 替换 de 按钮
  2. 把标志“userInterationEnable = yes;” 在初始化方法上
  3. 在视图中重写 UIResponder 方法“touchesEnded”在这里你可以触发你的动作

第四。设置delaysContentTouchesYES

于 2009-11-11T09:12:48.783 回答