I Have a button within a uitableview cell -
I have set it up to trigger a fmethod when clicked - (the function displays messages and resets the message count).
my code for this method is as follows -
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
//Define Custom Cells
static NSString *CellCountI =#"CellCount";
UITableViewCell *cell;
feedData *f = [self.HpFeedArray objectAtIndex:indexPath.section];
//Comparison Strings
NSString *count = #"Count";
//If statement Cell Filters
//If Count Cell
if ([f.FeedGroup isEqualToString:count]) {
cell = [tableView dequeueReusableCellWithIdentifier:CellCountI forIndexPath:indexPath];
HP_Header_TableViewCell *hpTC = (HP_Header_TableViewCell *)cell;
hpTC.buttonPressedSelector = #selector(buttonImpMsg);
hpTC.buttonPressedTarget = self;
[hpTC.msgsBtn setTitle: f.FeedTitle forState: UIControlStateNormal];
return hpTC;
}
}
The buttonImpMsg method is as follows -
- (void)buttonImpMsg
{
NSLog(#"Back Button Pressed!");
[self removeBtn];
}
I would like to hide the button when clicked - but I'm not sure how to reference it from the buttonImpMsg method?
Pass the sender to the selector:-
- (void)buttonImpMsg:(id)sender {
[sender removeFromSuperview];
}
You could implement the delegate pattern, IMHO, it's the most proper way.
i think , better will be to make it hidden, if you want to again. – #pawan
I would also hide the button rather than remove it.
Try this implementation to hide the button, you can also do the same thing to hide this one after.
TableViewCell.h:
#import "TableViewCell.h"
#implementation TableViewCell
//.... Connect your action or set selector to this method:
- (IBAction)hideButton:(id)sender
{
UIButton *button = (UIButton *) sender;
// hide the button by using the setter
button.hidden = YES;
//.... Check if the delegate method has been implemented
if ([_delegate respondsToSelector:#selector(hideButton)]) {
[_delegate hideButton];
}
}
#end
TableViewCell.h
//... Declare the delegate:
#protocol CellDelegate <NSObject>
- (void)hideButton;
#end
#interface TableViewCell : UITableViewCell
//... Add a delegate property:
#property (strong, nonatomic) id <CellDelegate> delegate;
#end
ViewController.m:
//... set self a the delegate of your cell
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
//...
cell.delete = self;
return cell;
}
ViewController.m:
//...
#interface ViewController () <CellDelegate>
//...
//... Implement the delegate method if you need to do stuff on the controller side
- (void)hideButton
{
//do stuff here if needed
}
Related
Hi I'm new to iOS Development and i am using Objective C. I have a problem in UITableview.That is, i need to write action for multiple UIButton which is loaded into a tableview(DutiesTableView.m & .h) as a custom cell(BreakTimeCell.m &.h). That tableview(DutiesTableView.m & .h) is loaded into another tableview(ViewController.m&.h, TableName is PersonalTable) as s custom cell using XIB.
[Shift(custom cell),Duties(UITableview),Break Time(UITableview)],[This is my ViewController.m
This is my sample output.When i click on the Break time UIButton it should show UIDatePicker on Main Tableview
Create A button,
UIButton *btnSample = [[UIButton alloc]initWithFrame:CGRectMake(215,10,100,50)];
[btnSample setTitle:#"Button Title" forState:UIControlStateNormal];
[yourView addSubview:btnSample];
Add a action to button
[btnSample addTarget:self action:#selector(your action or method) forControlEvents:UIControlEventTouchUpInside];
You can use delegates to get call back from custom cell to your view controller, for example in the custom cell in your code, BreakTimeCell.h file declare a protocol and add button in BreakTimeCell.xib and give outlet and action to custom cell like below,
#import <UIKit/UIKit.h>
#class BreakTimeCell; //forword declaration
#protocol BreakTimeCellDeleagte<NSObject>
- (void)breakTimeCell:(BreakTimeCell *)cell didTapTheButton:(UIButton *)aButton;
#end
#interface BreakTimeCell : UITableViewCell
#property (weak, nonatomic) IBOutlet UIButton *aButton;
#property (weak, nonatomic) id<BreakTimeCellDeleagte> cellDelegate;//this is important
- (IBAction)myButtonAction:(id)sender;
+ (BreakTimeCell *)getBreakTimeCell; //to get the custom cell
#end
and in BreakTimeCell.h file define action for the button
#import "BreakTimeCell.h"
#implementation BreakTimeCell
+ (BreakTimeCell *)getBreakTimeCell {
NSArray *nibContents = [[NSBundle mainBundle] loadNibNamed:#"BreakTimeCell" owner:self options:NULL];
NSEnumerator *nibEnumerator = [nibContents objectEnumerator];
BreakTimeCell *customCell = nil;
NSObject* nibItem = nil;
while ((nibItem = [nibEnumerator nextObject]) != nil) {
if ([nibItem isKindOfClass:[BreakTimeCell class]]) {
customCell = (BreakTimeCell *)nibItem;
break;
}
}
return customCell;
}
- (void)awakeFromNib {
[super awakeFromNib];
// Initialization code
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
[super setSelected:selected animated:animated];
// Configure the view for the selected state
}
//call the delegate method in the button action
- (IBAction)myButtonAction:(id)sender {
if([self.cellDelegate respondsToSelector:#selector(breakTimeCell:didTapTheButton:)])
{
[self.cellDelegate breakTimeCell:self didTapTheButton:sender];
}
}
#end
and in view controller just set the cell delegate to view controller,
#import "ViewController.h"
#import "BreakTimeCell.h"
#interface ViewController ()<UITableViewDelegate, UITableViewDataSource, BreakTimeCellDeleagte>
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return 10;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
BreakTimeCell *cell = [tableView dequeueReusableCellWithIdentifier:#"MY_BREAK_CELL"];
if (cell == nil) {
cell = [BreakTimeCell getBreakTimeCell]; //get the cell
}
cell.cellDelegate = self; //set the delegate to controller (self)
return cell;
}
//this is the delegate method called for the button action.
- (void)breakTimeCell:(BreakTimeCell *)cell didTapTheButton:(UIButton *)aButton {
NSIndexPath *indexPath = [self.personalTable indexPathForCell:cell];
NSLog(#"button tapped for index:%ld",(long)indexPath.row);
}
thats it, you will get output like below,
2017-12-13 11:44:24.693161+0530 Test[3082:100133] button tapped for index:3
2017-12-13 11:44:25.177169+0530 Test[3082:100133] button tapped for index:4
2017-12-13 11:44:26.223166+0530 Test[3082:100133] button tapped for index:0
2017-12-13 11:44:27.663060+0530 Test[3082:100133] button tapped for index:1
2017-12-13 11:44:28.495780+0530 Test[3082:100133] button tapped for index:2
Add this in cellForRow
yourButton.tag = indexPath.row;
[yourButton addTarget:self action:#selector(methodName:) forControlEvents:UIControlEventTouchUpInside];
Perform task in below method
- (void)methodName:(id)sender
{
// you can get index of button from sender.tag
}
I am using storyboard. I have a table view with 1 Prototype cell, and have four rows in table view. Each cell has a label,image view and a button. Now I want to show different data on next View Controller say(DetailsVc), each time when button is pressed.
Row1- Button click will open DetailsVC with some data.
Row2- Button click will open DetailsVC with different data.
and same process for remaining rows.
Any suggestions.
#implementation DetailsVC
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
EmpEngmntTableViewCell *emp;
if((emp.viewGalleryBtn.tag = 1))
{
NSLog(#"tag num for 1st row is %ld",(long)emp.viewGalleryBtn.tag);
self.title = #"Team Building Session";
}
}
The best is to use a delegate pattern. I added an answer here, you can use it for example.
Here is an example enclosed:
Custom Cell .h Class:
#protocol DetailVCProtocol <NSObject>
-(void)loadDetailScreenWithIndex:(NSInteger)rowIndex;
#end
#property (nonatomic, retain) id<DetailVCProtocol> delegate;
#property (nonatomic, retain) IBOutlet UIButton *btn;
Then Synthesize it in .m file
Add this in m file:
-(IBAction)loadDetails:(id)sender
{
// ..... blah blah
[self.delegate loadDetailScreenWithIndex:btn.tag];
}
The viewcontroller that loads this cell:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// create cell here
cell.btn.tag = indexPath.row;
cell.delegate = self;
}
-(void)loadDetailScreenWithIndex:(NSInteger)rowIndex
{
// Cretae DetailVC with parameter `rowIndex` to populate data
[self presentViewController:detailVC animated:YES completion:nil];
}
In function - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
use this code
cell.btn.tag = indexPath.row;
[cell.btn addTarget:self action:#selector(btnTpd:) forControlEvents:UIControlEventTouchUpInside];
- (IBAction)btnTpd:(id)sender
{
UIButton *btn = (UIButton *)sender;
\\You can get which button is pressed by btn.tag
\\Here call to next ViewController and pass data
}
In cellForRowAtIndexPath
cell.optionsBtn.tag=indexPath.row;
[cell.optionsBtn addTarget:self action:#selector(showMyOptionBtn:) forControlEvents:UIControlEventTouchUpInside];
-(void)showMyOptionBtn:(UIButton *)sender
{
EventDetailViewController *EventDetail=[[EventDetailViewController alloc]init];
EventDetail.buttonTag=sender.tag;
[self.navigationController pushViewController:EventDetail animated:NO];
}
In EventDetailViewController class
if (self.buttonTag==1) {
// your condation
}
else
{
//your condation
}
I'd like to implement a "like" functionality in a table view. Each row has a like button and a like counter.
When the user taps "like" the like counter should be increased by one. If the same user taps again, the like counter is decreased by one.
I have implemented this using reloadRowsAtIndexPaths. However, this reloads the whole cell and it is bad in terms of usability.
Would you know a simple mechanism to implement such a functionality? The main requirement is that only the like counter is updated, not the whole cell or table.
Thanks in advance,
Tiago
U don't need to reload a whole cell, simply change the title/image for your like button.
Smth. like this:
#pragma mark - custom table cell
#interface TableCellWithLikeButton : UITableViewCell
#property (weak, nonatomic) IBOutlet UIButton *userLikeButton;
#end
#implementation TableCellWithLikeButton
#end
#pragma mark - User modal
#interface User:NSObject
#property (assign, nonatomic) BOOL wasLiked;
#property (assign, nonatomic) NSInteger likesCounter;
#end
#implementation User
#end
#implementation VC1
{
IBOutlet UITableView *_tableView;
NSMutableArray *_users;
}
- (id)initWithCoder:(NSCoder*)aDecoder
{
if(self = [super initWithCoder:aDecoder]) {
_users = [NSMutableArray array];
for (int i=0;i<100;i++) {
User *user = [User new];
[_users addObject:user];
}
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
}
#pragma mark - UITableViewDataSource
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return _users.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"CellWithLikeButton";
TableCellWithLikeButton *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
[cell.userLikeButton addTarget:self action:#selector(changeLikeStatus:) forControlEvents:UIControlEventTouchUpInside];
User *user = _users[indexPath.row];
[cell.userLikeButton setTitle:user.wasLiked?#"Liked":#"Like" forState:UIControlStateNormal];
cell.userLikeButton.tag = indexPath.row;
return cell;
}
- (void)changeLikeStatus:(UIButton *)likeButton
{
User *user = _users[likeButton.tag];
user.likesCounter += user.wasLiked?-1:1;
user.wasLiked = !user.wasLiked;
[likeButton setTitle:user.wasLiked?#"Liked":#"Like" forState:UIControlStateNormal];
}
#end
I have a table view controller with three cells, each with a textfield where a user defines:
-Country
-State (disabled)
-City (disabled)
How do I enable "State" textfield after the user types in "Country"?
The main problem is that I'm using a model called "Field", which has a property called "depends", which shows the id of the other Field that must not be empty.
My custom table view cell has a property "Field".
If I use "textfieldDidBeginEditing()" I can only access the textfield inside the cell, not the "Field" property.
All my cells are created dynamically.
The project is in Objective-C.
Its quite easy, here are your steps
Set textfield.delegate to your UIViewController
Implement UITextFieldDelegate method - (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
Check for text length if needed
Grab next cell that u would like to enable using following code:
UITableViewCell *thisCell = [textView superCell];
NSIndexPath *thisIndexPath = [tableView indexPathForCell:thisCell];
NSIndexPath *nextIndexPath = [NSIndexPath indexPathForRow:thisIndexPath.row inSection:thisIndexPath.section];
UITableViewCell *nextCell = [tableView cellForRowAtIndexPath: nextIndexPath];
Call becomeFirstResponder for a textField inside nextCell
You will need to create an UIView category with following method for code above to work
- (UITableViewCell *)superCell {
UIView *cell = self;
do {
cell = cell.superview;
} while( ![cell isKindOfClass:[UITableViewCell class]] && cell != nil );
return (UITableViewCell *)cell;
}
Short and simple:
For short list like your (table view can place all cells on the screen) it's will be enough to keep references to all three fields:
// Your model
#interface AddressStorageModel : NSObject
#property (strong, nonatomic) NSString* country;
#property (strong, nonatomic) NSString* state;
#property (strong, nonatomic) NSString* city;
#end
// Your cell
#interface FieldTableCell : UITableViewCell
#property (readonly, nonatomic) UITextField* textField;
#end
// Your view controller implementation
#implementation ViewController
{
__weak UITextField* _countryField;
__weak UITextField* _stateField;
__weak UITextField* _cityField;
AddressStorageModel* _addressStorage;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return 3;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
FieldTableCell *cell = (FieldTableCell*)[tableView dequeueReusableCellWithIdentifier:#"cell" forIndexPath:indexPath];
cell.textField.delegate = self;
switch (indexPath.row) {
case 0: // Country
_countryField = cell.textField;
break;
case 1: // State
_stateField = cell.textField;
break;
case 2: // City
_cityField = cell.textField;
break;
}
return cell;
}
- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
[textField resignFirstResponder];
return YES;
}
- (void)textFieldDidEndEditing:(UITextField *)textField
{
// Validate somehow and move cursor to next field:
if (textField.text.length) {
UITextField* nextTextField = [self p_textFieldNextTo:textField];
[nextTextField setEnabled:YES];
[nextTextField becomeFirstResponder];
}
}
- (UITextField *)p_textFieldNextTo:(UITextField *)textField
{
if (textField == _countryField)
return _stateField;
else if (textField == _stateField)
return _cityField;
return _countryField;
}
- (void)completeButtonAction:(id)sender
{
_addressStorage.country = _countryField.text;
_addressStorage.state = _stateField.text;
_addressStorage.city = _cityField.text;
}
More complicated ... but simpler for future support
But if you plan to increase number of fields you should implement your code in this way:
// Your model
#interface AddressField : NSObject
#property (strong, nonatomic) NSString* title;
#property (strong, nonatomic) NSString* value;
#property (strong, nonatomic) AddressField* dependency;
#property (strong, nonatomic) NSIndexPath* indexPath;
#end
// Your cell
#protocol FieldTableCellDelegate <NSObject>
#required
- (void)fieldCellDidEndEditing:(FieldTableCell*)cell;
#end
#interface FieldTableCell : UITableViewCell <UITextFieldDelegate>
#property (readonly, weak, nonatomic) AddressField* addressField;
#property (weak, nonatomic) id<FieldTableCellDelegate> delegate;
- (void)configureForField:(AddressField *)field;
- (void)startEditing;
#end
#implementation ViewController
{
NSArray * _fields; // I left to your discretion how to create fields and assign dependencies
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return _fields.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
FieldTableCell *cell = (FieldTableCell*)[tableView dequeueReusableCellWithIdentifier:#"cell" forIndexPath:indexPath];
cell.delegate = self;
AddressField * field = _fields[indexPath.row];
field.indexPath = indexPath;
[cell configureForField:field];
return cell;
}
- (void)fieldCellDidEndEditing:(FieldTableCell *)cell
{
typeof(self) __weak weakSelf = self;
[UIView animateWithDuration:0.25
animations:^{
[weakSelf.tableView scrollToRowAtIndexPath:cell.addressField.dependency.indexPath
atScrollPosition:UITableViewScrollPositionMiddle
animated:NO];
}
completion:^(BOOL finished) {
FieldTableCell* nextCell = (FieldTableCell*)[weakSelf.tableView cellForRowAtIndexPath:cell.addressField.dependency.indexPath];
[nextCell startEditing];
}];
}
#end
#implementation FieldTableCell
{
UITextField* _inputField;
}
- (void)configureForField:(AddressField *)field
{
_addressField = field;
_inputField.text = field.value;
_inputField.placeholder = field.title;
// do all other configurations...
}
- (void)textFieldDidEndEditing:(UITextField *)textField
{
if (textField.text.length > 0) {
_addressField.value = textField.text;
[_delegate fieldCellDidEndEditing:self];
}
}
- (void)startEditing
{
[_inputField becomeFirstResponder];
}
#end
use textFielShouldBeginEditing method of UITextFieldDelegate
Declaration
SWIFT
optional func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool
OBJECTIVE-C
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField
example-
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField{
//do state button enabled task here
}
With a button:
-(IBAction) continueButton: (UIButton*) sender{
//check if the state textfield is not empty in order to proceed
if(![self.stateTextfield.text isEqualToString: #""]){
self.cityTextfield.enabled = YES;
}
}
Simple get around but not really clean.
UPDATE
Use a normal View Controller.
Drag a Container View into the View Controller to allow for the size of your tableview that captures data.
Delete the automatically generated View Controller and drag a Table View Controller onto the storyboard and embed it in the Container View (ctrl + drag and select the embed in option)
Set this table view to Static.
Drag a TableView (not TableViewController) into the the ViewController.
Ctrl + drag the from TableView to the View Controller icon and set datasource and delegate. This will allow for data to load into the tableview.
This should now solve any issues with loading data as the data will load into the dynamic tableview and the static tableview will be unaltered (if this is a viable option for you). This way you can connect your textfields to your view controller and access their properties.
If you need any help or this isn't a good solution let me know.
I got a button in a tableview cell (named "deleteTemplate"), and when it's pressed i should get the "index.row" for the cell the button is in. Anybody knows how to get the "index.row" for the cell, when you click a button in the cell?
My current code for the button:
UITableViewCell *clickedCell = (UITableViewCell *)[[sender superview] superview];
NSIndexPath *clickedButtonIndexPath = [self.tableView indexPathForCell:clickedCell];
NSDictionary *dic = [tableData objectAtIndex:clickedButtonIndexPath.row];
But clickedButtonIndexPath = NULL? :(
This worked in iOS 6.
Thank you!!!
You are getting null because the view hierarchy in iOS 7 is changed.
I recommend to use Delegate for getting the indexpath of TableViewCell.
You can get the sample project from here
Here is the sample:
My View Controller looks like this:
//#import "ViewController.h"
//#import "MyCustomCell.h"
#interface ViewController ()
{
IBOutlet UITableView *myTableView;
NSMutableArray *dataSourceArray;
}
#end
#implementation ViewController
-(void)viewDidLoad
{
[super viewDidLoad];
dataSourceArray = [[NSMutableArray alloc] init];
for(int i=0;i<20;i++)
[dataSourceArray addObject:[NSString stringWithFormat:#"Dummy-%d",i]];
}
-(void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
//pragma mark - UITableView Delegate And Datasource -
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return dataSourceArray.count;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdenfifier = #"MyCustomCell";
MyCustomCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdenfifier forIndexPath:indexPath];
[cell setDelegate:self];
[cell.myButton setTitle:[dataSourceArray objectAtIndex:indexPath.row] forState:UIControlStateNormal];
return cell;
}
//pragma mark - MyCustomCell Delegate -
-(IBAction)myCustomCellButtonTapped:(UITableViewCell *)cell button:(UIButton *)sender
{
NSIndexPath *indexPath = [myTableView indexPathForCell:cell];
NSLog(#"indexpath: %#",indexPath);
}
#end
MyCustomCell.h looks like this:
//#import
#protocol MyCustomCellDelegate
#optional
- (IBAction)myCustomCellButtonTapped:(UITableViewCell *)cell button:(UIButton *)sender;
#end
#interface MyCustomCell : UITableViewCell
#property (weak, nonatomic) IBOutlet UIButton *myButton;
#property(nonatomic, weak)id delegate;
#end
MyCustomCell.m looks like this:
//#import "MyCustomCell.h"
#implementation MyCustomCell
-(id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
}
return self;
}
-(void)setSelected:(BOOL)selected animated:(BOOL)animated
{
[super setSelected:selected animated:animated];
}
//#pragma mark - My IBActions -
-(IBAction)myCustomCellButtonTapped:(UIButton *)sender
{
if([self.delegate respondsToSelector:#selector(myCustomCellButtonTapped:button:)])
[self.delegate myCustomCellButtonTapped:self button:sender];
}
#end
you set tag for your button in cellforrowatindexpath
clickedButtonIndexPath.tag=indexpath.row;
when button is presse in action write
nslog(#"%i",btutton.tag);
if clickedbuttonindexpath is nil it wont uitableviewcell
This question is already answered many times. For completion, here is the best solution:
-(IBAction)cellButtonTapped:(UIButton *)sender
{
CGRect buttonFrame = [sender convertRect:sender.bounds toView:self.tableView];
NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:buttonFrame.origin];
}
when you make the cell assign at you button.tag the indexPath.row and when you click the button in your method you have the sender (the button) and you get the indexPath from the sender's tag