I have UITableView which uses a custom UITableViewCell containing a UITextField.
I want to call a method (myMethod) when it is clicked (UIControlEventTouchDown) and attempted to wire this up inside the UITableView delegate method cellForRowAtIndexPath by doing the following:
[tf addTarget:self action:#selector(myMethod) forControlEvents:UIControlEventTouchDown];
When the UITextField is clicked, nothing happens.
I attempted to do the exact same thing for another UITextField outside of the UITableView:
[othertf addTarget:self action:#selector(myMethod) forControlEvents:UIControlEventTouchDown];
When I click on othertf the method is called as I would expect.
I'm a bit confused as the code is identical apart from I've swapped tf for othertf.
Below is the complete code for cellForRowAtIndexPath:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *DetailCellIdentifier = #"DetailFieldView";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:DetailCellIdentifier];
if (cell == nil) {
NSArray *cellObjects = [[NSBundle mainBundle] loadNibNamed:DetailCellIdentifier owner:self options:nil];
cell = (UITableViewCell*) [cellObjects objectAtIndex:0];
}
cell.selectionStyle = UITableViewCellSelectionStyleNone;
UITextField *tf = (UITextField *)[cell viewWithTag:2];
tf.text = #"some value";
[othertf addTarget:self action:#selector(myMethod) forControlEvents:UIControlEventTouchDown];
[tf addTarget:self action:#selector(myMethod) forControlEvents:UIControlEventTouchDown];
return cell;
}
Can anyone spot what I'm doing wrong? It's probably something simple as I am new to iOS development.
Use UITextField delegate Methods:
UITextField delegate
//Use this method insted of addTarget:
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField {
if (textField == tf) {
[self myMethod];
return NO;
}
return YES;
}
and dont forget to set the delegate to the textField:
tf.delegate = self;
When you touch a UITextField it swallows the touch itself and tells the keyboard to appear etc. Instead use the following events:
UIControlEventEditingDidBegin
UIControlEventEditingDidEnd
UIControlEventEditingChanged
Further improvements
Tags is a rather loose way of coupling your NIB and your code. Consider Making a DetailCell.h and DetailCell.m file, and set the root view in the NIB file to the DetailCell class, and create reference outlets for all the views you need to access in code, and action outlets for all the actions you need to react on.
This is done by CTRL clicking on a view in the interface builder, and dragging it into the DetailCell.h file. Interface Builder will now ask you wheter you want to create an action or a reference outlet (References are basically pointers, and actions are basically events)
dequeueReusableCellWithReuseIdentifier now calls initWithFrame on your DetailCell class every time is creates a new instance of it. It is now your job to load the NIB file in this function.
Alternatively you could register a NIB file for cell reuse on the tableview with
[tableview registerNib:[UINib nibWithNibName:DetailCellIdentifier
bundle:nil] forCellWithReuseIdentifier: DetailCellIdentifier];
which the automatically loads the nib file every time a new cell is created. In >= iOS 5 (AFAIK) dequeueReusableCellWithReuseIdentifier never returns nil, but creates instances of the registered classes/nib files itself every time.
Related
I'm learning UITableView in Objective-C. Could you hint me how to access UIButton inside UITableviewHeaderFooterView subclass from my UIViewController class? Programatically, as I don't use IB.
Full code: https://gist.github.com/tomnaz/3d790b308d305af8b98c
[[??? btnEdit] addTarget:self
action:#selector(addNewItem:)
forControlEvents:UIControlEventTouchUpInside];
Don't do this in viewDidLoad, do it in viewForHeaderInSection: where you have a pointer to your header view.
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
static NSString *headerReuseIdentifier = #"TableViewSectionHeaderViewIdentifier";
ItemsHeaderView *sectionHeaderView = [tableView dequeueReusableHeaderFooterViewWithIdentifier:headerReuseIdentifier];
[sectionHeaderView.btnEdit addTarget:self action:#selector(addNewItem:) forControlEvents:UIControlEventTouchUpInside];
return sectionHeaderView;
}
When you initially create the buttons, you can store them in a property or array so you can easily find them later.
Alternatively, you could set a tag on the button, and then call viewWithTag: on your UITableviewHeaderFooterView subclass to find the button.
I want to know that how to get selected cell's row index on selection of cell. But how to do it without usingUITableViewDelegate methods.
Please tell me.
I have search lot but not getting solution so please if anyone know tell me.
Please shared the viewpoints regarding it.
Thanks in Advance...!!!
In that case, your interviewer wanted to know how can you implement the delegation yourself...
To achieve that, Create a custom class "YourTableViewCell" extended fromUITableViewCell and use this class object to be returned in -tableView:cellForRowAtIndexPath:
Write a protocol "CellSelectionProtocol" with a method -
-(void) cellSelected: (YourTableViewCell *) cell;
Now delegate this protocol to your ViewController that has your TableView
and define the implementation of the method as below -
-(void) cellSelected: (YourTableViewCell *) cell
{
NSIndexPath *selectedIndexPath = [_yourTableView indexPathForCell: cell];
}
My answer would be this if it was an interview, and I am pretty sure it would be accepted.
But for a good architecture... the protocol & delegate implementation should be in two levels, like ->
YourTableViewCell -> delegates -cellSelected: -> YourTableView -> delegates -tableView:didSelectRowAtIndexPath: -> YourViewController
Please see: Your interviewer just wanted to know how you create delegations manually, instead of using default UITableViewDelegates.
EDIT # Unheilig
I mean in 2 levels because, the selection of a UITableViewCell has to be delegated to the UITableView and not directly to the controller for the following reasons
UITableViewCell is subview of UITableView
There can be multiple UITableView in a controller. So if you delegate cell selection directly, how will you tell the controller that cell got selected for which UITableView object?
Also UITableView might have to do other processing with other UITableViewCell, Like if selected and changes backgroundColor, the previous selected should get deselected and get default backgroundColor. Or add to the array of selected cells, if multiple selection is enabled.
There are many such similar architectural necessities that make me say - "But for a good architecture... the protocol & delegate implementation should be in two levels, like ->"
I hope that is pretty explanative now...
There is no way to get selected cell row index with out using tableview delegate methods.
when you click on tableview, didSelectRowAtIndexPath called and get the tableview cell index.
There is one way to do this, but it is not correct procedure to get tableview cell index. Create a button on tableviewcell and pass the indexvalue as sender tag to button action. But need to click only on that button.
Answer edited:
Create a transparent button on tableview cell in cellForRowAtIndexPath method and pass the cell index to button tag.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] ;
}
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button.frame = CGRectMake(0, 0, cell.frame.size.width, cell.frame.size.height);
button.backgroundColor = [UIColor colorWithWhite:1.0 alpha:0.0];
[button addTarget:self action:#selector(buttonClicked:) forControlEvents:UIControlEventTouchUpInside];
button.tag = indexPath.row;
[cell addSubview:button];
cell.textLabel.text = [NSString stringWithFormat:#"%#",[numberArray objectAtIndex:indexPath.row]];
return cell;
}
-(void)buttonClicked:(id)sender
{
NSLog(#"%ld",(long)[sender tag]);
}
I have a view controller
in that having a table view in that a Custom cell.
I have a button in a custom cell
MyViewController
---View
------TableView
-------------Custom cell
-------------------UIButton
I want to implement the button action for that button in the custom cell, in the Custom cell class.
I want to present another viewcontoler called mailPage by clicking on button for thet
-(IBAction)webButtonClicked:(id)sender
{
[self presentModalViewController:mailpage animated:YES];
}
But here self means CustomCell, even i tried with superview i didn't get my view contoller to represent in place of self
I tried like this but no use.
MyViewController *myViewController =self.superview
How to get my View controler containing the current custom cell
I would strongly recommend you place your view controller presentation logic in a view controller, rather than a UITableViewCell.
As you're already using a custom cell, this will be fairly straightforward. Simply define a new protocol for your custom cell, and have your view controller act as a delegate. Or alternatively, as per this answer here you can forgo the delegate entirely, and simply have your view controller act as the target for your button.
Your custom UITableViewCell really shouldn't have any dependencies or knowledge of the view controller it's being displayed in.
well the easy way is to set an unique tag for the button in the cell and in cellForRowAtIndexpath method you can get the button instance as
UIButton *sampleButton=(UIButton *)[cell viewWithTag:3];
and set the action as
[sampleButton addTarget:self action:#selector(sampleButtonPressed:) forControlEvents:UIControlEventTouchUpInside];
and set up the action in the viewcontroller
-(void)sampleButtonPressed:(id)sender
{
}
try this:
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"OpenHouseListCustomCell";
OpenHouseListCustomCell *cell = (OpenHouseListCustomCell *)[tblOpenHouses dequeueReusableCellWithIdentifier:CellIdentifier];
if(cell == nil) {
NSArray* nib = [[NSBundle mainBundle] loadNibNamed:#"OpenHouseListCustomCell" owner:self options:nil];
cell = [nib objectAtIndex:0];
cell.showsReorderControl = NO;
cell.selectionStyle = UITableViewCellSelectionStyleNone;
cell.backgroundColor=[UIColor clearColor];
[cell.btn1 addTarget:self action:#selector(ButtonClicked:) forControlEvents:UIControlEventTouchUpInside];
}
cell.btn1.tag = indexpath.row;
return cell;
}
-(void) ButtonClicked {
//your code here...
}
I have an array of NSStrings, one UILabel & a UICollectionView.
My Question:
I want the array's count to determine how many UICollectionViewCell's there are.
Each UICollectionViewCell contains a button. Upon click, I want this button to cause the data in the array that corresponds to the UICollectionViewCell's number to be displayed in the label.
For example, if the user clicks on the 13th UICollectionViewCell's button, then the 13th NSString in the array would become the UILabel's text.
What I have done:
I have made my own subclass of UICollectionViewCell for the nib file that I use for all of the UICollectionViewCells, & connected the button to the .h file as a IBAction. I have also imported the MainViewController.h, which is the one that contains the array property that stores the NSStrings.
When I edit the code in the UICollectionViewCell's action, I cannot access the array property. The button does work - I placed an NSLog in the IBAction's method, which does work.
I have searched through tens of other answers on SO, but none answer my specific question. I can update this with samples of my code if requested.
I have made my own subclass of UICollectionViewCell for the nib file
that I use for all of the UICollectionViewCells, and connected the
button to the .h file as a IBAction.
If you connect the IBAction to the subclass of collectionViewCell you would need to create a delegate to make the touch event available in the viewController where you are displaying the data.
One easy tweak is to add the button the collectionViewCell, connect it's IBOutlet to the cell. But not IBAction. In the cellForRowAtIndexPath: add an eventHandler for button in that viewController containing collectionView.
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
//Dequeue your cell
[cell.button addTarget:self
action:#selector(collectionViewCellButtonPressed:)
forControlEvents:UIControlEventTouchUpInside];
return cell;
}
- (IBAction)collectionViewCellButtonPressed:(UIButton *)button{
//Acccess the cell
UICollectionViewCell *cell = button.superView.superView;
NSIndexPath *indexPath = [self.collectionView indexPathForCell:cell];
NSString *title = self.strings[indexPath.row];
self.someLabel.text = title;
}
Please try like this..
In YourCollectionViewCell.h
Create an IBOutlet not IBAction called button for the UIButton that you added to the xib. Remember you should connect the outlet to the cell object not to the file owner in the xib.
MainViewController.m
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
cell.button.tag = indexPath.row;
[cell.button addTarget:self action:#selector(buttonPressed:) forControlEvents:UIControlEventTouchUpInside];
return cell;
}
-(void)buttonPressed:(UIButton*)sender
{
NSLog(#"%d : %#",sender.tag,[array objectAtIndex:sender.tag]);
self.textLabel.text = [array objectAtIndex:sender.tag];
}
Edit- Handle multiple sections
-(void)buttonPressed:(UIButton*)sender
{
NSIndexPath *indexPath = [self.collectionView indexPathForCell: (UICollectionViewCell *)sender.superview.superview];
NSLog(#"Section : %d Row: %d",indexPath.section,indexPath.row);
if (0 == indexPath.section) {
self.textLabel.text = [firstArray objectAtIndex:indexPath.row];
}
else if(1 == indexPath.section)
{
self.textLabel.text = [secondArray objectAtIndex:indexPath.row];
}
}
When I edit the code in the UICollectionViewCell's action, I cannot access the array property.
That's because you connected the button action to the "wrong" object. It needs to be connected to the MainViewController (or whoever it is that does have access to the array property).
You are going to have several tasks to perform:
Receive the button action message.
Access the array (the model for the data).
Throw a switch saying which cell should now have its label showing.
Tell the collection view to reloadData, thus refreshing the cells.
All those tasks should most conveniently belong to one object. I am presuming that this is MainViewController (and thus I am presuming that MainViewController is the delegate/datasource of the collection view).
So i've been looking at using UITableView's registerNib: and [dequeueReusableCellWithIdentifier: forIndexPath:] for loading up a custom UITableCellView from a NIB. Here are the important bits from my controller:
- (void)viewDidLoad
[super viewDidLoad];
self.tableView.bounces = NO;
[self.tableView registerNib:[UINib nibWithNibName:#"ProgramListViewCell" bundle:nil] forCellReuseIdentifier:#"Cell"];
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
TVProgramListTableViewCell *cell = (TVProgramListTableViewCell *)[tableView dequeueReusableCellWithIdentifier:#"Cell" forIndexPath:indexPath];
cell.frame = CGRectMake(0, 0, CELLWIDTH, OPENCELLHEIGHT);
cell.selectionStyle = UITableViewCellSelectionStyleNone;
cell.clipsToBounds = YES;
cell.titleLabel.text = [NSString stringWithFormat:#"herpa derp: %i", indexPath.row];
return cell;
So i'm registering the NIB when the view loads and then utilizing it for cell dequeuing. Up to this point everything is working like i expect it to. My custom TVProgramListTableViewCell is being properly loaded from the NIB and it's IBOutlet is being wired up.
The NIB contains a view with a button in it that i would like to have fire events to the controller. I can set the File's Owner type as my Table View Controller class but i do not know how to actually wire up the File's Owner.
Now if i was using loadNibNamed:, and loading the NIB myself, wiring up the File's Owner would be easy. Is there any way to achieve this while utilizing registerNib? Other than not being able to wire up the File's Owner this seems like the perfect way of using custom cells in a UITableView.
As far as I know, there's no way to set the file's owner to your table view controller and connect up an action method to a button in a xib file -- I've tried that, and it crashes the app. The way this is normally done is to call addTarget:action:forControlEvents: on your button in the cellForRowAtIndexPath method, and pass self as the target.