如果将一个UIView添加为子视图,则UIButton的目标不起作用

25

我有一个UIButton。它有两个子视图。然后我调用:

[createButton addTarget:self action:@selector(openComposer) forControlEvents:UIControlEventTouchUpInside];

如果我点击UIButton的未被其子视图覆盖的部分,就能正常工作。但是,如果我点击其中一个子视图,则不会触发动作。

在这两个子视图上,我都设置了.userInteractionEnabledYES

这两个子视图都是普通的UIViews,具有framebackgroundColor

如何让点击事件“穿透”UIView并传递到UIButton

谢谢。

编辑:我需要使用UIControlEvents,因为我需要UIControlEventTouchDown。

编辑2:

这是按钮的代码。

@interface CreateButton ()

@property (nonatomic) Theme *theme;
@property (nonatomic) UIView *verticle;
@property (nonatomic) UIView *horizontal;
@property (nonatomic) BOOL mini;

@end

@implementation CreateButton

#pragma mark - Accessors

- (UIView *)verticle {
    if (! _verticle) {
        _verticle = [[UIView alloc] init];
        _verticle.backgroundColor = [self.theme colorForKey:@"createButtonPlusBackgroundColor"];
        _verticle.userInteractionEnabled = NO;
    }
    return _verticle;
}

- (UIView *)horizontal {
    if (! _horizontal) {
        _horizontal = [[UIView alloc] init];
        _horizontal.backgroundColor = [self.theme colorForKey:@"createButtonPlusBackgroundColor"];
        _verticle.userInteractionEnabled = NO;
    }
    return _horizontal;
}

#pragma mark - Init

- (instancetype)initWithTheme:(Theme *)theme frame:(CGRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
        _theme = theme;

        self.layer.cornerRadius = roundf(frame.size.width / 2);
        self.layer.shadowColor = [UIColor blackColor].CGColor;
        self.layer.shadowOpacity = .1f;
        self.layer.shadowOffset = CGSizeZero;
        self.layer.shadowRadius = 15.f;
        self.backgroundColor = [self.theme colorForKey:@"createButtonBackgroundColor"];

        [self addSubview:self.verticle];
        [self addSubview:self.horizontal];

        [self addTarget:self action:@selector(animate) forControlEvents:UIControlEventTouchDown];
        [self addTarget:self action:@selector(animate) forControlEvents:UIControlEventTouchUpInside];
        [self addTarget:self action:@selector(animate) forControlEvents:UIControlEventTouchUpOutside];
    }
    return self;
}

#pragma mark - Actions

- (void)animate {
    [UIView animateWithDuration:0.2 delay:0 usingSpringWithDamping:0.4 initialSpringVelocity:8 options:kNilOptions animations:^{
        if (self.mini) {
            self.transform = CGAffineTransformMakeScale(1.0, 1.0);
            self.mini = NO;
        } else {
            self.transform = CGAffineTransformMakeScale(0.90, 0.90);
            self.mini = YES;
        }
    } completion:nil];
}

#pragma mark - UIView

- (void)layoutSubviews {
    CGSize size = self.bounds.size;

    NSInteger width = 3;
    NSInteger verticleInset = 12;
    self.verticle.frame = CGRectMake((size.width - width) / 2, verticleInset, width, size.height - (verticleInset * 2));
    self.horizontal.frame = CGRectMake(verticleInset, (size.height - width) / 2, size.width - (verticleInset * 2), width);
}

@end

这是因为您没有为子视图添加触摸事件。这难道很难吗? - Zane Helton
我无法使用UIGestureRecognizer,因为我需要触发ControlEventTouchDown事件。 - Jason Silberman
子视图是在按钮框架内还是外部?因为如果它们在外部,它们将正常显示,但不在按钮的活动区域内。 - galrito
而且你没有处理任何这些子视图上的触摸事件? - galrito
目前不行。@galrito - Jason Silberman
你能展示一下创建按钮及其子视图的代码吗? - galrito
5个回答

44

将您的子视图的 userInteractionEnabled 设置为NO,以便触摸事件可以穿过它们并传递到您的按钮。

另外,请确保在您 horizontal 属性的懒加载器中更改_verticle.userInteractionEnabled = NO;_horizontal.userInteractionEnabled = NO; ,因为我认为这是一个打字错误。


3
请使用以下代码(将“ViewControllerFilterCategory”替换为您自己的代码 + nibName)将控制器添加为当前控制器的子控制器,然后再将视图添加为子视图。这样iOS系统会按照您的期望调用附加到子控制器的操作:
    let vcCats = ViewControllerFilterCategory(nibName: "ViewControllerFilterCategory", bundle: Bundle.main);
    self.addChildViewController(vcCats);
    self.view.addSubview(vcCats.view);

P.S. 在删除子视图之前,请不要忘记调用“removeFromParentViewController”(如下所示):

@IBAction func onCanceled(_ sender: Any) {
    self.removeFromParentViewController()
    self.view.removeFromSuperview()
}

需要添加 self.addChildViewController(vcCats);. - saturngod

1

您需要将isUserInteractionEnabled设置为true,以便为要添加手势目标的视图。

