【问题标题】:How to add UIViewController as target of UIButton action created in programmatically created UIView?如何将 UIViewController 添加为在以编程方式创建的 UIView 中创建的 UIButton 操作的目标?
【发布时间】:2013-12-11 22:06:21
【问题描述】:

我以编程方式创建了一个UIView 并添加了一个UIButton 作为它的子视图。
我希望 UIViewController 成为该按钮操作的目标。
我该怎么做?
如果它是由 Interface Builder 创建的,那么使用 IBAction 很容易。

【问题讨论】:

  • 代码,显示一些代码。让 stackoverflowers 更容易为您提供帮助

标签: ios iphone objective-c uiview uibutton


【解决方案1】:

如果您以编程方式将按钮添加到 UIView 的子类,则可以通过以下两种方式之一进行:

  1. 你可以让按钮成为视图的属性,然后在实例化视图的viewController中你可以设置按钮的目标如下:

    [viewSubclass.buttonName addTarget:self action:@selector(buttonTapped:) forControlEvents:UIControlEventTouchUpInside];
    

    这会将按钮的目标设置为 buttonTapped: 在 vi​​ewController.m 中的方法

  2. 您可以在子视图中创建一个协议,父视图控制器将遵守该协议。在您的视图中,当您添加按钮时,将其设置为调用视图中的方法。然后从该视图调用委托方法,以便您的 viewController 可以响应它:

在您的视图子类 .h 顶部创建协议:

@protocol ButtonProtocolName

- (void)buttonWasPressed;

@end

为委托创建一个属性:

@property (nonatomic, assign) id <ButtonProtocolName> delegate;

在子类 .m 中设置您的按钮选择器:

[button addTarget:self action:@selector(buttonTapped:) forControlEvents:UIControlEventTouchUpInside];

在buttonTapped:方法中调用委托方法:

- (void)buttonTapped:(id)sender {
    [self.delegate buttonWasPressed];
}

在您的 viewController.h 中,您需要确保它符合协议:

@interface someViewController : UIViewController <SomeButtonProtocolName>

在您的 viewController.m 中,当您初始化子视图时,您必须设置委托:

SomeView *view = ... // Init your view
// Set the delegate
view.delegate = self;

最后,将委托方法buttonWasPressed添加到viewController.m:

- (void)buttonWasPressed {
    // Put code here for button's intended action.
}

更新以提供 Swift 示例

// Simple delegate protocol.
protocol SomeViewDelegate: class {
  // Method used to tell the delegate that the button was pressed in the subview.
  // You can add parameters here as you like.
  func buttonWasPressed()
}

class SomeView: UIView {
  // Define the view's delegate.
  weak var delegate: SomeViewDelegate?

  // Assuming you already have a button.
  var button: UIButton!

  // Once your view & button has been initialized, configure the button's target.
  func configureButton() {
    // Set your target
    self.button.addTarget(self, action: #selector(someButtonPressed(_:)), for: .touchUpInside)
  }

  @objc func someButtonPressed(_ sender: UIButton) {
    delegate?.buttonWasPressed()
  }
}

// Conform to the delegate protocol
class SomeViewController: UIViewController, SomeViewDelegate {
  var someView: SomeView!

  func buttonWasPressed() {
    // UIViewController can handle SomeView's button press.
  }
}

此外,这是一个使用闭包而不是委托的快速示例。 (这也可以使用块在 ObjC 中实现。)

// Use typeAlias to define closure
typealias ButtonPressedHandler = () -> Void

class SomeView: UIView {
  // Define the view's delegate.
  var pressedHandler: ButtonPressedHandler?

  // Assuming you already have a button.
  var button: UIButton!

  // Once your view & button has been initialized, configure the button's target.
  func configureButton() {
    // Set your target
    self.button.addTarget(self, action: #selector(someButtonPressed(_:)), for: .touchUpInside)
  }

  @objc func someButtonPressed(_ sender: UIButton) {
    pressedHandler?()
  }
}

class SomeViewController: UIViewController {
  var someView: SomeView!

  // Set the closure in the ViewController
  func configureButtonHandling() {
    someView.pressedHandler = {
      // UIViewController can handle SomeView's button press.
    }
  }
}

【讨论】:

  • 感谢您的回答,我应该如何将 UITextField 委托设置为 UIViewController?
  • 如果视图中有一个 UITextField,那么您可以按照我上面说明的方式进行操作。但是,如果您使用选项 2,则必须将视图子类设置为文本字段委托,然后使用视图的委托将更改传达给 viewController。在 textField 的情况下,使用选项 1 会更容易。
  • 这比它需要的复杂得多。在此处查看我对类似问题的回答:stackoverflow.com/questions/15361391/…
  • @leftspin,IMO,您的链接应该被视为一种不好的做法,因为它没有明确说明预期的行为应该是什么。当然,它减少了一些样板代码,但模糊了实现。
  • @AronCrittendon 我的回答是简单地使用责任链模式(en.wikipedia.org/wiki/Chain-of-responsibility_pattern),它“提倡松散耦合的思想,这被认为是一种编程最佳实践”,并且是核心思想在响应者链后面。它不会比使用 NSNotifications、委托,甚至(在某种意义上)方法重载更模糊实现;所有这些都是促进松散耦合的间接技术。要进行良好的讨论,请参阅cocoanetics.com/2012/09/the-amazing-responder-chain
【解决方案2】:

您可以为按钮添加目标和操作。

[button addTarget:controller/self action:@selector(onTapButton) forControlEvents:UIControlEventTouchUpInside];

目标是控制器,所以如果你想在当前控制器中处理触摸事件,请使用 self.否则,您应该有一个指向控制器 obj 的指针/引用,并使用该引用而不是 self. onTapButton 是当用户点击按钮时将调用的选择器。 onTapButton不带任何参数,如果要带参数使用onTapButton:

- (IBAction/void)onTapButton{
}
-(IBAction/void)onTapButton:(id)sender{
}

注意: 处理这个问题的更好方法是使用委托模式,在 self 中有目标是什么类,然后调用委托,控制器应该实现该委托。保持对控制器的直接引用不是好的做法。

【讨论】:

  • 这行不通。 OP 在视图的子类中添加按钮,因此 self 将是视图,而不是视图控制器。
  • 在这种情况下,他应该引用控制器并将 self 替换为视图控制器。
  • C_X:我知道你做出这个回应已经有几个月了,但我想指出你的第二条评论实际上违反了 MVC。视图本身不应代表 viewController,因此在视图子类中使用对 viewController 的引用是一个坏主意。最佳实践是实现一个委托,以便 viewController 在调用某个方法时可以为视图执行操作。 (接受的答案有一个例子。)
【解决方案3】:

我们也可以在没有委托的情况下实现这一点。

在您的CustomUIView.m 类中,实现以下方法:-

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];

    NSArray *viewsInNib = [[NSBundle mainBundle]loadNibNamed:@"AddressAlertView" owner:self options:nil];
    for (id view in viewsInNib) {
        if ([view isKindOfClass:[self class]]) {
            self = view;
            break;
        }
    }

    return self;
}

说明:- 在 initWithFrame 方法中,我正在加载当前的笔尖。加载 nib 意味着初始化它包含的所有子视图。获取同一类的完全初始化视图并分配给自己。返回包含完全初始化的 uiview 的 self。

在您的viewcontroller.m 文件中,编写以下代码以添加自定义uiview 并设置按钮目标:-

UIWindow *mainWindow = [[[UIApplication sharedApplication]delegate]window];

AddressAlertView *objAddressAlertView = [[AddressAlertView alloc] initWithFrame:mainWindow.bounds];
[objAddressAlertView.btnCross addTarget:self
                                 action:@selector(dummy)
                       forControlEvents:UIControlEventTouchUpInside];
if (objAddressAlertView)
    [mainWindow addSubview:objAddressAlertView];

【讨论】:

    【解决方案4】:
    [buttonName addTarget:self action:@selector(buttonPressed:) forControlEvents:UIControlEventTouchUpInside];
    

    现在,如果你使用 UINavigationController 你的 buttonPressed 函数将是

    - (void)buttonPressed:(id)sender
    {
        NewViewController *newVC = [NewViewController new];
        [self.navigationController pushViewController:newVC animated:YES];
    
    }
    

    如果你不使用导航控制器而不是

    - (void)buttonPressed:(id)sender
    {
        NewViewController *newVC = [NewViewController new];
        [self presentViewController:newVC animated:YES completion:nil];
    
    }
    

    【讨论】:

      【解决方案5】:

      对于 Swift 4

      class MyView: UIView {
      
       weak var delegate: MyBtnDelegate?
      
       var myBtn: UIButton = {
          let myCustomButton = UIButton()
      
          // Button UI code goes here
      
          myCustomButton.addTarget(self, action: #selector(saveData), for: .touchUpInside)
          return myCustomButton
      }()
      
      
      @objc func saveData() {
      
      delegate?.doSomething()
      
      }
      

      在视图控制器中

      protocol MyBtnDelegate: class {
      func doSomething()
      }
      
      class ViewController: UIViewController, MyBtnDelegate {
      var customView = MyView()
      
       override func viewDidLoad() {
          super.viewDidLoad()
          customView.delegate = self  // Unless you add this line code won't be working
      }
      
      func doSomething() {
      
      //Do whatever you want
      
        }
      }
      

      希望这会有所帮助。快乐编码:]

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-06-08
        • 1970-01-01
        • 2021-10-21
        • 1970-01-01
        • 2016-03-06
        • 2012-05-22
        相关资源
        最近更新 更多