I have a UITextField in a customCell. I want to be able to get the text that user inputs in it. In my customCell.m file I can get the text that is inputed with
[self.aTextField addTarget:self action:#selector(aTextFieldEditing:) forControlEvents:UIControlEventEditingChanged];
but I cannot get the indexPath so I can save it to the NSMutableArray that holds all the UITextFields.
In my TableView I cannot get the didSelectRowAtIndexPath to run. I also cannot get the textFieldDidBeginEditing method to output anything. If it helps I have a TableView with sections.
My idea at the moment is to get the indexpath.row and indexpath.section from the tableView and save them as an extern so I can access it in the customCell.m
I would be grateful for any ideas or specific examples of how I could do this. Or a different cleaner way to accomplish what I want.
You should be able to get the index path using the -indexPathForRowAtPoint: method on UITableView.
You can do something like this (not tested):
- (void)aTextFieldEditing:(UITextField *)textField {
CGPoint convertedPoint = [textField.superview convertPoint:textField.center toView:self.tableView];
NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:convertedPoint];
// Use your index path...
}
Edit:
In the case your cell is receiving the edit events, you can always define a protocol and make your view controller a delegate of the cell. You could then call this delegate method from your aTextFieldEditing method and pass the cell. Now that you have the cell you can call -indexPathForCell on your table view. If none of this makes sense, look into the delegate pattern. It's very common in Cocoa/Cocoa Touch and is very well documented online.
Related
I'm getting an EXC_BAD_ACCESS error when using scrollToRowAtIndexPath in the viewWillAppear method. I searched for solutions and saw some old posts recommending to set delegate and table to nil (see code below), however when I set that I simply dont get anything loaded in my tableview.
I should say that this is part of a chat application where I want to show the last message entered first. Many thanks for any assistance with this.
Here's my viewWillAppear:
-(void)viewWillAppear:(BOOL)animated {
[self.table reloadData];
int lastRowNumber = [self.table numberOfRowsInSection:0] - 1;
NSIndexPath* ip = [NSIndexPath indexPathForRow:lastRowNumber inSection:0];
//self.table.delegate = nil;
//self.table = nil;
[self.table scrollToRowAtIndexPath:ip atScrollPosition:UITableViewScrollPositionTop animated:YES];
}
I should add that this code seems to work fine in other parts of my program, the only time I get the error is in the viewWillAppear method.
viewWillAppear: is too early to do any animation on view. Per Apple Documentation:
This method is called before the view controller's view is about to
be added to a view hierarchy and before any animations are configured
for showing the view. You can override this method to perform custom
tasks associated with displaying the view. For example, you might use
this method to change the orientation or style of the status bar to
coordinate with the orientation or style of the view being presented.
If you override this method, you must call super at some point in your
implementation.
So, you cannot add animations when even the view hierarchy is not set completely.
It may help you
NSIndexPath * lastIndex =[NSIndexPath indexPathForRow:yourContenetArray.count-1 inSection:0];
[self.yourTableView scrollToRowAtIndexPath:lastIndex atScrollPosition:UITableViewScrollPositionNone animated:YES];
Write your code in viewDidAppear method.As your table view is loaded
then after you should call the method scrollToRowAtIndexPath . Then
your animation will be performed.And if you want to use in
viewWillAppear , then You can try to reload your tableView and then
write code for scrollToRowAtIndexPath
You should probably use the viewDidLoad method for scrolling, and if your array count is 0 (empty array) it will throw an error. It is not possible to scroll to cell at index -1.
Very beginner obj-c question.
I have plain UITableView with two sections, but I am interested only in first section now. This section have four custom cells (inherited from standard UITableViewCell), and they have a UITextField's as a property.
I need to improve custom Input Accessory View with buttons "Next", "Previous"(for switch between textFields in tableview) and "Done" (dismissimg of keyboard). http://uaimage.com/image/62f08045
In -textFieldShouldReturn i set tags for textFields from 0 to 3. My next plan is to add textFields into NSMutableArray in -viewDidLoad and then just set and resign first responder for the textFields. Approximate code listing for "Next" button:
- (void) inputAccessoryViewDidSelectNext:(FDInputAccessoryView *)view {
for (UITextField *textField in [self textFieldsArray]) {
if ([textField isFirstResponder]) {
[textField resignFirstResponder];
UITextField *field = [[self textFieldsArray] objectAtIndex:textField.tag + 1];
[field becomeFirstResponder];
}
}
}
Questions:
Is this a right way or maybe there is a better approach to solve problem?
Do I need to tag textfields or use indexPath of cells in what they are built in? (or what is the best to track textFields?)
And the main question: what is the syntax to "get" textField from cell?
Sorry for the dumb questions, I am a very beginner.
Thanks,
Alex.
I think you have the right idea, but a few things come to mind:
Just to be safe, don't start with tag number 0. Every view has a tag number defaulted to 0, so you may get something unexpected.
Don't set the text view's tags inside of the textFieldShouldReturn, set the tags in cellForRowAtIndexPath or viewDidLoad, wherever you init the textFields.
Add the textFields to the cell's contentView, not the cell itself.
You don't have to resign first responder from the first text field, you can just becomeFirstResponder on the new one.
Make sure you're handling the last text view edge case: You could loop around to the first text field or simply dismiss the keyboard at the end.
If you want to get the textField in the cell:
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:
[NSIndexPath indexPathForRow:ROW_NUMBER inSection:1]];
UITextField *textField = (UITextField *)[cell.contentView viewWithTag:TEXT_FIELD_TAG];
Is there a way to get a reference to the view controller of my superview?
There were several instances that I needed this on the past couple of months, but didn't know how to do it. I mean, if I have a custom button on a custom cell, and I wish to get a reference of the table view controller that controls the cell I`m currently in, is there a code snippet for that? Or is it something that I should just solve it by using better design patterns?
Thanks!
Your button should preferably not know about its superviews view controller.
However, if your button really needs to message objects that it shouldn't know the details about, you can use delegation to send the messages you want to the buttons delegate.
Create a MyButtonDelegate protocol and define the methods that everyone that conforms to that protocol need to implement (the callback). You can have optional methods as well.
Then add a property on the button #property (weak) id<MyButtonDelegate> so that any class of any kind can be set as the delegate as long as it conforms to your protocol.
Now the view controller can implement the MyButtonDelegate protocol and set itself as the delegate. The parts of the code that require knowledge about the view controller should be implemented in the delegate method (or methods).
The view can now send the protocol messages to its delegate (without knowing who or what it is) and the delegate can to the appropriate thing for that button. This way the same button could be reused because it doesn't depend on where it is used.
When I asked this question I was thinking of, in a situation where I have custom cells with buttons on them, how can the TableViewController know which cell's button was tapped.
More recently, reading the book "iOS Recipes", I got the solution:
-(IBAction)cellButtonTapped:(id)sender
{
NSLog(#"%s", __FUNCTION__);
UIButton *button = sender;
//Convert the tapped point to the tableView coordinate system
CGPoint correctedPoint = [button convertPoint:button.bounds.origin toView:self.tableView];
//Get the cell at that point
NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:correctedPoint];
NSLog(#"Button tapped in row %d", indexPath.row);
}
Another solution, a bit more fragile (though simpler) would be:
- (IBAction)cellButtonTapped:(id)sender
{
// Go get the enclosing cell manually
UITableViewCell *parentCell = [[sender superview] superview];
NSIndexPath *pathForButton = [self.tableView indexPathForCell:parentCell];
}
And the most reusable one would be to add this method to a category of UITableView
- (NSIndexPath *)prp_indexPathForRowContainingView:(UIView *)view
{
CGPoint correctedPoint = [view convertPoint:view.bounds.origin toView:self];
return [self indexPathForRowAtPoint:correctedPoint];
}
And then, on your UITableViewController class, just use this:
- (IBAction)cellButtonTapped:(id)sender
{
NSIndexPath *pathForButton = [self.tableView indexPathForRowContainingView:sender];
}
If you know which class is the superview of your view controller, you can just iterate through the subviews array and typecheck for your superclass.
eg.
UIView *view;
for(tempView in self.subviews) {
if([tempView isKindOfClass:[SuperViewController class] ])
{
// you got the reference, do waht you want
}
}
I have not yet found any really good examples on how to do this. There is an image that I want to use as the accessory button and when I put it in and click on it doesn't work. So it looks correct but doesn't work...
Here is my code:
[cell setAccessoryType:UITableViewCellAccessoryDetailDisclosureButton];
cell.accessoryView = [[UIImageView alloc]
initWithImage:[UIImage imageNamed:#"TableView_Green_Disclosure.png"]];
So how do I get my UIImageView to call accessoryButtonTappedForRowWithIndexPath whenever it is tapped?
A thorough reading of accessoryView and accessoryType would reveal that they are mutually exclusive ways to customize a cell.
Setting the accessoryType will cause the table view delegate method to be called when it is tapped.
Setting the accessoryView will ignore the setting of accessoryType and give you something to display. If you want to receive a callback from the custom view you've put in place, it should be a control that is wired up to do so. (Or any view with a gesture recognizer.)
If you use a button, and set its action to accessoryTapped:, you will receive the button as the "sender" argument. You can walk up the view hierarchy until you find a table view cell, and then ask your table view what the indexPath of that cell is. This will then get you an index into your model objects and you be able to act on it appropriately.
Alternate to the button, you can enable interaction on the UIImageView above, and add a gesture recognizer to it.
To make the button actually do something, you'll need to implement - (void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath from UITableViewDelegate.
When an accessory button is tapped in a row, this method will be called and you'll have the chance to act appropriately using the passed in index path to determine which row's accessory was tapped.
Check the blog post hdr->cmdline for creating custom accessory view for UITableView.
The author used UIButton objects with images for custom accessory view.
To make use of the accessoryView - you would need to set the cell's accessoryType to UITableViewCellAccessoryNone deposit a UIButton (with associated image) into the cell and then wire it up to receive user touches. You might use something like the code below as the IBAction response to the cell's UIButton being touched:
- (IBAction) accessoryButtonPressed:(id) sender
{
NSUInteger pathInts[] = { 0,0 };
pathInts[1] = self.currentselectedrow; // ivar set when tableview row last selected
NSIndexPath* indexPath = [NSIndexPath indexPathWithIndexes:pathInts length:2];
[self tableView:mytableview accessoryButtonTappedForRowWithIndexPath:indexPath];
}
The UIButton would be wired to execute this glue code by way of a line inside your tableview's "cellForRowAtIndexPath:" function
[thecell setButtonTarget:self action:#selector(accessoryButtonPressed:)];
One thing I noticed is that the UIButton seems to want a 'swipe right' versus a simple 'tap' touch in order to trigger the event - but it could be my beta iOS that's the problem. Note that I had added a UIButton* object named 'cell_accessoryButton' to the Custom Cell source.
In the cell's source you'd support the 'setButtonTarget' call with code like this:
- (void) setButtonTarget:(MyViewController*)inTarget action:(SEL) inAction
{
[self.cell_accessoryButton addTarget: inTarget
action: (SEL) inAction
forControlEvents: UIControlEventTouchUpInside];
}
It's so much easier to just use the accessoryType reference and let iOS do the heavy lifting - but, if you want a custom graphic, etc - this is another path that works.
I've been working on a menubar note-taking application for Mac. It is written in Objective-C and Cocoa, and I'm using BWToolkit with it. My problem is getting keyDown: and mouseDown: events in a BWTransparentTableView which is a subclass of NSTableView. I just can't get it to work. I've tried searching the Internet, and some places say that you must subclass NSTableView. I've tried that, but it still doesn't work. I am pretty new to Objective-C and Cocoa, and I may just be doing something incorrectly.
Items in an NSTableView will automatically begin editing when they are slow double-clicked or when the Return key is pressed. Make sure that the table view, the cell and the array controller (if used) are marked as editable.
If you are not using an NSArrayController, make sure that your table view has a delegate and that it responds to tableView:shouldEditTableColumn:row:.
To handle a double click, you just need to set the doubleAction of the table view:
- (void)awakeFromNib
{
[tableView setTarget:self];
[tableView setDoubleAction:#selector(doubleClickInTable:)];
}
- (void)doubleClickInTable:(id)sender
{
NSInteger rowIndex = [sender selectedRow]; //Use selectedRowIndexes if you're supporting multiple selection
//Handle the double click
}
Note that neither of these methods require you to subclass NSTableView.