5

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?

Community
  • 1
  • 1
bhawesh
  • 1,310
  • 2
  • 19
  • 57

7 Answers7

9

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];

 }
Anil Varghese
  • 42,757
  • 9
  • 93
  • 110
  • 1
    this was the answer i was looking for.....This is the right way to achieve functionality asked in the question using delegate :) – bhawesh Mar 12 '13 at 14:47
  • It's much simpler than this and you don't even need a delegate to do it. Check out my answer below. – leftspin Aug 28 '15 at 20:04
3

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.

leftspin
  • 2,468
  • 1
  • 25
  • 40
1

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.

  1. 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.
  2. Define callback APIs with @protocol, using @optional if not all the methods are required.
  3. 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.
Mark Kryzhanouski
  • 7,251
  • 2
  • 22
  • 22
0

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 :)

Xavier Huppé
  • 544
  • 1
  • 4
  • 17
-1

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.

GeneralMike
  • 2,951
  • 3
  • 28
  • 56
  • @ancode: um, ok, in that case I think you have to use `@selector`s. I've never done it that way, so I don't remember the exact syntax, but I think that's what it involved. – GeneralMike Mar 12 '13 at 12:49
  • selector would work only in the case when you have the reference to the object which contains the method .....I don't have the reference to the object :) – bhawesh Mar 12 '13 at 12:59
-1

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.

Community
  • 1
  • 1
GeneralMike
  • 2,951
  • 3
  • 28
  • 56
  • as a said here self is not the target ...ViewController of self is the traget – bhawesh Mar 12 '13 at 13:07
  • @ancode: So you're subclassing UIView, and creating a UIButton there? That's not really the typical way to do this. Is there a specific reason why you can't create the button when you create the view with your view controller, then add the button as a subview of your view? (see edit) – GeneralMike Mar 12 '13 at 14:04
  • view can be used by many view controller! if i add button in ViewController then i have to do it in every other view controller who wants the same view – bhawesh Mar 12 '13 at 14:11
-1

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
}
Kumar
  • 7
  • 5