另外,根据苹果UIView.animate函数的文档,当动画正在进行时,用户交互被禁用。

“在动画期间,正在进行动画的视图的用户交互被暂时禁用。(在iOS 5之前,整个应用程序的用户交互都被禁用。)”

请查看他们的文档

因此,解决方案是在DispatchQueue.main.asyncAfter内运行任何动画。

如果您从那里启动另一个动画,请在UIView.animate的完成块内使用此调用。

例如

UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 0.5, initialSpringVelocity: 0.7, options: [.curveEaseInOut, .allowUserInteraction], animations: {
                topConstraint.constant = 0
                windowView.layoutIfNeeded()
                oldNotificationView?.removeFromSuperview()
            }, completion: { result in
                DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
                    //Its important to run below animation in asyncAfter as otherwise setting delay for 4 seconds seems to change frame such that tap gesture or touchesBegan is not sent for the view. So execting after delay using dispatch queue is right way for touch events to be fired instead of using animate functions delay.
                    UIView.animate(withDuration: 0.5, delay:0
                        , options: [.curveEaseOut , .allowUserInteraction], animations: {
                        topConstraint.constant = -height
                        windowView.layoutIfNeeded()
                    } , completion: { result in
                        notificationView.removeFromSuperview()
                        self.inAppNotificationView = nil
                    })
                }
            })

UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 0.5, initialSpringVelocity: 0.7, options: [.curveEaseInOut, .allowUserInteraction], animations: {
            topConstraint.constant = 0
            windowView.layoutIfNeeded()
            oldNotificationView?.removeFromSuperview()
        }, completion: { result in
            DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
                //Its important to run below animation in asyncAfter as otherwise setting delay for 4 seconds seems to change frame such that tap gesture or touchesBegan is not sent for the view. So execting after delay using dispatch queue is right way for touch events to be fired instead of using animate functions delay.
                UIView.animate(withDuration: 0.5, delay:0
                    , options: [.curveEaseOut , .allowUserInteraction], animations: {
                    topConstraint.constant = -height
                    windowView.layoutIfNeeded()
                } , completion: { result in
                    notificationView.removeFromSuperview()
                    self.inAppNotificationView = nil
                })
            }
        })

0

我遇到了这个问题。

在我的情况下,我将nib作为子视图加载。

UIViewController *vc = [[[NSClassFromString(@"nib_controller") alloc] initWithNibName:@"nib_name" bundle:nil] autorelease];
[self.view addSubview:vc.view];

在nib视图(vc.view)中,所有按钮都没有调用动作。

[(UIButton*)[self.view viewWithTag:button_tag] addTarget:self action:@selector(btnActions:) forControlEvents:UIControlEventTouchUpInside];


-(IBAction)btnActions:(id)sender{

    // list star buttons for bookmark
    NSLog(@"btnActions tag %d", [sender tag]);

    // bla bla bla
}

我能够触摸按钮,但是方法btnActions没有被触发。

然后,我尝试了一些非常简单和逻辑的东西。

在父视图控制器上,我有一个同名的方法:

-(IBAction)btnActions:(id)sender{

    // list star buttons for bookmark
    NSLog(@"parent btnActions tag %d", [sender tag]);

    // bla bla bla
}

我刚刚在里面添加了NSLog以查看发生了什么。结果出现在日志控制台上:

parent btnActions tag xxx

在 iOS 9 之前的版本中,这是不可能的。

但现在,如果视图从 nib 控制器视图加载,则可以访问父视图中的方法。

对于仍然感到困惑的人,请参见下面的示例:

父视图 -> A
子视图 > B

----------
|        | 
|   A    |
|        |
----------
    |
    |--- -(IBAction)btnActions:(id)sender


----------
|        | 
|   B    |
|        |
----------
    |
    |--- -(IBAction)btnActions:(id)sender (the controller have an method with same name as parent)

B.view 是从 A 控制器中添加的

----------
|        | 
|   A    |
|        |
----------
    |
    |--- (touching some button -> execute this:

UIViewController *B = [[[NSClassFromString(@"B_controller") alloc] initWithNibName:@"B_name" bundle:nil] autorelease];
[self.view addSubview:B.view]; // The B.view is added as subview.

B.view内的按钮发送动作到-(IBAction)btnActions

示例

[(UIButton*)[self.view viewWithTag:button_tag] addTarget:self action:@selector(btnActions:) forControlEvents:UIControlEventTouchUpInside];

所有按钮似乎都能正常工作,因为它们都可以在Xcode模拟器中被触摸或点击。但是在B.controller中声明的-(IBAction)btnActions方法没有被调用。我陷入了解决这个问题的困境中很多小时,直到我意识到操作被发送到了如上所述的A.Controller。

头疼的是,在iOS早期版本中,这是不可能的。

我不确定,但我认为从iOS9开始,我们有了这种不同的行为。

该应用程序在iOS8之前一直运行良好。


0
在我的情况下,有一个包含下面按钮的.xib文件,我将这个.xib添加到了我的ViewController中。由于在ViewController中设置了.xib文件的约束条件,@IBActions无法正常工作。

enter image description here


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接