Events when clicks on UITableViewCell in objective C? - ios

I encountered an issue in my project as below:
I wrote a class that is subclass of UIView with:
1 - a button
2 - a table view
3 - a textfield
I've created an object of that class and add the object to an view of uiviewcontroller. When I click on the button, make the table view appear with a list of all countries and click on a cell of table view, the textfield will get cell's value. I wanna detect events when I touch on a cell of tableview. How can I do that? Please help me, Thank in advance.
P/S: tableview is subView of contentView. Hierarchy as follow:
ViewController - ScrollView - contentView -(View1, View2...)

Is this what you're trying to do?
You didn't give codes, or something.. So, this is just my assumption of what you're trying.. hmm..
Please give us more details... edit your question if necessary..
//viewController.h
#interface viewController : UIViewController
#property (nonatomic) NSMutableArray *selectedIndexs;
// use this array as your dataSource for the secondTableView (popup tableView?)
#end
//viewController.m
#implementation viewController
// codes..
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
/*
you need to store the countries indexPath to cell the name/details
in this example i will just get the `.textLabel.text`
*/
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath/*current selected indexPath*/];
if ([self.selectedIndexs containsObject:cell.textLabel.text])
{
// [self.selectedIndexs removeObject:cell.textLabel.text]; ]> delete if already in the array
// return; ]> if you dont want to remove the object just return..
}
else
[self.selectedIndexs addObject:cell.textLabel.text]; // add the country if not exist in the array
}
- (void)showSelectedCountries:(UIButton *)sender //this is the number 1 from the image (Button)
{
UIView *customViewForSelected = [[UIView alloc] init];
// then if you have declared a property for `dataSource` inside your 'customViewForSelected' just:
customViewForSelected.yourSelectedsDataSource = selectedIndexs;
// then show the view with the table inside which contains only the table.. correct?
[self.view addSubview:customViewForSelected]; // i just added as subview because i dont know how you call it..
}
#end
Hope, you find this helpful.. Cheers..

I created a class: DropdownListViewCountries is subclass of UIView and include a button, a text field and a tableview.
#interface DropdownListViewCountries : UIView<UITextFieldDelegate, UITableViewDelegate>
.In #implementation DropdownListViewCountries, I implemented didSelectRowAtIndexPath
and then I created class: IndividualPersonalData is subview of UIViewController. This class contains a progress view bar.
And then I created an object of DropdownListViewCountries and add to IndividualPersonalData.
How to I touch on a cell of tableview to change progress view value?

UITableView had delegate protocol, and for your needs, you only have to implement
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
You will know where you tap by indexPath, so you know the location, then you can do whatever you want.
If you want to get the corresponding cell, use
- (UITableViewCell *)cellForRowAtIndexPath:(NSIndexPath *)indexPath
Simply pass indexPath from didSelectRowAtIndexPath to cellForRowAtIndexPath

Like people said in the comments, all you need is in the delegate and data source. If you're doing it all programmatically you need to add many things to your code such as <UITableViewDelegate, UITableViewDataSource> and also call them on viewDidLoad. Then you need to implement some methods. Apple has a vast documentation on all classes and methods, take a look at them. You need the didSelectRowAtIndexPath.
Hope I could help

