I have a UIViewController which contains a UITableView and conforms to UITableViewDelegate and UITableViewDatasource. I'm designing my UITableViewCell as a prototype cell in Storyboard. The cell has a subview called ViewA. When the user pans over a cell, I'd like ViewA to move with the gesture. I have two related questions:
Part 1
Should I add the UIPanGesture to the tableviewor to the cell?
Part 2
If I should add it to the cells, should I do this in - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath?
Part 3
If I want to get the cell and then move ViewA, how would I go about doing this within my pan function? I currently have:
- (void)didPan:(UIPanGestureRecognizer *)recognizer {
CGPoint velocity = [recognizer velocityInView:self.view];
CGPoint translation = [recognizer translationInView:self.view];
if (recognizer.state == UIGestureRecognizerStateEnded) {
CGPoint swipeLocation = [recognizer locationInView:self.tableview];
NSIndexPath *swipedIndexPath = [self.tableview indexPathForRowAtPoint:swipeLocation];
UITableViewCell *cell = [self.tableview cellForRowAtIndexPath:swipedIndexPath];
} else {
}
}
Yu have to make a custom class for UITableViewCell and add the panGestureRecognizer to the cell here in custom class only.
There is an excellent tutorial over here
http://www.raywenderlich.com/62435/make-swipeable-table-view-cell-actions-without-going-nuts-scroll-views
Hope this helps.
Related
Suppose that I have a custom UITableViewCell contains UIButton control. When the user presses the button it should present another view controller with the appropriate info according to which cell was chosen (to be more precise, I have an array of objects used to represent information in the UITableView and I want to transfer this info to the next view controller). The question is how can I detect button on which cell was exactly selected?
Thanks in advance.
There are many ways of doing this.. but the way of superView is no more available since iOS8. I am sharing a code which is working perfect in all iOS.
Write this selector on your cellForRow method
[cell.btnOnCell addTarget:self action:#selector(btnAction:event:) forControlEvents:UIControlEventTouchUpInside];
Now write this method
-(void)btnAction: (UIButton *)sender event:(id)event
{
NSSet *touches = [event allTouches];
UITouch *touch = [touches anyObject];
CGPoint currentTouchPosition = [touch locationInView:self.tblView];
NSIndexPath *indexPath = [self.tblView indexPathForRowAtPoint:currentTouchPosition];
NSLog(#"%li",(long)indexPath.row);
}
Try this:
Insert below code in cellForRowAtIndexpath
[cell.yourbuttonName addTarget:self action:#selector(yourbuttonNamePressed:) forControlEvents:UIControlEventTouchDown];
paste the below code as separate method
- (void)yourbuttonNamePressed:(UIButton *)sender
{
CGPoint swipepoint = sender.center;
CGPoint rootViewPoint = [sender.superview convertPoint:swipepoint toView:self.goalsTable];
NSIndexPath *indexPath = [self.goalsTable indexPathForRowAtPoint:rootViewPoint];
}
Simple solution is that when you configure cell in CellForRowAtIndexPath there you need to set the cell.button.tag=100+indexPath.row.
-(void)btnClicked:(UIButton *)sender
{
NSIndexPath* path= [NSIndexPath indexPathForRow:sender.tag-100 inSection:0];
UITableViewCell*cell=(UITableViewCell *)[tblView cellForRowAtIndexPath:path];
}
NOTE: you need to remove addTarget and buttonClicked implementation from UITableviewcell
and in cellForRowAtIndexPath method addtarget to button and in this class implemnt buttonClicked Method.**
I'm a beginner to making iPhone app with Xcode6. (and excuse me non a native English speaker...)
setting up UITableView with customized cells (as another class, i.e. MyCustomCell)
I would like to show/hide a component (UIView) in customized cell class, when a cell in UITableView has a longpress (longTap) but stacked.
I could not know how to control (recognise) the target component on the same cell in customized cell class with indexPath (or something) some part of programs are as follows,
MyCustomCell.h
#interface MyCustomCell : UITableViewCell
#property (weak,nonatomic) IBOutlet UIView *customPanel;
UIViewController.m
#import “MyCustomCell.h"
********************************
- (void)viewDidLoad {
[super viewDidLoad];
[_tableView registerNib:[UINib nibWithNibName:#“MyCustomCell" bundle:nil] forCellReuseIdentifier:#"cell"];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath: (NSIndexPath *)indexPath
{
MyCustomCell *cell = [tableView dequeueReusableCellWithIdentifier:#"cell"];
return cell;
}
-(void)longtapAction:(UILongPressGestureRecognizer *)gestureRecognizer {
CGPoint p = [gestureRecognizer locationInView:self.tableView];
NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:p];
if (indexPath == nil){
}else if (gestureRecognizer.state == UIGestureRecognizerStateBegan){
// I would like to change the hidden status of a UIVIEW in the CustomCell like
{self.customcell.custompanel:(indexPath).hidden = YES;}
}
}
Would you please let me know how to go over this trap ?
You need to create and assign a gestureRecognizer in to a cell:cellForRowAtIndexPath
UILongPressGestureRecognizer * recognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(longTapAction:)];
recognizer.delegate = self;
[cell addGestureRecognizer:recognizer];
You can add the gesture-recogniser to the UITableView itself and then extract the target UITableViewCell on which the gesture was performed using something like this or this.
I have few UI objects in the cell that have got gesture recognizer instance. I need to get cell where pressing object is located. I have the method below for getting it, but it just work before iOS 7:
UITableViewCell *cell = (UITableViewCell *)[[[sender view] superview]superview];
for iOS 6 it return UITableViewCell
for iOS 7 it return UITableViewCellScrollView
I think the new cell has some additional views in iOS 7, that's why I grab UITableViewCellScrollView instead of UITableViewCell as before.
The best way to get a table view cell from it's subview is to convert the subview's location to a location in the table view, then ask the table view for the index path of the cell at that point:
CGPoint subviewPosition = [sender convertPoint:CGPointZero toView:self.tableView];
NSIndexPath* indexPath = [self.tableView indexPathForRowAtPoint:subviewPosition];
Then, you can get the cell for that index path:
UITableViewCell* cell = [self.tableView cellForRowAtIndexPath:indexPath];
As you can see, relying on the view hierarchy is not a good approach - Apple can break it at any time.
You should use a delegate protocol to connect your cell to the controller.
CGPoint subviewPosition = [sender convertPoint:CGPointZero toView:self.tableView];
NSIndexPath* indexPath = [self.tableView indexPathForRowAtPoint:subviewPosition];
UITableViewCell* cell = [self.tableView cellForRowAtIndexPath:indexPath];
NSIndexPath *path=[self.tableView indexPathForCell:cell];
I have a UIImageView that I am able to successfully move over top of a UITableView. Both views (i.e. the UIImageView, and the UITableView) are subviews of the parent view of the viewController. The UIImageView I am moving using a UIPanGestureRecognizer object which then calls the method panGestureDetected. My method for panGestureDetected looks like this:
- (void)panGestureDetected:(UIPanGestureRecognizer *)recognizer {
_startLocation = [recognizer locationInView:_imageView];
NSLog(#"The point is: %d", _startLocation);
int selectedRow = [_table indexPathForSelectedRow].row;
NSLog(#"The current row is: %d", selectedRow);
CGPoint newCenter = _imageView.center;
newCenter.y = [recognizer locationInView:[_imageView superview]].y;
_imageView.center = newCenter;
}
The user can drag the UIImageView up and down over top of the UITableView. However, what I would like to do is have the UIImageView "link" or "connect" with whichever UITableViewCell/row that the UIImageView is covering, or closest to. If the UIImageView is in between two rows, the UIImageView should move to whichever row it is closest to on its own. Here is an image of what I am talking about:
In this image, the UIImageView is closer to row 1 than row 2, so after the user removes his/her finger, the UIImageView will simply move up a little bit more, and cover up row 1, making row 1 the "active row" so to speak.
At the moment, the movement of my UIImageView, and my UITableView are completely independent of one another, but when it comes to this movement where the user is dragging the UIImageView, I want there to be a connection. Can anyone show me how to do this?
Use the UITableView method indexPathForRowAtPoint: to determine which row at which the user stopped moving the image. Then cellForRowAtIndexPath to get the cell, then the cell's center, then set the UIImageView center to the cell center. Something like:
NSIndexPath *indexPath = [myTableView indexPathForRowAtPoint:touchPoint];
UITableViewCell *cell = [myTableView cellForRowAtIndexPath:indexPath];
myUIImageView.center = cell.contentView.center;
You could also animate the last statement so it moves to over the cell smoothly rather than jumping. Something like:
[UIView animateWithDuration:0.25 animations:^{
myUIImageView.center = cell.contentView.center;
}];
Inside of a gesture recognizer handler:
-(void)myGestureRecognizerHandler:(UIPanGestureRecognizer *)gestureRecognizer{
CGPoint touchPoint = [gestureRecognizer locationInView:myTableView];
NSIndexPath *indexPath;
UITableViewCell *cell;
switch ([gestureRecognizer state]) {
case UIGestureRecognizerStateBegan:
// Do stuff for UIGestureRecognizerStateBegan...
case UIGestureRecognizerStateChanged:
// Do stuff for UIGestureRecognizerStateChanged, e.g.,
myUIImageView.center = touchPoint;
case UIGestureRecognizerStateEnded:
indexPath = [myTableView indexPathForRowAtPoint:touchPoint];
cell = [myTableView cellForRowAtIndexPath:indexPath];
{
[UIView animateWithDuration:0.25f animations:^{
myUIImageView.center = cell.contentView.center;
}];
}
break;
case UIGestureRecognizerStateCancelled:
case UIGestureRecognizerStateFailed:
default:
// Do stuff for cancelled/failed/...
break;
}
}
You can use the following methods to help you out.
- (NSIndexPath *)indexPathForRowAtPoint:(CGPoint)point
- (CGRect)rectForRowAtIndexPath:(NSIndexPath *)indexPath
- (NSArray *)indexPathsForRowsInRect:(CGRect)rect
basically, whenever you finish moving the imageview, I would recommend grabbing a cell with indexPathForRowAtPoint using the new center of your image view. Then you can grab the frame for that cell with rectForRowAtIndexPath and then center your imageView in that frame.
I have UITableView that contains many cell. User can expand cell to see more content in this cell by push the expand button in this cell (only 1 cell can expand at time):
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if(selectedRowIndex == indexPath.row) return 205;
else return 60;
}
In the storyboard, I drag UILongPressGesture into cell button and named it longPress (cell is custom, it has 2 buttons in it, 1 need to recognize LongPressGesture, the other expand cell height):
#property (retain, nonatomic) IBOutlet UILongPressGestureRecognizer *longPress;
And in the viewDidLoad:
- (void)viewDidLoad
{
[longPress addTarget:self action:#selector(handleLongPress:)];
}
It's work perfectly, however when I use following code to recognize cell indexPath, it's wrong when one cell is expanded:
- (void)handleLongPress:(UILongPressGestureRecognizer*)sender {
// Get index path
slidePickerPoint = [sender locationInView:self.tableView];
NSIndexPath *indexPath= [self.tableView indexPathForRowAtPoint:slidePickerPoint];
// It's wrong when 1 cell is expand and the cell's button I hold is below the expand button
}
Can anyone please show me how to get correct indexPath when there're different cell height?
Thank in advance
One way to do it would be to add a UILongPressGestureRecognizer to each UITableViewCell (that all use the same selector), then when the selector is called you can get the cell via sender.view. Perhaps not the most memory efficient, but if the single gesture recognizer won't return the right row in certain situations, this way should work.
Something like this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
...
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc]
initWithTarget:self action:#selector(handleLongPress:)];
[longPress setMinimumPressDuration:2.0];
[cell addGestureRecognizer:longPress];
[longPress release];
return cell;
}
then
- (void)handleLongPress:(UILongPressGestureRecognizer*)sender {
UITableViewCell *selectedCell = sender.view;
}
First add the long press gesture recognizer to the table view:
UILongPressGestureRecognizer *lpgr = [[UILongPressGestureRecognizer alloc]
initWithTarget:self action:#selector(handleLongPress:)];
lpgr.minimumPressDuration = 2.0; //seconds
lpgr.delegate = self;
[self.myTableView addGestureRecognizer:lpgr];
[lpgr release];
Then in the gesture handler:
-(void)handleLongPress:(UILongPressGestureRecognizer *)gestureRecognizer
{
if (gestureRecognizer.state == UIGestureRecognizerStateBegan)
{
CGPoint p = [gestureRecognizer locationInView:self.myTableView];
NSIndexPath *indexPath = [self.myTableView indexPathForRowAtPoint:p];
if (indexPath == nil)
NSLog(#"long press on table view but not on a row");
else
NSLog(#"long press on table view at row %d", indexPath.row);
}
}
You have to be careful with this so that it doesn't interfere with the user's normal tapping of the cell and also note that handleLongPress may fire multiple times before user lifts their finger.
Thanks...!