可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I have a UIViewController
with its UIView
which contains a UIButton
. I want to trigger a method in UIViewController
on button click event.
Keeping reference of UIViewController
doesn't seem to be a good idea like the following link says:
Get to UIViewController from UIView?
So I want to achive this using a delegate. Any hint on how to achieve this?
回答1:
You can do something like this
CustomView.h
#import <UIKit/UIKit.h>
@protocol CustomViewDelegate <NSObject>
-(void)didButtonPressed;
@end
@interface CustomView : UIView
@property (assign) id<CustomViewDelegate> delegate;
@end
CustomView.m
#import "CustomView.h"
@implementation CustomView
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
self.backgroundColor = [UIColor whiteColor];
//[self addSubview:titleLbl];
UIButton *button= [UIButton buttonWithType:UIButtonTypeRoundedRect];
button.frame = CGRectMake(100, 100, 100, 50);
[button addTarget:self.delegate action:@selector(didButtonPressed) forControlEvents:UIControlEventTouchUpInside];
[button setTitle:@"pressMe" forState:UIControlStateNormal];
[self addSubview:button];
}
return self;
}
in your ViewController.m
-(void)loadView
{
[super loadView];
CustomView *view = [[CustomView alloc]initWithFrame:self.view.bounds];
view.delegate = self;
[self.view addSubview:view];
}
回答2:
This is what the responder chain was built for. When you add a target to your button, just supply nil
for the target:
[mySpecialButton addTarget:nil
action:@selector(mySpecialButtonTapped:)
forControlEvents:UIControlEventTouchUpInside];
The nil
target basically means "send mySpecialButtonTapped:
to any object in the responder chain that can handle it".
Now you can handle this selector anywhere in the responder chain, which includes the button itself, its containing view, its containing view controller, the UIApplication, and finally your AppDelegate. Just place this method in the object most appropriate for your needs:
- (void)mySpecialButtonTapped:(id)sender {
NSLog("My special button was tapped!");
}
You don't need delegates or callback blocks (as in the accepted answer) if you just want to bubble a message up.
回答3:
I guess that you expected something more fundamental then just pass some button action to controller.
I always follow MVC pattern in case of model/view/controller collaboration. It resolve your issue and many other. And I want to share my experience.
- Separate controller from view and model: don't put all of the "business logic" into view-related classes; this makes the code very unusable. Make controller classes to host this code, but ensure that the controller classes don't make too many assumptions about the presentation.
- Define callback APIs with
@protocol
, using @optional
if not all the methods are required.
- For view define protocol like
<view class name>Protocol
(example NewsViewProtocol). For controller define delegate like <view class name>Delegate
(example NewsViewDelegate) and dataSource like <view class name>DataSource
(example NewsViewDataSource). Keep all this @protocols in one separate file named <view class name>Protocol.h
(example NewsViewProtocol.h)
Short example:
Contents of NewsView.h
//
// NewsView.h
@interface NewsView : UIView <NewsViewProtocol> {
@protected
NSObject* delegate_;
NSObject* dataSource_;
}
@end
Contents of NewsController.h and .m
//
// NewsController.h
@interface NewsController : UIViewController <NewsViewDataSource, NewsViewDelegate> {
}
@property (nonatomic, weak) UIView<NewsViewProtocol>* customView;
@end
@implementation NewsController
- (void)viewDidLoad {
[super viewDidLoad];
self.customView = (UIView<NewsViewProtocol>*)self.view;
[self.customView setDelegate:self];
[self.customView setDataSource:self];
}
@end
Contents of NewsViewProtocol.h
//
// NewsViewProtocol.h
@protocol NewsViewProtocol;
@protocol NewsViewDelegate<NSObject>
@optional
- (void)someAction;
- (void)newsView:(UIView<NewsViewProtocol>*)newsView didSelectItemAtIndexPath:(NSIndexPath *)indexPath;
@end
@protocol NewsViewDataSource<NSObject>
@required
- (id)newsView:(UIView<NewsViewProtocol>*)newsView itemAtIndexPath:(NSIndexPath *)indexPath;
- (NSInteger)numberOfItemsInNewsView:(UIView<NewsViewProtocol>*)newsView section:(NSInteger)section;
- (BOOL)newsView:(UIView<NewsViewProtocol>*)newsView shouldDisplaySection:(NSInteger)section;
@end
@protocol NewsViewProtocol<NSObject>
@required
//Never retain delegate instance into implementation of this method
- (void)setDelegate:(NSObject<NewsViewDelegate>*)delegate;
//Never retain delegate instance into implementation of this method
- (void)setDataSource:(NSObject<NewsViewDataSource>*)dataSource;
- (void)reload;
@end
You may consider that it is redundant. In simple view controller, YES. But if you develop very complex screen with huge amount of data then it gives you some advantages as:
- Helps you to separate responsibility between view and controller.
- Keeps your code clear.
- Makes you code more reusable.
回答4:
Life is easy in xCode.
At the very beginning be sure that your xib View (the one with your button inside it) is associated to the right ViewController class. Which can be the default ViewController class that comes with a new project or your custom one.
After this, here comes the magic trick! Separate your view into 2 panel. The goal is to see your xib and your viewController code (the .m file). Now press the control key of your keyboard and drag your UIButton to the code. Select IBAction. It will generate something you can call a "listener" in other language. Go to the core code of your View Controller and complete the method!
Easy as that! Have fun :)
回答5:
You don't really need delegates for this - it is how UIButtons are intended to be used. Just control-click and drag from your button to the .m file for your UIViewController. This will create a new method. From there, you can either make a call to the method you wrote or just copy-paste what you have into the new method.
回答6:
You can try this:
[yourButton addTarget:self action:@selector(yourButtonAction:) forControlEvents:UIControlEventTouchUpInside];
And in your selector specify the action
- (IBAction)yourButtonAction:(id)sender {
//Action to perform
}
回答7:
To add a button programmatically, in myViewController.m
UIView *yourView = [[UIView alloc] init];
UIButton *yourButton = [[UIButton alloc] initWithFrame:CGRectMake(0,0,100,21)];
[yourButton addTarget:self action:@selector(yourMethod) forControlEvents:UIControlEventTouchDown];
[yourView addSubview:yourButton];
More info here.