I have a tableview controller class, a tableviewcell subclass and a uibutton subclass.
I am creating an instance of the unbutton subclass in the tableviewcell subclass and initialize a button in a specific cell position.
Then I am using this cell in the tableview controller class. Also I am trying to add an IBAction to the button. But it can't recognize the object of the uibutton subclass while everything else works fine. What am I doing wrong in the declaration?
tableviewcell.m
#import "tableviewcell.h"
#import "CustomCheckButton.h"
CustomCheckButton *starbtn = [[CustomCheckButton alloc] init];;
starbtn = [[CustomCheckButton alloc]initWithFrame:CGRectMake(243,0, 30, 30)];
tableviewcontroller.m
In the cellForRowAtIndexPath object startbtn won't be recognized:
#import "ScannedProductControllerViewController.h"
#import "imageCellCell.h"
tableviewcell*firstRowCell = (tableviewcell *)cell;
[firstRowCell.prodimage setImage: [UIImage imageNamed:#"test1.jpg"]];
[firstRowCell.label1 setText:#"17.5"];
[firstRowCell.label2 setText:#"Score"];
[firstRowCell.basket setImage: [UIImage imageNamed:#"Basket.jpg"]];
// reference of the home button to the buttonclick method
[firstRowCell.homebtn addTarget:self action:#selector(clickButton:) forControlEvents:UIControlEventTouchUpInside];
// reference of the favorites button to the buttonclick method
[firstRowCell.starbtn addTarget:self action:#selector(clickFavButton:) forControlEvents:UIControlEventTouchUpInside];
CustomCheckButton.h
#import <UIKit/UIKit.h>
#interface CustomCheckButton : UIButton {
BOOL _checked;
}
#property (nonatomic, setter = setChecked:) BOOL checked;
-(void) setChecked:(BOOL) check;
#end
#interface TableViewCell : UITableViewCell
#property (nonatomic, strong) CustomCheckButton *startButton;
#end
#implementation TableViewCell
- (instancetype)init {
self = [super init];
self.startButton = [[CustomCheckButton alloc]initWithFrame:CGRectMake(243,0, 30, 30)];
return self;
}
- (void)viewDidLoad {
[super viewDidLoad];
[self.contentView addSubview:self.startButton];
}
#end
First, why don't you create your cells in the storyboard as a prototype cell or inside of a XIB file.
Then, create an IBAction to the button and respond to it in the cell. Finally, make a protocol for your cell that will inform the delegate when the button is tapped. You can then setup your table view controller as the delegate of the cells and respond to the button tapped. If you need to determine which cell you're working with, you can even pass the cell as a parameter of one of the protocol's methods you use.
Related
I have a custom UIButton class like below :
.h
#import <UIKit/UIKit.h>
#class FriendButton;
#protocol LongPressedButtonDelegate
- (void)buttonIsLongPressed:(FriendButton *)button;
#end
#interface FriendButton : UIButton
#property (nonatomic, weak) id<LongPressedButtonDelegate > delegate;
#end
.m
#implementation FriendButton
//this is called from the interface builder
-(id) initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:(NSCoder *)aDecoder];
NSLog(#"init called");
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc]initWithTarget:self action:#selector(longPress:)];
[self addGestureRecognizer:longPress];
return self;
}
-(void)longPress:(id)sender {
NSLog(#"long press");
[self.delegate buttonIsLongPressed:self];
}
#end
My buttons are set up in the interface builder, they are contained in a UITableView cell. In my UITableViewController I have :
-(void)buttonIsLongPressed:(FriendButton *)button {
NSLog(#"Delegate method!");
}
and it controller conforms to protocol. The long press gesture works but the delegate method is not being called. I'm not sure why it's not working. Is it because I have to set each buttons delegate to the UITableViewController? If so how would I do that? The buttons are set up in the interface builder.
Define your UIButton property this way
#interface FriendButton : UIButton
#property (nonatomic, weak) IBOutlet id<LongPressedButtonDelegate > delegate;
#end
Then go to Interface Builder and right click on UIButton and you will see delegate link this delegate to UITableViewController. It should work
In your cellForRowAtIndexPath implementation in your UITableViewController you will need to do something like:
cell.button.delegate = self;
Edit: This is if you want to do it programmatically. Refer to the answers around IBOutlets if you want to do it in your storyboard.
I have a uiview named leftMenuView and on that view i have a button and i want to add an action to that button so that action method should call a view controller. Take a look what till i have done:
this is my leftMenuView.h
#import <UIKit/UIKit.h>
#class LeftMenuView;
#protocol LeftMenuViewProtocol <NSObject>
-(void)homeClicked;
#end
#interface LeftMenuView : UIView
#property (nonatomic,assign) id<LeftMenuViewProtocol> customDelegate;
-(IBAction)homeClickedAction:(id)sender;
#end
and in leftMenuView.m file
#import "LeftMenuView.h"
#implementation LeftMenuView
-(IBAction)homeClickedAction:(id)sender
{
[self.customDelegate homeClicked];
NSLog(#"Clicked Home");
}
#end
Now i am trying to call that method through the delegate
Now in homeViewController.h
#interface homeViewController : UIViewController<LeftMenuViewProtocol>
and now in my homeViewController.m i am trying to call that method but it is not called
-(void)homeClicked
{
NSLog(#"Clicked Home");
}
But the above method is not called where as in leftViewMenu.m that method is called. Hope any one helps me regarding this issue.
Or, assuming you have a reference on your leftMenuView in your homeViewController you can create your UIButton programmatically and set the target of your button with your homeViewController. For example, in the viewDidLoad of your homeViewController, you can do something like:
UIButton *myButton = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 100, 20)]; // set the frame you want
myButton.backgroundColor = [UIColor redColor]; // Do additional set up
[myButton addTarget:self action:#selector(homeClicked) forControlEvents:UIControlEventTouchUpInside]; // here the target of your button's action is your homeViewController
[self.leftMenuView addSubView:myButton]; // Add the button in your left view
I suppose that in homeViewController you have a property for the LeftMenuView you need to set the customDelegate to self. In the homeViewController viewDidLoad method:
self.leftMenuView.customDelegate = self;
I have a switch inside a custom cell. The switch is allocated and set to the accessoryView of the cell inside the .m file of the custom cell.
However, I need the selector method for the switch to be handled in the ViewController of the tableView the custom cell resides in.
Currently when clicking the switch I get a crash that it can't find the selector, most likely because its looking in the cell's .m.
How can I declare my switch to have its selector look in the correct location?
edit as per request...
//cell .m
- (void)setType:(enum CellType)type
{
if (_type == SwitchType)
{
UISwitch *switchView = [[UISwitch alloc] init];
[switchView addTarget:self action:#selector(flip:) forControlEvents:UIControlEventValueChanged];
self.accessoryView = switchView;
}
}
Sounds like the job for a delegate. Create a protocol in your cell interface like:
#protocol MyCellDelegate <NSObject>
- (void)myCell:(MyCell *)sender switchToggled:(BOOL)value;
#end
and specify a delegate
id <MyCellDelegate> delegate;
Then in your MyCell.m, when the switch is toggled check if the delegate is defined, if so call it:
if (self.delegate != nil && [self.delegate respondsToSelector:#selector(myCell:switchToggled:)]) {
[self.delegate myCell:self switchToggled:switch.value]
}
And in your ViewController be sure to set the ViewController to be the delegate for the cell and implement the protocol method.
You could create your switch as a public property then set its target in cellForRowAtIndex:
#interface CustomCell : UITableViewCell
#property (nonatomic, strong) UISwitch *switch;
Or you could create a custom NSNotification that is fired off. And have your viewController listen for the notification then deal with it.
Blocktastic :)
You could also get fancy with blocks.
typedef void(^CustomCellSwitchBlock)(BOOL on);
#interface CustomCell : UITableViewCell
#property (nonatomic, readwrite) CustomCellSwitchBlock switchAction;
Then in your CustomCell.m:
- (void)handleSwitch:(UISwitch *)switch
{
switchAction(switch.on);
}
Then in your cellForRowAtIndex::
cell.action = ^(BOOL on){
if (on) {
// Perform On Action
} else {
// Perform Off Action
}
};
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?
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];
}
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.
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.
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 :)
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.
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
}
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.
I have a UIView .xib file. This is opened from the storyboard entry point UIViewController1 as a subview. The subview has a IBButton which when pressed opens a UIViewController2. Is this possible by any chance?
First, create a segue going from your first view controller to your second. I'm going to name it OpenViewController2. We'll be able to call that segue programmatically.
In your UIView .h file, create a protocol that defines a delegate for that view:
SomethingView.h:
#protocol SomethingViewDelegate <NSObject> {
- (void)importantThingHappened;
}
...
#interface SomethingView {
id<SomethingViewDelegate> delegate;
}
#property (nonatomic, strong) id<SomethingViewDelegate> delegate;
SomethingView.m:
#implementation
#synthesize delegate;
...
// The IBAction for the button in your view
- (IBAction)buttonClicked:(id)sender {
[delegate importantThingHappened];
}
MyViewController.m, where you create your view:
// Create view
UIView *myView = ...
myView.delegate = self;
Later in MyViewController:
// Implement the protocol. This function will be called when the button action
// inside of the UIView you created is pressed
- (void)importantThingHappened {
[self performSegueWithIdentifier:#"OpenViewController2" sender:self];
}
First give your IBButton an unique tag, and in your UIViewController's viewDidLoad,
// add following line into viewDidLoad
[(UIButton*)[self.view viewWithTag:MY_UNIQUE_TAG_FOR_BUTTON] addTarget:self action:#selector(buttonPressed:) forControlEvent:UIControlEventTouchUpInside];
and finally implement the buttonPressed: for whatever you want
-(void) buttonPressed:(UIButton*)aButton {
// do what you want to do.
}