UIScrollview 与 UIButtons - 如何重新创建跳板?

发布于 2024-07-15 07:38:19 字数 1433 浏览 9 评论 0原文

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

I'm trying to create a springboard-like interface within my app. I'm trying to use UIButtons added to a UIScrollView. The problem I'm running in to is with the buttons not passing any touches to the UIScrollView - if I try to flick/slide and happen to press on the button it doesn't register for the UIScrollView, but if I flick the space between buttons it will work. The buttons do click/work if I touch them.

Is there a property or setting that forces the button to send the touch events up to its parent (superview)? Do the buttons need to be added to something else before being added the UIScrollView?

Here is my code:

//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];

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

野心澎湃 2024-07-22 07:38:20

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

  1. UIScrollView 中的 canCancelContentTouches 设置为 YES
  2. 扩展 UIScrollView 以覆盖 touchesShouldCancelInContentView:(UIView *)view 以在 viewUIButton 时返回 YES

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

由于 UIButton 是一个 UIControl,因此需要扩展才能使 canCancelContentTouches 生效以启用滚动。

Solution that worked for me included:

  1. Setting canCancelContentTouches in UIScrollView to YES.
  2. Extending UIScrollView to override touchesShouldCancelInContentView:(UIView *)view to return YES when view is a UIButton.

According to documentation touchesShouldCancelInContentView returns "YES to cancel further touch messages to view, NO to have view continue to receive those messages. The default returned value is YES if view is not a UIControl object; otherwise, it returns NO."

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

や三分注定 2024-07-22 07:38:20

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

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

In order for UIScrollView to determine the difference between a click that passes through to its content view(s) and a touch that turns into a swipe or pinch, it needs to delay the touch and see if your finger has moved during that delay. By setting delaysContentTouches to NO in your above example, you're preventing this from happening. Therefore, the scroll view is always passing the touch to the button, instead of canceling it when it turns out that the user is performing a swipe gesture. Try setting delaysContentTouches to YES.

It might also be a good idea, structurally, to add all the views to be hosted in your scroll view to a common content view and only use that one view as the scroll view's subview.

送舟行 2024-07-22 07:38:20

我有一个类似的情况,UIScrollView 上有许多按钮,我想滚动这些按钮。 一开始,我将 UIScrollView 和 UIButton 都子类化。 但是,我注意到我的子类 UIScrollView 没有收到 TouchEnded 事件,因此我更改为仅子类 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

I have a similar case that a number of buttons on a UIScrollView, and I want to scroll these buttons. At the beginning, I subclassed both UIScrollView and UIButton. However, I noticed that my subclassed UIScrollView did not receive touchesEnded event, so I changed to only subclass 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
初相遇 2024-07-22 07:38:20

UIScrollView 本身处理很多事件。 您需要手动处理 UIScrollView 内按钮的 TouchDidEnd 和点击测试。

UIScrollView handles a lot of events itself. You need to handle touchesDidEnd and hit test for buttons inside the UIScrollView manually.

兰花执着 2024-07-22 07:38:20

好的,这就是您的答案:

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

  • 添加一个属性。其中一个 BOOL 类型
    (称为enableToRestore)
  • 添加一个属性。 CGPoint 类型之一
    (称为 startTouchPosition)
  • 在 awakeFromNib 中并且
    initWithFrame,设置
    将enableToRestore设置为isEnabled
    属性)
  • 覆盖“touchesBegan:withEvent:”
    存储触摸的开始
    位置。
  • 覆盖“touchesMoved:withEvent:”以
    检查是否有水平
    移动。
  • 如果是,则将启用设置为“否”并且
    选择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 毫秒),并且无法实现轻快的触摸。

OK here is your answer:

Subclass UIButton. (NOTE: call [super ....] at the start of each override.

  • Add a property. One of type BOOL
    (called enableToRestore)
  • Add a property. One of type CGPoint
    (called startTouchPosition)
  • in the awakeFromNib and
    initWithFrame, set the
    enableToRestore to the isEnabled
    property)
  • Override "touchesBegan: withEvent:"
    to store the start of the touch
    position.
  • Override "touchesMoved: withEvent:" to
    check to see if there is horizontal
    movement.
  • If YES, set enabled to NO and
    selected to NO.

Sample code:

- (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];
    } 
}

This will activate the UIScrollView.

Subclass UIScrollView. (NOTE: call [super ....] at the start of each override.

  • Override both "touchesEnded: withEvent:" and "touchesCancelled: withEvent:"
  • In the override, reset all subviews (and their subviews) enabled flag.
  • NOTE: Use a Category and add the method to 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];
}
  • In the Category:

.

-(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];
        }
    }
}

EDIT
Note: I never got Answer 3 to work.
Likewise: the setDelaysContentTouches:NO (set in the view controller or someplace) is to be set for best results on Answer 4. This provides very fast response to the buttons. Setting setDelaysContentTouches:YES puts a serious impact (150ms) on response time to the buttons and makes light,fast touching not possible.

国粹 2024-07-22 07:38:20

另一种方法是:
1。 用简单的自定义 UIView 替换按钮
2。 放置标志“userInterationEnable = yes;” 在 init 方法上
3. 在视图中重写 UIResponder 方法“touchesEnded”,您可以像按钮一样触发您需要的操作。

Another way is:
1. Substitute de button by a simple custom UIView
2. Put the flag "userInterationEnable = yes;" on the init method
3. In the view override the UIResponder method "touchesEnded" here you can trigger the action you need like a button.

盛夏尉蓝 2024-07-22 07:38:20

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

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

  1. 用简单的自定义 UIView 替换按钮
  2. 放置标志“userInterationEnable = yes;” 在 init 方法上
  3. 在视图中重写 UIResponder 方法“touchesEnded”
    您可以触发您的操作

第四。 将delaysContentTouches设置为YES

In my experience the first answer, i.e., simply setting delaysContentTouches to YES, does not change anything with regard to the problem. The buttons will still not deliver tracking results to the scroll view. The third answer is both simple and very usable. Thanks sieroaoj!

However, for the third answer to work you also need delaysContentTouches set to YES. Otherwise the method touchesEnded will also be called for tracking within the view. Therefore I could solve the problem by:

  1. Substitute de button by a simple custom UIView
  2. Put the flag "userInterationEnable = yes;" on the init method
  3. In the view override the UIResponder method "touchesEnded" here
    you can trigger the action you

Fourth. set delaysContentTouches to YES

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文