如果将UIView添加为子视图,则UIButton目标不工作。

问题描述 投票:22回答:4

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

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

如果我点选其中的部分 UIButton 但其中一个子视图没有被覆盖,它可以正常工作。然而,如果我点击其中一个子视图,它不会触发动作。

在这两个子视图上,我有 .userInteractionEnabled 设为 YES.

两个子视图都是普通的 UIViews设有 framebackgroundColor.

怎样才能让水龙头 "通过 "这个...?UIView 并到 UIButton?

谢谢你

EDIT.我需要使用UIControlEvents,因为我需要UIControlEventTouchDown。 我需要使用UIControlEvents 因为我需要UIControlEventTouchDown。

EDIT 2.我需要使用UIControlEvents,因为我需要UIControlEventTouchDown。

这是按钮的代码

@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
ios objective-c uiview uibutton
4个回答
35
投票

设置 userInteractionEnabledNO 为你的子视图,让触摸通过他们,并到你的按钮。

另外,请确保更改 _verticle.userInteractionEnabled = NO; 在你的懒惰加载器中,为你的 horizontal 属性为 _horizontal.userInteractionEnabled = NO; 因为我相信这是一个错别字。


3
投票

请使用下面的代码(将'ViewControllerFilterCategory'替换为你自己的+nibName),将nib中的控制器(子控制器)添加为当前控制器的子控制器,然后再将视图添加为子视图。然后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()
}

1
投票

你必须为你试图添加手势目标的视图设置isUserInteractionEnabled为true。

另外,根据苹果UIView.animate函数的文档,当动画开启时,用户交互是被禁用的。

"在动画期间,对于被动画化的视图,用户交互会被暂时禁用。在iOS 5之前,整个应用程序的用户交互都会被禁用)"

查看他们的文档

所以解决方案是在DispatchQueue.main.asyncAfter里面运行任何动画。

如果你要从UIView.animate中启动另一个动画,也可以在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.视图是由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 simulator)。但是 -(IBAction)btnActions 方法申报到 B.controller 没有被调用。我在解决这个问题上坚持了很多小时,直到我意识到动作被发送到了我的电脑上。A.Controller,如上所述。

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

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

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


0
投票

在我的案例中,下面的按钮有一个.xib文件,我把这个.xib添加到我的ViewController中。由于为ViewController中的.xib文件设置了约束条件,@IBActions拒绝工作。

enter image description here

© www.soinside.com 2019 - 2024. All rights reserved.