CollectionView Cell swipe to perform operations - ios

hi iam developing a social networking application using UICollectionview horizontal layout, when ever my app launches collection view loads up to 5 cells then if i swipe 5th cell then that time only 6th cell memory will allocates. same way swipe 6th cell to 7 th cell like that.So,how can i implement this process
thanks in advance for any advices

You need to add a swipe gesture recognizer to your collectionView:
UISwipeGestureRecognizer *swipeGesture = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(handleSwipeGesture:)];
swipeGesture.direction = UISwipeGestureRecognizerDirectionUp;
[collectionView addGestureRecognizer:swipeGesture];
Then, handle the swipe in handleSwipeGesture: to allocate the cell.
-(void) handleSwipeGesture:(UISwipeGestureRecognizer *) sender
{
...
}
You can make the swipe direction whatever you want to use, this one is configure for up.
That's pretty much all there is to it. Main thing is you don't want the direction to conflict with the scrolling direction swipe, as I don't think there is a clean way to deal with that. When you are scrolling horizontally, that is a swipe, so you would need to use up/down swipe direction.
To attach to the cell instead (to capture gesture on an individual cell) I do it most times for simple collectionviews in the following method:
- (UICollectionViewCell *)collectionView:(UICollectionView *)collView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
...
UILongPressGestureRecognizer *longPressGestureRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(handleLongPress:)];
[cell addGestureRecognizer:longPressGestureRecognizer];
...
}
(This is a long press, but it works the same way). In the case of putting it in the cell, you'll need to put a tag on the cell then reference that tag int the handler to figure out which cell it came from:
Here's one for the above:
- (void) handleLongPress: (UILongPressGestureRecognizer *) sender
{
if (sender.state != UIGestureRecognizerStateBegan)
return;
CGPoint p = [sender locationInView: collectionView];
NSIndexPath *indexPath = [collectionView indexPathForItemAtPoint:p];
if (indexPath != nil)
{
UICollectionViewCell* cell = [collectionView cellForItemAtIndexPath:indexPath];
if (cell)
{
int indexOfItem = cell.tag;
NSLog(#"Selected item = %d", indexOfItem);
}
}
At least something along this line...

You can achieve this by implement the UIScrollViewDelegate method
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
CGPoint offset = scrollView.contentOffset;
CGRect bounds = scrollView.bounds;
CGSize size = scrollView.contentSize;
UIEdgeInsets inset = scrollView.contentInset;
float y = offset.x + bounds.size.width - inset.right;
float h = size.width;
float reload_distance = 75; //distance for which you want to load more
if(y > h + reload_distance) {
// write your code getting the more data
NSLog(#"load more rows");
}
}
in this when you go at the last cell of the collection then this scroll view delegate method will called.
and you may change the distance vale according to your requirement.

Related

Only The Last Cell Gets Highlighted No Matter What IndexPath is Selected in UICollectionView

This is such a crazy thing happening and I just don't understand it.
I have a UICollectionView filled with images. I want to be able to select an image and draw a red border around it (or add an x, change the alpha, or do ANYTHING to it). But no matter what I do or where I put the code, ONLY the last image in the view gets the red border. Everything executes as expected after cell selection - (the correct image is removed (the one at the correct indexpath), the collectionview gets reloaded, and the document directory gets updated). Why can't I highlight the correct image?
I do have a TapGestureRecognizer to implement drawing the border if that makes a difference but it was doing the same thing when I called the code from a button press, in which case it was expected that ALL the cells would get a red border, but that didn't happen either. Only the last cell got the border???
To make it clear, say there are nine images in the collectionview. I can tap on index 0 but only index 8 ever gets the red border. Same thing if I tap on indexes 1 through even 8 itself, only index 8 ever gets the border.
Another thing I tried was to set an image of an x on every cell and hide it when presenting the collection view. I attempted to unhide it when an image was tapped at the selected index. Same thing happens. When a cell is tapped, the last cell gets the x and not the selected cell! What the heck?
All my NSLogs indicate that the correct indexpaths are being read and it is proven in the final outcome as everything gets updated but it is a poor user experience if the correct cell cannot be highlighted.
I would be most appreciative of any ideas on this ridiculousness.
The cells are filled in a normal fashion:
_cell.bookImageView.image = [_book.imageArray objectAtIndex:indexPath.row];
Here is the tap gesture in cellForItemAtIndexPath:
UITapGestureRecognizer *deleteImageTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(deleteImageTapped:)];
deleteImageTap.numberOfTapsRequired = 2;
_cell.bookImageView.tag = indexPath.row;
[_cell.bookImageView addGestureRecognizer:deleteImageTap];
_cell.bookImageView.userInteractionEnabled = YES;
This is all I'm attempting to do:
_cell.layer.borderColor = [UIColor yellowColor].CGColor;
_cell.layer.borderWidth = 2;
Or even this:
_cell.alpha = 0.4;
A few places I tried the code in:
-(void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
-(void)collectionView:(UICollectionView *)collectionView didHighlightItemAtIndexPath:(NSIndexPath *)indexPath
//my tap gesture method
-(void)deleteImageTapped:(UITapGestureRecognizer *)gesture
//how I get the indexpath in my method
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:gesture.view.tag inSection:0];
I knows very well about your problem.
I suggest you to use a button in CellForRowAtIndexPath, instead of UiTapGesture.
First add button in your cell.
cell.yourbutton.tag = indexPath.row;
[cell.yourbutton addTarget:self action:#selector(yourButtonClicked:) forControlEvents:UIControlEventTouchUpInside];
-(void)yourButtonClicked:(UIButton*)sender
{
if (sender.tag == 0)
{
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:sender.tag inSection:0];
}
}
It turns out that I just needed to "mention" my cell in didHighlightItemAtIndexPath and didUnhighlightItemAtIndexPath. But for whatever reason, adding the changes to the cells never got called here.
I left the UITapGestureRecognizer in my cellForItemAtIndexPath and added the changes to the cell in the method called from my tap gesture.
The basic set up is like this:
#property (nonatomic, strong) CollectionViewCell *cell;
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
_cell = [collectionView dequeueReusableCellWithReuseIdentifier:reuseIdentifier forIndexPath:indexPath];
if (!_cell.selected)
{
//this "turns off" the cell changes
_cell.alpha = 1.0;
_cell.deleteButton.alpha = 0.0;
_cell.bookImageView.layer.borderColor = nil;
_cell.bookImageView.layer.borderWidth = 0;
}
UITapGestureRecognizer *deleteImageTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(deleteImageTapped:)];
deleteImageTap.numberOfTapsRequired = 2;
_cell.bookImageView.tag = indexPath.row;
[_cell.bookImageView addGestureRecognizer:deleteImageTap];
_cell.bookImageView.userInteractionEnabled = YES;
return _cell;
}
-(void)deleteImageTapped:(UITapGestureRecognizer *)gesture
{
_cell.alpha = 0.4;
_cell.deleteButton.alpha = 1.0;
_cell.bookImageView.layer.borderColor = [UIColor redColor].CGColor;
_cell.bookImageView.layer.borderWidth = 5;
//more code to delete image blah blah blah
}
And then this is where I "mention" my cell
- (void)collectionView:(UICollectionView *)colView didHighlightItemAtIndexPath:(NSIndexPath *)indexPath
{
_cell = (CollectionViewCell *)[self.collectionView cellForItemAtIndexPath:indexPath];
}
- (void)collectionView:(UICollectionView *)colView didUnhighlightItemAtIndexPath:(NSIndexPath *)indexPath
{
_cell = (CollectionViewCell *)[self.collectionView cellForItemAtIndexPath:indexPath];
}
Hope this can help someone!

