带有 UIButtons 的 UIScrollview - 如何重新创建跳板?

Posted

技术标签:

【中文标题】带有 UIButtons 的 UIScrollview - 如何重新创建跳板?【英文标题】:UIScrollview with UIButtons - how to recreate springboard? 【发布时间】:2009-03-16 13:33:29 【问题描述】:

我正在尝试在我的应用程序中创建一个类似跳板的界面。我正在尝试使用添加到 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];

【问题讨论】:

需要注意的是,尽管苹果之前已经允许了,10.4Apps that create alternate desktop/home screen environments or simulate multi-App widget experiences will be rejected。以防万一一些新人计划发布一个有自己跳板的生产应用程序。 【参考方案1】:

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

    UIScrollView 中的canCancelContentTouches 设置为YES。 当viewUIButton 时,扩展UIScrollView 以覆盖touchesShouldCancelInContentView:(UIView *)view 以返回YES

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

由于UIButtonUIControl,因此需要扩展以使canCancelContentTouches 生效,从而启用滚动。

【讨论】:

非常感谢!澄清一下,这在 delaysContentTouches = NO 时效果最好。 这是要走的路! (至少对于 ios 4+)我发现像这样覆盖它是最通用的:- (BOOL)touchesShouldCancelInContentView:(UIView *)view return ![view isKindOfClass:[UISlider class]]; 它仍然对我不起作用。 touchesShouldCancelInContentView 永远不会被调用,除非我将延迟设置为 NO,但在后一种情况下,根本没有滚动。 @Michael 是的,当然它不起作用,作者没有费心阅读文档BOOL delaysContentTouches; // default is YES. if NO, we immediately call -touchesShouldBegin:withEvent:inContentView:,所以很多支持者不知道它的作用。【参考方案2】:

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

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

【讨论】:

使用 delaysContentTouches 的一个可能问题是,它会在按钮突出显示之前添加延迟。下面 Roman K 的回答将让您没有延迟,但在按下按钮后仍然可以滚动。 结构建议很棒,但我想知道为什么。谢谢。【参考方案3】:

我有一个类似的情况,即 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

【讨论】:

【参考方案4】:

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

【讨论】:

【参考方案5】:

好的,这是你的答案:

子类 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:” 在覆盖中,重置所有子视图(及其子视图)启用标志。 注意:使用 Category 并将方法添加到 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 毫秒),并且无法实现轻快的触摸。

【讨论】:

【参考方案6】:

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

【讨论】:

【参考方案7】:

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

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

    用简单的自定义 UIView 替换 de 按钮 设置标志“userInterationEnable = yes;”在init方法上 在视图中覆盖 UIResponder 方法“touchesEnded” 你可以触发你的动作

第四。将delaysContentTouches 设置为YES

【讨论】:

delaysContentTouches = YES 默认情况下。

以上是关于带有 UIButtons 的 UIScrollview - 如何重新创建跳板?的主要内容,如果未能解决你的问题,请参考以下文章

带有 UIButtons 的 UIScrollview - 如何重新创建跳板?

在单击或旋转之前未显示带有 UIButtons 的 UIView

带有文本快捷方式的自定义 UIToolbar 的行为与带有 UIButtons 和 UITableViewControllers 的键盘完全不同

带有 UIButtons 的 UIScrollView - 当按钮开始触摸时,滚动视图不会滚动

UITableViewCell 中的 UIButtons 不显示 textLabel 更改,直到单元格滚出屏幕

管理多个按下的 UIButtons