Trying to link UIImageView to a UITableViewCell from panGestureDetected method in iOS - 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.

Related

Keeping UITableView and UICollectionView scroll position in sync

I have a UIViewController that is able to show its content via a UICollectionView (cv) or via aUITableView (tv).
Both views are added to the view hierarchy via storyboard and I'm just hiding the one that should not be shown. Both the UICollectionView and the UITableView have the same data source.
The UICollectionView is showing the content in a two items per row grid, the UITableView obviously in a one item per row grid.
So what I want to happen is if the user decides to switch between the views the newly shown view should scroll to the element that was visible in the other view.
I did some experimenting with scrollToRowAtIndexPath:atScrollPosition:animated: and scrollToItemAtIndexPath:atScrollPosition:animated: with no success.
I understand, that both the UICollectionView and UITableView are able to return their current visible items'/rows' indexPaths, but how can i transform those indexPath to a indexPath of the other view and tell them to scroll to that element?
My current touch event handling of the switch button looks like this:
- (IBAction)switchView:(id)sender {
self.showListView = !self.showListView;
UIView *fromView, *toView;
NSIndexPath *indexPath;
if (self.showListView)
{
fromView = self.ArticleCollection;
toView = self.ArticleList;
CGRect visibleRect = (CGRect){.origin = self.ArticleCollection.contentOffset, .size = self.ArticleCollection.bounds.size};
CGPoint visiblePoint = CGPointMake(100, CGRectGetMidY(visibleRect));
indexPath = [self.ArticleCollection indexPathForItemAtPoint:visiblePoint];
}
else
{
fromView = self.ArticleList;
toView = self.ArticleCollection;
CGRect visibleRect = (CGRect){.origin = self.ArticleList.contentOffset, .size = self.ArticleList.bounds.size};
CGPoint visiblePoint = CGPointMake(100, CGRectGetMidY(visibleRect));
indexPath = [self.ArticleList indexPathForRowAtPoint:visiblePoint];
}
NSInteger row = indexPath.row;
if (self.showListView) {
[self.ArticleList scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:row*2 inSection:0]
atScrollPosition:UITableViewScrollPositionTop
animated:NO];
} else {
[self.ArticleCollection scrollToItemAtIndexPath:[NSIndexPath indexPathForItem:ceil(row/2)
inSection:0
atScrollPosition:UICollectionViewScrollPositionCenteredVertically
animated:NO]];
}
// Here a transitionFromView:ToView:duration:options:completion call follows
}
Thanks in advance for any contribution to my problem.
Appart from a little typo inside the scrollToItemAtIndexPath:atScrollPosition:animated: call:
[self.ArticleCollection scrollToItemAtIndexPath:[NSIndexPath indexPathForItem:row
inSection:0]
atScrollPosition:UICollectionViewScrollPositionCenteredVertically
animated:NO];
I did think indexPath.row would hold different values whether it came from the cv's visible indexPath or the tv's visible indexPath so I calculated the new row by multiplying or dividing through 2. But as it turns out the rows are identical in both cases, so I just needed to use the same value for both cases:
NSInteger row = indexPath.row;
if (self.showListView) {
[self.ArticleList scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:row inSection:0]
atScrollPosition:UITableViewScrollPositionTop
animated:NO];
} else {
[self.ArticleCollection scrollToItemAtIndexPath:[NSIndexPath indexPathForItem:row
inSection:0]
atScrollPosition:UICollectionViewScrollPositionCenteredVertically
animated:NO];
}
Sorry for the inconvenience.

Adding UIPanGesture to UITableViewCell

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.

UITextView in UITableViewCell scroll to location

