call UITableView methods from UITouch - objective-c

I have a table with customs cells, which has subclass UITableViewCells and for control touches I use UITouch (code based on this tutorial http://gregprice.co.uk/blog/?p=280) I need inside touchesEnded method get index of cell which was touch.
I try:
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
//some code
UITableView *parentTable = (UITableView *)self.superview;
NSIndexPath *index = [NSIndexPath alloc];
index = [parentTable indexPathForSelectedRow];
NSLog(#"Call, x= %f, index=%d", xPos, index.row);
}
But always I've got index=0.
Whats I do wrong?

it seems like you are taking a very round about way to select a row in a UITableView. set your UITableView's delegate and data source to the class you are working in, then implement its protocol methods which you can find in the UITableViewDelegate and UITableViewDataSource reference. Once you have done that you can use this method to determine which row was selected in the table
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
}
maybe you already knew this but it sounds like you are new to iOS development and i dont see why you would want to manually do it that way.
and also as Dave Leverton's answer said, dont do an alloc like that

Not sure this is causing the problems but it's bad practice to call 'alloc' like you are here. I'd remove the line with alloc (As this is creating memory for an object you're not initialising or using) and just have the NSIndexPath object defined in-line:
UITableView *parentTable = (UITableView *)self.superview;
NSIndexPath *index = [parentTable indexPathForSelectedRow];
NSLog(#"Call, x= %f, index=%d", xPos, index.row);

Related

Couldn't set values in Custom Cell in UITableView from NSMutableArray

Help me to get rid of with this dilemma that occurred yet when I tried to dequeued the cell (Custom Cell).Below are some steps and Indents that I did with my Project.
The very first is I drag and drop a UITableView in my ViewController and add the ViewController.h doing after this
#interface ViewController : UIViewController <UITableViewDataSource,UITableViewDelegate>
Then I made a Custom Cell with 3 UILabels and change the height of the Cell to 65.
After that I made a property in ViewController.m
#property (nonatomic,strong) NSMutableArray *myTodoTitles;
Then in method(ViewDidLoad) I did.
myTodoTitles = [NSMutableArray arrayWithCapacity:10];
[myTodoTitles addObject:#"Go for ride"];
[myTodoTitles addObject:#"Do University Assignments"];
[myTodoTitles addObject:#"Watch Show"];
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:[self.myTodoTitles count]-1 inSection:1];
[self tableView:self.myTodoTable cellForRowAtIndexPath:indexPath];
After that I just did these things in my ViewController.m
#pragma mark - Table view data source
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
NSString *myIdentifier = #"TodoCell";
TodoCell *todoCell = (TodoCell*)[tableView dequeueReusableCellWithIdentifier:myIdentifier];
todoCell.todoTitleLabel.text = [self.myTodoTitles objectAtIndex:indexPath.row];
return todoCell;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return [myTodoTitles count];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
return 1;
}
But when I run the project it dequeued nothing.
Please help
Most likely that you have not connected your viewController to be the dataSource of your tableView. This could be done from Interface Builder or from the code. You can easily check it by adding self.myTodoTable.dataSource = self; at the very first of viewDidLoad method.
And also: what did you mean by `
[self tableView:self.myTodoTable cellForRowAtIndexPath:indexPath];`
in viewDidLoad ? Seems like you wanted to do
[self.myTodoTable reloadData];
There are to UITableView methods with similar name:
- (id)dequeueReusableCellWithIdentifier:(NSString *)identifier
and
- (id)dequeueReusableCellWithIdentifier:(NSString *)identifier
forIndexPath:(NSIndexPath *)indexPath
The first one will try to dequeue a reusable cell. If it returns nil you are responsible to create appropriate cell.
The latter one will always return a valid cell: you will have to register a certain class or NIB with that tableview beforehand though. Docs.
EDIT:
As ReDetection pointed out: first method will also return a valid cell as long as you had registered a proper class (or nib) with that tableview.
In your case that means that you should register TodoCell in viewDidLoad with your tableView.
If TodoCell is made without .xib:
[self.tableView registerClass:[ToDoCell class]
forCellReuseIdentifier:#"TodoCell"];
Or if it is made with .xib.
[self.tableView registerNib:[UINib nibWithNibName:#"TodoCell"
bundle:nil]
forCellReuseIdentifier:#"TodoCell"];
EDIT2:
Your code also seems to be missing the dataSource setting. Something like:
self.tableView.dataSource = self;
This will trigger initial reload.
You'd probably want to set a delegate (since your controller claims to adopt that protocol) in the same manner.

tableView numberOfRowsInSection calls tableView viewForHeaderInSection on iOS4

I got a big app containing a lot of dependencies. For this case I implemented a class called RootTableViewController to handle all the stuff that has to be done everytime a table view controller is required.
Now I discovered an endless loop and I dont know how to fix it. I got the following code in RootTableViewController:
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
NSString *sectionTitle = [self tableView:tableView titleForHeaderInSection:section];
int numbersOfRowInSection = [self.tableView numberOfRowsInSection:section];
if (numbersOfRowInSection > 0)
{
// ...
}
else
{
UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 320.0f, 28.0f)];
return view;
}
}
This works perfect on iOS 5 and iOS 6, but on iOS4 it causes an endless loop, because [tableView numberOfRowsInSection] is calling [tableView viewForHeaderInSection]. How can I fix this using the table view api? Its no solution for me to work with the [ count] of internal data arrays because I got a lot of table view controllers extending this RootTableViewController with different data sources.
This is simply not good style. You are supposed to subclass or rahter implement the related delegate method but you shoudl not call UITableView.numberofRowsInSection:
However, you have certainly implemented tableView:numberOfRowsInSection. Move all of its functionality to the new method myNumberOfRowsInSection: In there do the same. It is mainly a copy of your current numberOfRowsInSection.
Then here in your code sniplet call [self myNumberOfRowsInSection:...];
And within tableView:numberOfRowsInSection:section just do:
return [self myNumberOfRowsInSection:section];
Apply the same pattern to all delegate methods that you may want to call yourself. Move all its business logic into your own method and then only call your own method from the delegate method and from your own code.
If you want to get the number of rows in a section of the data source without accessing the internal data array, you could query the dataSource delegate for it, something like
int numbersOfRowInSection = [self.tableView.dataSource tableView:self.tableView numberOfRowsInSection:section];
(not compiler checked)

Indexpath of the button from UICollectionView

I tried to:
- (IBAction)delete:(UIButton*)sender{
NSIndexPath *indexPath = [self.collectionView indexPathForCell:(TourGridCell *)[[[sender superview]superview]superview]];
}
But NSLog shows that cell exist, but indexpath is nil.
OK, here it is:
- (IBAction)delete:(UIButton *)sender{
NSIndexPath *indexPath = nil;
indexPath = [self.collectionView indexPathForItemAtPoint:[self.collectionView convertPoint:sender.center fromView:sender.superview]];
}
Sometimes it's the simplest possible answer. I was having exactly the same problem as #Schmidt, but was frustrated to see that his answer was to use indexPathForItemAtPoint:, as though indexPathForCell: was somehow broken and couldn't be used as intended.
Then I tried his solution, and still had the same result: the index path was coming back nil.
Solution: the view controller's collectionView outlet wasn't connected to the UICollectionView instance in the NIB (on the storyboard). After making that missing connection, both methods (indexPathForCell: and indexPathForItemAtPoint) worked as expected.
I know other devs run into this problem sometimes, so take this as a reminder: the problem may not be your code, per se, but rather something in Interface Builder like an unconnected outlet (or just as confusing, an outlet that's connected to something which no longer exists).
Swift version of the answer
var indexPath: IndexPath? = nil
indexPath = collectionView.indexPathForItem(at: collectionView.convert(sender.center, from: sender.superview))
Another best way is to subclass the UIButton and add an NSIndexPath property to it.
When loading the cell in
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath method
add this statement.
yourCell.customButton.indexPath = indexPath;
And
- (IBAction)delete:(UIButton *)sender
{
NSIndexPath *indexPath = sender.indexPath;
}

UITableView Center Cell Details

I am trying to sync a UITableView with a UILabel to make sure they show the same data; of course, things will be different in the end, but for testing this is what I need to do.
See the arrow? I want that middle cell (from px44-88) to show the cell.textLabel.text in a UILabel when it is the "middle cell".
I tried using - (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath but I was having so many problems I figured I'd come here to ask if anyone has a better way of doing this. I'm not sure if it would make a difference or not but I am using NSFetchedResultsController to populate my UITableView.
UITableView is a subclass of UIScrollView, so probably you can use UIScrollViewDelegate
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
member to detect which cell is currently in the middle.
I.e. assuming you have a plain table with no sections, and tableView is an outlet for the table, label is an outlet for the label, than this will function in your controller will work:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
int row = (scrollView.contentOffset.y + tableView.frame.size.height / 2) / 44;
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:row inSection:0];
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
label.text = cell.textLabel.text;
}
Of course, you need to do some scrolling to make it work ;)
You can use visibleCells method to get layout of visible cells for non-plain table and use it to detect cell in the middle of the table.

addTarget:action:forControlEvents - UISwitch in TableView - sender ok, event always 0x0

Using the fantastic posts in this forum, I created a switch as a accessoryView in a tableView. When the switch is touched my action (switchChanged) is called. Only the sender has a valid value, the event is 0x0.
Adding target to the switchView:
[switchView addTarget:self action:#selector(switchChanged:forEvent:) forControlEvents:(UIControlEventValueChanged | UIControlEventTouchDragInside)];
The Action:
- (void) switchChanged:(id)sender forEvent:(UIEvent *)event {
if(event) {
NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:[[[event touchesForView:sender] anyObject] locationInView:self.tableView]];
IFDFlightlogFormQuestions *question = [self.resultsController objectAtIndexPath:indexPath];
NSLog(#"IFDNewFlightlogViewController_Pad:switchChanged Switch is %# xmlAttrib %#",[sender isOn],question.XmlAttrib);
[self.xmlResults setValue:([sender isOn])?#"true":#"false" forKey:question.XmlAttrib];
}
}
Using action:#selector(switchChanged: forEvent:) - Added space - no change.
Using action:#selector(switchChanged::) - removed forEvent - unrecognized selector.
My goal is to get the indexPath to the tableView so I can change the value in my dictionary.
My current workaround is to use just the sender information, but I would like the event information:
- (void) switchChanged:(id)sender forEvent:(UIEvent *)event {
UISwitch *switchView = (UISwitch *)sender;
UITableViewCell *cell = (UITableViewCell *)switchView.superview;
UITableView *tableView = (UITableView *)cell.superview;
NSIndexPath *indexPath = [tableView indexPathForCell:cell];
IFDFlightlogFormQuestions *question = [self.resultsController objectAtIndexPath:indexPath];
NSLog(#"IFDNewFlightlogViewController_Pad:switchChanged Switch is %# xmlAttrib %#",([sender isOn])?#"On":#"Off",question.XmlAttrib);
[self.xmlResults setValue:([sender isOn])?#"true":#"false" forKey:question.XmlAttrib];
}
Any pointers on getting the event information on this?
It is possible to trigger #selector by adding a target to the UISwitch.
[switchCtl addTarget:self action:#selector(action:) forControlEvents:UIControlEventValueChanged];
Another possibility is to set the tag property on the UISwitch to match the row of your tableview. If you use multiple sections as well, then you could come up with some solution to encode these in a single integer value. Keep in mind that with this approach, you need to update the tag every time you update the cell (tableView:cellForRowAtIndexPath:) since the row can change as cells are reused.
Unfortunately no forEvent: is sent from a UIControl action... only the first part which is why you are not seeing any value.
The way your workaround works is a suitable method.
Another way would be to subclass the UISwitch so it held a reference to the NSIndexPath, or even overrides
- (void)sendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event
to interrupt and send a custom event...