I created a UIView
programmatically and added a UIButton
as it's subview.
I want a UIViewController
to be the target of that button action.
How would I do that?
If it was created by Interface Builder then it was easy by using IBAction
.
问题:
回答1:
If you are adding the button programmatically to a subclass of UIView, then you can do it one of two ways:
You can make the button a property of the view, and then in the viewController that instantiates the view you can set the target of the button as follows:
[viewSubclass.buttonName addTarget:self action:@selector(buttonTapped:) forControlEvents:UIControlEventTouchUpInside];
This will set the button's target to a method of buttonTapped: in the viewController.m
You can create a protocol in your subview, which the parent viewController will conform to. In your view, when you add your button set it to call a method in your view. Then call the delegate method from that view so that your viewController can respond to it:
In the top your view subclass .h create the protocol:
@protocol ButtonProtocolName
- (void)buttonWasPressed;
@end
Create a property for the delegate:
@property (nonatomic, assign) id <ButtonProtocolName> delegate;
In the subclass .m set your button selector:
[button addTarget:self action:@selector(buttonTapped:) forControlEvents:UIControlEventTouchUpInside];
In the buttonTapped: method call the delegate method:
- (void)buttonTapped:(id)sender {
[self.delegate buttonWasPressed];
}
In your viewController.h you'll need to make sure it conforms to the protocol:
@interface someViewController : UIViewController <SomeButtonProtocolName>
In your viewController.m when you init your subview, you'll have to set the delegate:
SomeView *view = ... // Init your view
// Set the delegate
view.delegate = self;
Finally, add the delegate method buttonWasPressed to the viewController.m:
- (void)buttonWasPressed {
// Put code here for button's intended action.
}
Updated to provide Swift example
// 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.
}
}
Additionally, here is a quick example using a closure instead of a delegate. (This can approach also be implemented in ObjC using blocks.)
// 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.
}
}
}
回答2:
You can add target and action to button.
[button addTarget:controller/self action:@selector(onTapButton) forControlEvents:UIControlEventTouchUpInside];
Target is controller so If you want to handle touch event in current controller use self. Other wise you should have a pointer/reference to the controller obj, and use that reference instead of the self.
onTapButton
is selector which will be called when user tap on the button. onTapButton
do not taking any parameter, If you want to use with parameter use onTapButton:
- (IBAction/void)onTapButton{
}
-(IBAction/void)onTapButton:(id)sender{
}
NOTE: Better way is to handle this is to use delegation pattern, have target in self what ever class that is, and After that call delegate, and controller should implement that delegate. Keeping direct reference to the controller is not good practice.
回答3:
We can achieve this without delegates as well.
In your CustomUIView.m
class, implement the following method:-
- (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;
}
Explanation:- In initWithFrame method, I am loading the current nib. Loading the nib means initialising all the sub view which it contains. Get the fully initilised view of same class and assigned to self. Return self which contains fully initilised uiview.
In your viewcontroller.m
file, write the below code to add custom uiview and set target of button:-
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];
Now, if you are using UINavigationController your buttonPressed function will be
- (void)buttonPressed:(id)sender
{
NewViewController *newVC = [NewViewController new];
[self.navigationController pushViewController:newVC animated:YES];
}
If you are not using navigation Controller than
- (void)buttonPressed:(id)sender
{
NewViewController *newVC = [NewViewController new];
[self presentViewController:newVC animated:YES completion:nil];
}
回答5:
For 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()
}
In ViewController
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
}
}
Hope this helps. Happy coding :]