I have a UITextView in a UITableViewCell. They both dynamically size itself to fit. The problem I have is that when the texview reaches below the bottom of the screen (from making new lines), the tableView does not scroll to its location.
I tried this code, but it didn't work.
- (void)textViewDidChangeSelection:(UITextView *)textView {
[textView scrollRangeToVisible:textView.selectedRange];
}
You can find the cell containing the textView and tell the table view to scroll to that cell:
UIView *parentView = textView.superview;
while(![parentView isKindOfClass:[UITableViewCell class]]) {
parentView = parentView.superview;
}
NSIndexPath *indexPath = [self.tableView indexPathForCell:(UITableViewCell*)parentView];
[self.tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionMiddle animated:YES];
You should add code below to scroll the tableView too:
- (void)textViewDidChangeSelection:(UITextView *)textView {
[textView scrollRangeToVisible:textView.selectedRange];
// since you are only scrolling the table a little bit and not by a whole cell, then change the scrollView.offset
CGPoint offset = tableView.contentOffset;
offset.x += 20; // an example. You need to change this based on the new size of the textview.
tableView.contentOffset = offset;
}

CollectionView Cell swipe to perform operations

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.

Get position of cell or text field in this cell

I have a split view controller: view & tableview. In this table view I have custom cells with text field. I don't know how many cells will I have, so it generate automatically. And now I'm trying scroll to textfield, when it becomeFirstResponder. I've tried something like this:
-(void) textFieldDidBeginEditing:(UITextField *)textField {
CGPoint focusOnTextField = CGPointMake(0, 300 + textField.frame.origin.y);
[scroller setContentOffset: focusOnTextField animated: YES];
}
300px - start position of my TableView. All seems alright, but textField.frame.origin.y always equal to 0 (like and bounds.origin.y btw).
I thought I can solve a problem if get position of cell, which textfield is active, and then replace textField.frame.origin.y on cell.frame.origin.y or something like this.
===================================================================
I forgot say that my tableviews scroll is disabled. I follow your advices and code examples and solve it like that:
- (UITableViewCell *)cellWithSubview:(UIView *)subview {
while (subview && ![subview isKindOfClass:[UITableViewCell self]])
subview = subview.superview;
return (UITableViewCell *)subview;
}
- (void)textFieldDidBeginEditing:(UITextField *)textField {
UITableViewCell *activeCell = [self cellWithSubview:textField];
float offsetValueY = 200 + activeCell.origin.y;
CGPoint focusOnTextField = CGPointMake(0, offsetValueY);
[scroller setContentOffset:focusOnTextField animated:YES];
}
And know what? It's working! :-) But it create a new problem. When I begin editing textfield, scroller at first jump on top, and only then going to it correct position. When I write [scroller setContentOffset:focusOnTextField animated:NO]; and this problem disappear, but there is no smooth move of scroller. And this is bad to me :-) So how we can solve it?
Here's how to scroll to a cell containing a text field ...
// find the cell containing a subview. this works independently of how cells
// have been constructed.
- (UITableViewCell *)cellWithSubview:(UIView *)subview {
while (subview && ![subview isKindOfClass:[UITableViewCell self]])
subview = subview.superview;
return (UITableViewCell *)subview;
}
Your idea is correct to trigger that action when editing begins. Just do it using the cell...
- (void)textFieldDidBeginEditing:(UITextField *)textField {
// find the cell containing this text field
UITableViewCell *cell = [self cellWithSubview:textField];
// now scroll using that cell's index path as the target
NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];
[self.tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionTop animated:YES];
}
If you add the textfield in the UITableVieCell content view (added by default if you are using .xib) then you have to call something like textField.superview.superview and this will give you the parent cell. If you add the text field directly to the cell view then you have to textField.superview.
[tableView scrollToRowContainingComponent:textField atScrollPosition: UITableViewScrollPositionMiddle animated:YES];
after adding the following category to UITableView:
#implementation UITableView (MyCategory)
-(NSIndexPath*)indexPathOfCellComponent:(UIView*)component {
if([component isDescendantOfView:self] && component != self) {
CGPoint point = [component.superview convertPoint:component.center toView:self];
return [self indexPathForRowAtPoint:point];
}
else {
return nil;
}
}
-(void)scrollToRowContainingComponent:(UIView*)component atScrollPosition:(UITableViewScrollPosition)scrollPosition animated:(BOOL)animated {
NSIndexPath *indexPath = [self indexPathOfCellComponent:component];
if(indexPath) {
[self scrollToRowAtIndexPath:indexPath atScrollPosition: scrollPosition animated:animated];
}
}
#end

Resources