Thanks you all for your help. Now for my purpose I found below code to detect events:
[self.firstDropdownListView.countriesTextField addObserver:self forKeyPath:#"text" options:0 context:nil];
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if([keyPath isEqualToString:#"text"] && object == self.firstDropdownListView.countriesTextField)
{
// text has changed
LogDebug(#"HELLO WORLD");
}
}

Related

How can I allocate and initialize a custom UITableViewCell NOT in cellForRowAtIndexPath?

I won't go into the WHY on this one, I'll just explain what I need.
I have a property in my implementatioon file like so...
#property (nonatomic, strong) MyCustomCell *customCell;
I need to initialize this on viewDidLoad. I return it as a row in cellForRowAtIndexPath always for the same indexPath, by returning "self.customCell".
However it doesn't work, I don't appear to be allocating and initializing the custom cell correctly in viewDidLoad. How can I do this? If you know the answer, save yourself time and don't read any further.
Ok, I'll put the why here in case people are curious.
I had a table view which was 2 sections. The first section would always show 1 row (Call it A). The second section always shows 3 rows (Call them X, Y, Z).
If you tap on row A (which is S0R0[Section 0 Row]), a new cell would appear in S0R1. A cell with a UIPickerView. The user could pick data from the UIPickerView and this would update the chosen value in cell A.
Cell X, Y & Z all worked the same way. Each could be tapped, and they would open up another cell underneath with their own respective UIPickerView.
The way I WAS doing this, was all these cell's were created in the Storyboard in the TableView, and then dragged out of the View. IBOutlets were created for all. Then in cellForRAIP, I would just returned self.myCustomCellA, or self.myCustomCellY.
Now, this worked great, but I need something more custom than this. I need to use a custom cell, and not just a UITableViewCell. So instead of using IBOutlets for all the cells (8 cells, 4 (A,X,Y,Z) and their 4 hidden/toggle-able UIPickerView Cell's), I created #properties for them in my implementation file. But it's not working like it did before with the standard UITableViewCell.
I'm not sure I'm initializing them correctly? How can I properly initialize them in/off viewDidLoad so I can use them?
.m
#interface MyViewController ()
#property (strong, nonatomic) MyCustomCell *myCustomCellA;
...
viewDidLoad
...
self.myCustomCellA = [[MyCustomCell alloc] init];
...
cellForRowAtIndexPath
...
return self.myCustomCellA;
...
If only I understood your question correctly, you have 3 options:
I would try really hard to implement table view data source with regular dynamic cells lifecycle in code and not statically – this approach usually pays off when you inevitably want to modify your business logic.
If you are certain static table view is enough, you can mix this method with overriding data source / delegate methods in your subclass of table view controller to add minor customisation (e.g. hiding certain cell when needed)
Alternatively, you can create cells using designated initialiser initWithStyle:reuseIdentifier: to instantiate them outside of table view life cycle and implement completely custom logic. There is nothing particular that you should do in viewDidLoad, that you wouldn't do elsewhere.
If you have a particular problem with your code, please post a snippet so community can help you
I suggest you to declare all your cells in storyboard (with date picker at right position) as static table and then override tableView:heightForRowAtIndexPath:
Define BOOL for determine picker visibility and its position in table
#define DATE_PICKER_INDEXPATH [NSIndexPath indexPathForRow:1 inSection:0]
#interface YourViewController ()
#property (assign, nonatomic) BOOL isPickerVisible;
#end
Then setup initial value
- (void)viewDidLoad {
[super viewDidLoad];
self.isPickerVisible = YES;
}
Override tableView delegate method
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
if ([indexPath isEqual:DATE_PICKER_INDEXPATH] && !self.isPickerVisible) {
return 0;
} else {
return [super tableView:tableView heightForRowAtIndexPath:indexPath];
}
}
And finally create method for toggling picker
- (void)togglePicker:(id)sender {
self.isPickerVisible = !self.isPickerVisible;
[self.tableView beginUpdates];
[self.tableView endUpdates];
}
which you can call in tableView:didSelectRowAtIndexPath:
According to your problem, you can create pairs (NSDictionary) of index path and bool if its visible and show/hide them according to that.
Here's what I was looking for:
MyCustomCell *cell = (MyCustomCell *)[[[UINib nibWithNibName:#"MyNibName" bundle:nil] instantiateWithOwner:self options:nil] firstObject];

How to save "state" of of UITableViewCell

using Objective-C
I have a table with cell that can be swiped (as example use this post with some changes in logic). Currently i can swipe cell and after flipping table it's "state" will be reset to "closed". I want to implement some "memory" mechanism for storing last state of each cell.
How I try to do it - in cell class create protocol, where implement 2 methods for getting current indexPath of cell and adding it to the NSMutableSet, and before presenting it, controller check if this cell already in collection - set required state.
I use separate .xib files for both viewController with tableView and tableViewCell.
Code - cell.h class file:
#protocol PEDoctorsViewTableViewCellDelegate
#optional
//buttonAction
- (void)buttonDeleteAction;
//method for closing cell with swipe
- (void)cellDidSwipedIn: (UITableViewCell*)cell;
//method for opening cell with swipe
- (void)cellDidSwipedOut:(UITableViewCell *)cell;
#end
#interface PEDoctorsViewTableViewCell : UITableViewCell <PEDoctorsViewTableViewCellDelegate>
#property (strong, nonatomic) id <PEDoctorsViewTableViewCellDelegate> delegate;
//method for protocol PEDoctorsViewTableViewCellDelegate
- (void)setCellSwiped;
#end
cell.m file
...
//add some code for handling reuse of cells
//fixing issue with not remembeering of cell's state
//close all cells during flipping tableView
- (void)prepareForReuse{
[super prepareForReuse];
//close all cells before showing
self.viewDoctorsNameView.frame = CGRectMake(0, 0, self.viewDoctorsNameView.frame.size.width, self.viewDoctorsNameView.frame.size.height);
}
And viewController with tableView class.m
#pragma mark - PEDoctorsViewTableViewCellDelegate
- (void)cellDidSwipedOut:(UITableViewCell *)cell{
NSIndexPath * currentOpenedCellIndexPath = [self.tableView indexPathForCell:cell];
[self.currentlySwipedAndOpenesCells addObject:currentOpenedCellIndexPath];
}
- (void)cellDidSwipedIn:(UITableViewCell *)cell{
[self.currentlySwipedAndOpenesCells removeObject:[self.tableView indexPathForCell:cell]];
}
- (void)buttonDeleteAction {
NSLog(#"delete action");
}
and check in - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath method like
if ([self.currentlySwipedAndOpenesCells containsObject:[self.tableView indexPathForCell:cell]]){
[cell setCellSwiped];
}
also this class have
//place to store set of opened cells
#property (strong, nonatomic) NSMutableSet * currentlySwipedAndOpenesCells;
initialized in - (void)viewDidLoad.
As result - i got all cells with reset position.
I try to change code in - (void)prepareForReuse method - position of cell changes according to my changes in method - this part works fine.
Try to check delegate method - all fire in exactly required moment.
Try to add some NSLog method before adding object in NSMuatbaleSet and before cell will be recreated (think maybe data lost) - result - during adding object in to set - all looks like OK:
During recreating - looks like OK too :
but not changes in table shown. I try to check what object are captured during saving -
and after small flip for starting recreating process got next:
as I understand - same cell now a new object. Also try to add some BOOL property to cell for controlling and comparing it's state - result the same - each time new cell.
Any idea what i'm doing wrong?

Calling methods when UITableViewCell is pressed

I would like to call a method when a UITableViewCell is selected/tapped. I could do it easily with a static table view, but it requires a UITableViewController which is not good for me in this case, therefore I'm using a normal vc.
I have 10 specified methods like this:
- (void) methodOne {
NSLog(#"Do something");
}
- (void) methodTwo {
NSLog(#"Do something");
}
....
And I would like to call the methodOne when the first cell was tapped, call the methodTwo when the second cell was tapped and so on..
As a first step I set the numberOfRowsInSection to return 10 cells, but have no idea how could I connect the selected cells with the methods. Is there any quick way to do it? It would be a dirty solution to create 10 custom cells and set the every method manually for the custom cells, and there is no free place for it.
You can create an array of NSStrings with method names in the order they should be called from their corresponding UITableViewCells.
NSArray *selStringsArr = #[#"firstMethod", #"secondMethod", #"thirdMethod];
Then create a selector in tableView:didSelectRowAtIndexPath: from the strings array and call it using performSelector:.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *selString = selStringsArr[indexPath.row];
SEL selector = NSSelectorFromString(selString);
if ([self respondsToSelector:#selector(selector)]) {
[self performSelector:#selector(selector)];
}
}
Of course, there are some limitations to using performSelector: which you can read here.
You can use this method for whenever any cell is tapped on the table view
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSInteger selectedRow = indexPath.row; //this is the number row that was selected
switch (selectedRow)
{
case 0:
[self methodOne];
break;
default:
break;
}
}
Use selectedRow to identify which row number was selected. If the first row was selected, selectedRow will be 0.
Don't forget to set the table view's delegate to your view controller. The view controller also has to conform to the UITableViewDelegate protocol.
#interface YourViewController : UIViewController <UITableViewDelegate, UITableViewDataSource>
As long as the table view has a data source and a delegate, it doesn't matter what kind of view controller it is on. All a UITableViewController really is is a UIViewController that already has a table view on it and is that table view's delegate and data source.

If a UITableViewCell has multiple buttons, where is the best place to handle touch events

I'm presenting a lot of data in format of a table with multiple columns. Almost each column has a button (up to 4 in total) and each row is a UITableViewCell.
How could I detect that the buttons were touched and where should I handle touch events of the buttons? I'm certain, it shouldn't be a didSelectRowAtIndexPath method though.
As soon as, I could detect which button was pressed, I would fetch the data in that particular row and manipulate it. So, I need to know the indexPath of the row, as well as what button was pressed on it.
You can subclass UIButton with two properties row and column and implement the logic below:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell"
forIndexPath:indexPath];
MyButton *button1 = (MyButton *)[cell viewWithTag:1];
button1.row = indexPath.row;
button1.column = 1; // view tag
[button1 addTarget:self
action:#selector(clickAction:)
forControlEvents:UIControlEventTouchUpInside];
// button2,button3...
return cell;
}
-(void)clickAction:(MyButton *)sender {
// now you can known which button
NSLog(#"%ld %ld", (long)sender.row, (long)sender.column);
}
Generalized undetailed answer:
Create UITableviewcell subclass, link cell ui elements to this class.
Add method configureWithModel:(Model*)model; //Model being the information you want the cell to represent
Manipulate that information or
If you need to manipulate the screen or other objects. You need to give the table view cell subclass a reference to the other objects when the cell is created. (in code or in storyboard or in nib).
how to handle button presses in ios 7: Button in UITableViewCell not responding under ios 7 (set table cell selection to none)
how to link a button: http://oleb.net/blog/2011/06/creating-outlets-and-actions-via-drag-and-drop-in-xcode-4/
If those four views are UIButton then you will receive the tap events on each button or if they are not UIButton then you should add UITapGestureReconiser on each of this views
Several options here. But I would do the following:
Adopt a Delegate Protocol in your custom cell class (see here: How to declare events and delegates in Objective-C?) . This will handle the target selector for the buttons. Pass this message back to your view controller with the sender. To detect which cell it was in do the following:
CGPoint buttonPosition = [sender convertPoint:CGPointZero toView:self.tableView];
CGRect senderFrame = CGRectMake(buttonPosition.x, buttonPosition.y, sender.frame.size.width, sender.frame.size.height);
From here you can decide what the do. Use the buttons .x coordinate to determine which button it was or specify a different tag for each button in cellForRowAtIndexPath:. Or if you want to grab the index path of the cell you can do:
NSArray *indexPaths = [YOUR_TABLE_VIEW indexPathsForRowsInRect:senderFrame];
NSIndexPath *currentIndexPath = [indexPaths lastObject];
Because each button has a different action, the only thing you need to get at runtime is the indexPath of the button. That can be done by looking at the button's superviews until a cell is found.
- (IBAction)action1:(UIButton *)button
{
UITableViewCell *cell = [self cellContainingView:button];
NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];
MyDataModel *object = self.objects[indexPath.row];
// perform action1 on the data model object
// Now that the data model behind indexPath.row was done, reload the cell
[self.tableView reloadRowsAtIndexPaths:#[indexPath]
withRowAnimation: UITableViewRowAnimationAutomatic];
}
- (id)cellContainingView:(UIView *)view
{
if (view == nil)
return nil;
if ([view isKindOfClass:[UITableViewCell class]])
return view;
return [self cellContainingView:view.superview];
}
There: no delegates, no tags, and the action doesn't care about the internals of the cell.
You will still want to subclass UITableViewCell with the four buttons (call them button1, button2, button3, and button4 if you don't have better names). You can make all the connection is Interface Builder. This will only be needed for populating object data into the cell during -tableView:cellForRowAtIndexPath:.
Ideally, you should create a custom cell by subclassing UITableViewCell and implement the actions for each of these buttons in that cell. If your view controller needs to know about these actions, you can define a protocol, MyCustomCellDelegate or similar, and have your view controller conformed to that protocol. Then MyCustomCell will be able to send messages to the view controller when user interacts with its buttons or other controls.
As in the example code below, you can create a cell in storyboard or nib and hook one of the button's action to firstButtonAction method of CustomTableCell class.
Also, you need to set your view controller as delegate property of CustomTableCell object created and implement the method buttonActionAtIndex: of CustomTableCellDelegate in your view controller class. Use controlIndexInCell param passed to this method to determine which button might have generated the action.
#protocol CustomTableCellDelegate <NSObject>
- (void) buttonActionAtIndex:(NSInteger)controlIndexInCell
#end
In CustomTableCell.h class
#interface CustomTableCell: UITableViewCell
#property (nonatomic, weak) id <CustomTableCellDelegate> delegate
- (IBAction) firstButtonAction:(id)sender
#end
In CustomTableCell.m class
#implementation CustomTableCell
#synthesize delegate
- (IBAction) firstButtonAction:(id)sender{
if ([delegate respondToSelector:#selector(buttonActionAtIndex:)])
[delegate buttonActionAtIndex:0];
}
#end
This is a personal preference on how I like to handle situations like these, but I would first subclass UITableViewCell because your table cells do not look like a default iOS UITableViewCell. Basically you have a custom set up, so you need a custom class.
From there you should set up your 4 IBActions in your header file
- (IBAction)touchFirstButton;
- (IBAction)touchSecondButton;
- (IBAction)touchThirdButton;
- (IBAction)touchFourthButton;
You do not need to pass a sender in these actions, because you will not be using that object in these methods. They are being created to forward the call.
After that set up a protocol for your UITableViewSubClass
#protocol UITableViewSubClassDelegate;
Remember to put that outside and before the #interface declaration
Give your sell a delegate property
#property (nonatomic, strong) id<UITableViewSubClassDelegate> delegate;
and finally define your actual protocol, you will need to set up 4 methods, 1 for each button and take your subclass as a parameter
#protocol UITableViewSubClassDelegate <NSObject>
- (void)forwardedFirstButtonWithCell:(UITableViewSubClass*)cell;
- (void)forwardedSecondButtonWithCell:(UITableViewSubClass*)cell;
- (void)forwardedThirdButtonWithCell:(UITableViewSubClass*)cell;
- (void)forwardedFourthButtonWithCell:(UITableViewSubClass*)cell;
#end
This will be placed outside of the #interface #end section at the bottom
After that create a configureWithModel: method in your #interface and #implementation as well as a property for your model
#interface:
#property (nonatomic, strong) Model *model;
- (void)configureWithModal:(Model*)model;
#implementation:
- (void)configureWithModal:(Model*)model {
self.model = model;
// custom UI set up
}
From here you should configure your action methods in your #implementation file to call the delegate methods, i'm only showing the first one, but you would do this with all of the IBActions
- (void)configureWithModal:(Model*)model {
[self.delegate forwardFirstButtonWithCell:self];
}
From here your custom cell set up is done and we need to go back to the UIViewController that is displaying the UITableView. First go into the header file of the view controller, and import your custom UITableViewCellSubClass and then setup the class to implement this protocol.
It should look something like this
#interface MYViewController : UIViewController <UITableViewSubClassDelegate>
from there you should go into your cellForRowAtIndexPath: method and configure your custom UITableViewCell
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCellSubClass *cell = [tableView dequeueReusableCellWithIdentifier:#"CellIdentifier"];
cell.delegate = self;
Model *cellModel = self.tableData[indexPath.row];
[cell configureWithModel:cellModel];
return cell;
}
Now go into your cell class and copy paste all of the protocol methods into your viewController class. I will display one as an example.
In your UIViewController:
- (void)forwardedFirstButtonWithCell:(UITableViewSubClass*)cell {
Model *cellModel = cell.model;
// do stuff with model from outside of the cell
}
do that for all methods and you should be good.
Remember to have all your #imports in so there's no forward declarations and remember to link up the IBActions to your storyboard or xib files. If you want a custom xib for your table cell you will have to check if the cell is nil and then allocate a new one, but if you are using prototype cells then this should be sufficient.
For simplicity sakes i put forwardFirstButtonWithCell: but i would encourage making the name something that describes what it's doing such as, displayPopOverToEnterData or something similar. From there you could even change the parameters of the delegate protocol methods to take models instead so instead of
- (void) displayPopOverToEnterDataWithCell:(UITableViewSubClass*)cell;
make it
- (void) displayPopOverToEnterDataWithModel:(Model*)model;
but, i don't know what type of information you need to access from the cell. So update these methods as you see fit.

tableView didSelectRowAtIndexPath doesn't work correctly

I have a really strange problem.
I have a UITableView in a view controller, I can customize all that I want it works. But I want that when I click on a row an other view appear (it is not difficult), so I have done that:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"didSelectRowAtIndexPath");
self.myFicheView.hidden=NO;
[tableView deselectRowAtIndexPath:indexPath animated:YES];
}
But when I click on a row the method is not called. Like it doesn't work I have tried many things and I have found something strange.
When I click briefly on the row nothing happen, but when I click during 3 seconds it work and when I slide my finger on the row too.
Somebody know what is the problem here ?
#interface MapViewController : UIViewController<UITableViewDelegate,UITableViewDataSource>
{
}
#property (strong, nonatomic) IBOutlet UITableView *myTableView;
here the connections inspector of my tableView
http://hpics.li/256d01e
I exactly have no idea how your xib looks like, assuming if the view is coming somewhere in front of your table view, try to bring your tableview in front to that unhidden view.
Assuming if you have put [tableView deselectRowAtIndexPath:indexPath animated:YES]; method only to unhide the selection then there is a property in xib which provides you this facility, by using this you can remove this method and try to run your code. Please give a snapshot of your xib or some of your implemented code for better clarification.
I think you will not call the delegate method
plz include this code to your viewDidLoad method
myTableView.delegate=self;
myTableView.dataSource=self;

Resources