How to get the location of the UICollectionViewCell in self.view?

I used the code below to get the location of the cell when touched.
However, when the UICollectionView.bounds.size.width > 320, or > 640, the "origin.x" returned often > 320, for example, 657, 469. Cause there are some images inside cells. So when I touched the cell on the second page. The value X returned may be 368.0 or other values.
I just need to get the value X in the current view.(320 * 480).
UICollectionViewLayoutAttributes *attributes = [self.collectionView layoutAttributesForItemAtIndexPath:indexPath];
- (void)viewDidLoad
{
// attach long press gesture to collectionView
UILongPressGestureRecognizer *lpgr
= [[UILongPressGestureRecognizer alloc]
initWithTarget:self action:#selector(handleLongPress:)];
lpgr.minimumPressDuration = .5; //seconds
lpgr.delegate = self;
[self.collectionView addGestureRecognizer:lpgr];
}
-(void)handleLongPress:(UILongPressGestureRecognizer *)gestureRecognizer
{
if (gestureRecognizer.state != UIGestureRecognizerStateEnded) {
return;
}
CGPoint p = [gestureRecognizer locationInView:self.collectionView];
NSIndexPath *indexPath = [self.collectionView indexPathForItemAtPoint:p];
if (indexPath == nil){
NSLog(#"couldn't find index path");
} else {
// get the cell at indexPath (the one you long pressed)
UICollectionViewCell* cell =
[self.collectionView cellForItemAtIndexPath:indexPath];
// do stuff with the cell
}
}
If I understand your question correctly, your collection view may have multiple pages horizontally. Assuming these pages are frame-width, you could do this:
int xVal = (int)p.x % (int)self.view.frame.size.width;
Keep in mind that you will lose decimal precision.

UIGestureRecognizer in table view cell get indexPath always return the indexPath of the last cell

I have a UIImageView inside UITaleViewCell, I added a tap recognizer to the UIImageView.
- (IBAction)ui_tapped:(UITapGestureRecognizer *)sender {
NSIndexPath *indexPath = [CocoaHelper indexPathWithTableView:self.tableView sender:sender.view];
RichMediaViewController *viewController = (RichMediaViewController *)[CocoaHelper viewControllerWithIdentifier:VC_RICH_MEDIA];
Message *message = self.messages[indexPath.row];
[viewController setupWithEntity:message];
[self presentViewController:viewController animated:YES completion:nil];
}
+ (NSIndexPath *)indexPathWithTableView:(UITableView *)tableView sender:(id)sender {
CGPoint hitPoint = [sender convertPoint:CGPointZero toView:tableView];
NSIndexPath *hitIndex = [tableView indexPathForRowAtPoint:hitPoint];
return hitIndex;
}
I used + (NSIndexPath *)indexPathWithTableView:(UITableView *)tableView sender:(id)sender
for buttons inside cells and it is correct, but for gesture, it always returns the cell of the last index path.
EDIT
Hard-coding the tap recognizer works
if ([message.type isEqualToString:#"image"]) {
UIImageView *view = ((ImageMessageCell *)cell).imageView;
UITapGestureRecognizer *tapRec = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(ui_tapped:)];
[view setGestureRecognizers:[NSArray arrayWithObject:tapRec]];
}
This is very tedious coding (since I need to check for each types of cells including image, video and other rich media), please post your answer if you know how to add it from the storyboard
It turns out that it's not able to add gesture recognizer to each instantiated prototype cell separately, i.e. only one gesture recognizer for all the cells instantiated from the same prototype cell.
Solved this problem by using UIButton with background image view instead of using image view.
[self.imageButton setBackgroundImage:[UIImage imageNamed:LOADING_IMAGE_FILE] forState:UIControlStateNormal];
NSData *blob = post.thumbnailBlob;
if (blob) {
[self.imageButton setBackgroundImage:[UIImage imageWithData:blob] forState:UIControlStateNormal];
}
In order for each cell to have it's unique tap gesture tag, it needs to be done in a proper sequence:
let tapGest = UITapGestureRecognizer(target: self, action: #selector(yourTapFunction(_:)))
//1 add gesture to the cell
cell.imageView.addGestureRecognizer(tapGest)
//2 assign a tag
tapGest.view?.tag = indexPath.row
#objc func yourTapFunction(_ tapGest: UITapGestureRecognizer) {
guard let index = tapGest.view?.tag else { return }
//do smth
}
if you reverse 1 and 2, the tag will always be 0

Trying to link UIImageView to a UITableViewCell from panGestureDetected method in iOS

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.

iOS - indexPathForRowAtPoint don't return correct indexPath with different cell height

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...!

Resources