Trying to use visibleCells when scrolling UITableView in iOS - ios

I am building an app where a UIImageView traverses a UITableView programmatically. I have created two buttons ("up" and "down") that control which direction the UIImageView moves. What I am trying to do is programmatically scroll the UITableView if the UIImageView is moving up/down to a row that is not presently visible. I realize I need to use the UITableView method, "visibleCells" to check to see if the cell is within the array is returned. If the cell is not in the array, then I need to scroll the table up/down by precisely one row (each time the user clicks on the "up"/"down" button). Unfortunately I have not come across any good examples on how to use the method, and I need help. Here is the code that I have here:
- (IBAction)buttonClicked:(id)sender {
if([sender tag] == 1){
//here is where I will need to make the call to [_tableView visibleCells] to see if the cell that the UIImageView is about to scroll up to is within this array. If not, then programmatically scroll up one row.
if (_index.row == 0) {
_index = [NSIndexPath indexPathForRow:[_tableData count] - 1 inSection:_index.section];
[_table scrollToRowAtIndexPath:_index atScrollPosition:UITableViewScrollPositionTop animated:YES];
}
else
_index = [NSIndexPath indexPathForRow:_index.row - 1 inSection:_index.section];
[UIView animateWithDuration:.3 animations:^{
CGRect rect = [self.view convertRect:[_table rectForRowAtIndexPath:_index] fromView:_table];
CGFloat floatx = _imageView.frame.origin.x - rect.origin.x;
_imageView.frame = CGRectMake(rect.origin.x + floatx, rect.origin.y, _imageView.frame.size.width, _imageView.frame.size.height);
}];
}
else if([sender tag] == 2){
if (_index.row + 1 == [_tableData count]) {
_index = [NSIndexPath indexPathForRow:0 inSection:_index.section];
[_table scrollToRowAtIndexPath:_index atScrollPosition:UITableViewScrollPositionBottom animated:YES];
}
else
_index = [NSIndexPath indexPathForRow:_index.row + 1 inSection:_index.section];
[UIView animateWithDuration:.3 animations:^{
CGRect rect = [self.view convertRect:[_table rectForRowAtIndexPath:_index] fromView:_table];
CGFloat floatx = _imageView.frame.origin.x - rect.origin.x;
_imageView.frame = CGRectMake(rect.origin.x + floatx, rect.origin.y, _imageView.frame.size.width, _imageView.frame.size.height);
}];
}
}
Can anyone show me how to accomplish the functionality that I am trying to achieve?

If you have a little bit time to learn something new, i recommend you to try with iCarousel by #nick-lockwood. Yo can use a vertical iCarousel to achieve something like that:
It's like a UITableView, but for each item in the carousel you can set any UIView, and then you can show a specific item setting the currentItemIndex property!
Take a look if it fits to you. iCarousel saved me two days of work!

Related

Don't Move Table View Cells with a Long Press Gesture on specific position

I've completely implemented the UILongGesture in my App which exchanges the cell value by drag and drop. For now I've requirement that if I move first row with last row then first row should remain at first position means don't want change the position.
I've tried chunk of codes and wasted my time but couldn't get result. Below is my code.
- (IBAction)longPressGestureRecognized:(id)sender{
UILongPressGestureRecognizer *longGesture = (UILongPressGestureRecognizer *)sender;
UIGestureRecognizerState state = longGesture.state;
CGPoint location = [longGesture locationInView:self.tblTableView];
NSIndexPath *indexpath = [self.tblTableView indexPathForRowAtPoint:location];
static UIView *snapshotView = nil;
static NSIndexPath *sourceIndexPath = nil;
switch (state) {
case UIGestureRecognizerStateBegan:
if (indexpath) {
sourceIndexPath = indexpath;
UITableViewCell *cell = [self.tblTableView cellForRowAtIndexPath:indexpath];
snapshotView = [self customSnapshotFromView:cell];
__block CGPoint center = cell.center;
snapshotView.center = center;
snapshotView.alpha = 0.0;
[self.tblTableView addSubview:snapshotView];
[UIView animateWithDuration:0.25 animations:^{
center.y = location.y;
snapshotView.center = center;
snapshotView.transform = CGAffineTransformMakeScale(1.05, 1.05);
snapshotView.alpha = 0.98;
cell.alpha = 0.0;
} completion:^(BOOL finished) {
cell.hidden = YES;
}];
}
break;
case UIGestureRecognizerStateChanged: {
CGPoint center = snapshotView.center;
center.y = location.y;
snapshotView.center = center;
if (indexpath && ![NSIndexPath isEqual:sourceIndexPath]) {
[self.namesArray exchangeObjectAtIndex:indexpath.row withObjectAtIndex:sourceIndexPath.row];
[self.tblTableView moveRowAtIndexPath:sourceIndexPath toIndexPath:indexpath];
sourceIndexPath = indexpath;
NSIndexPath *indexPathOfLastItem =[NSIndexPath indexPathForRow:([self.namesArray count] - 1) inSection:0];
NSLog(#"last :::: %#",indexPathOfLastItem);
if (indexpath==indexPathOfLastItem) {
[self.namesArray exchangeObjectAtIndex:indexPathOfLastItem.row withObjectAtIndex:sourceIndexPath.row];
[self.tblTableView moveRowAtIndexPath:indexPathOfLastItem toIndexPath:0];
UITableViewCell *cell = [self.tblTableView cellForRowAtIndexPath:sourceIndexPath];
cell.hidden = NO;
cell.alpha = 0.0;
}
}
break;
}
default: {
UITableViewCell *cell = [self.tblTableView cellForRowAtIndexPath:sourceIndexPath];
cell.hidden = NO;
cell.alpha = 0.0;
[UIView animateWithDuration:0.25 animations:^{
snapshotView.center = cell.center;
snapshotView.transform = CGAffineTransformIdentity;
snapshotView.alpha = 0.0;
cell.alpha = 1.0;
} completion:^(BOOL finished) {
sourceIndexPath = nil;
[snapshotView removeFromSuperview];
snapshotView = nil;
}];
break;
}
}
}
EDIT: What I have come across is the cell is not exchanging that's what I want but it is hidden. Here is the image: Image1 and Image2
First of all, I don't think you should do the row exchanges in UIGestureRecognizerStateChanged but instead in UIGestureRecognizerStateEnded. UIGestureRecognizerStateChanged is processed rapidly(many many times) while you are long pressing and moving your finger across the screen. So this causes the row exchange code to run many times which is not your intention I guess. UIGestureRecognizerStateBegan and UIGestureRecognizerStateEnded are run once per each longpress.
I would keep these following three lines of code in UIGestureRecognizerStateChanged and move the rest to UIGestureRecognizerStateEnded:
CGPoint center = snapshotView.center;
center.y = location.y;
snapshotView.center = center;
The UIGestureRecognizerStates are as follows:
UIGestureRecognizerStatePossible, // the recognizer has not yet recognized its gesture, but may be evaluating touch events. this is the default state
UIGestureRecognizerStateBegan, // the recognizer has received touches recognized as the gesture. the action method will be called at the next turn of the run loop
UIGestureRecognizerStateChanged, // the recognizer has received touches recognized as a change to the gesture. the action method will be called at the next turn of the run loop
UIGestureRecognizerStateEnded, // the recognizer has received touches recognized as the end of the gesture. the action method will be called at the next turn of the run loop and the recognizer will be reset to UIGestureRecognizerStatePossible
UIGestureRecognizerStateCancelled, // the recognizer has received touches resulting in the cancellation of the gesture. the action method will be called at the next turn of the run loop. the recognizer will be reset to UIGestureRecognizerStatePossible
UIGestureRecognizerStateFailed, // the recognizer has received a touch sequence that can not be recognized as the gesture. the action method will not be called and the recognizer will be reset to UIGestureRecognizerStatePossible
And instead of using switchs default:, I believe it will be better to cover more of these states in your state machine logic.
When your finger detaches from the screen during animation, iOS returns gesture.state = UIGestureRecognizerStatePossible.
So remember to handle that gesture!

Creating a stack of photos with UICollectionView which a user can swipe through

I am attempting to create a stack of photos using UICollectionView in which a user can swipe a photo to the side. As a photo is dragged, it gets smaller (to a minimum scale) and the photo behind is enlarged to a maximum scale. This maximum scale is the original size of the foreground photo.
When the pan gesture which drags the foreground photo ends, if it is the minimum scale, it is sent to the back of the stack, otherwise, it is animated back to the centre of the screen and enlarged back to its original size (along with the growing photo behind it).
I hope you're still with me haha.
Now, assuming the photo being dragged is at its minimum size, I remove user interaction of the UICollectionView, animate the photo to the centre of the screen again, and its size is animated to match the rest of the background images. I then send the cell to the back of the UICollectionViews subviews.
Once all of these animations have taken place, I update my data source (an array of images) by adding the photo in front to the back, then deleting that photo at the front.
Then, I update the UICollectionView. This consists of a batch update in which I delete the item at an NSIndexPath of 0 : 0, then, insert an item at NSIndexPath ([photo count] - 1) : 0.
When this batch update finished, I reload my UICollectionView (as only the item at NSIndexPath 0 : 0 has a pan gesture) and re-add user interaction to the UICollectionView.
I hope this makes sense.
Notes
The size of the background photos is a percent of 80%.
The size of the foreground photo is a percent of 100%.
This is relative to the UICollectionView which fits the screen of the iPhone.
My problem
My code seems to work very well. My issue comes from when I take my finger off the screen (the pan gesture ends) and the animation of the photo (centring it to the centre of the screen and sizing it like the rest of the background photos) ends.
The photo dragged off reappears on screen, sizes from the size of the background photos (80%) to the size of the foreground photo (100%) then fades away. Once this happens the photos are reordered as expected.
Does anyone know why this might happen?
Here is my Pan Gesture code:
CGPoint touchTranslation = [gesture translationInView:self.view];
static CGPoint originalCenter;
DIGalleryCollectionViewLayout *galleryCollectionViewLayout = (DIGalleryCollectionViewLayout *)self.galleryCollectionView.collectionViewLayout;
if (gesture.state == UIGestureRecognizerStateBegan) {
//
// Began.
//
originalCenter = gesture.view.center;
} else if (gesture.state == UIGestureRecognizerStateChanged) {
//
// Changed.
//
CGPoint translate = [gesture translationInView:gesture.view.superview];
touchTranslation = CGPointMake(originalCenter.x + translate.x, originalCenter.y + translate.y);
[gesture.view setCenter:touchTranslation];
CGPoint pointsPhotoIsMovedBy = CGPointMake(fabsf(fabsf(originalCenter.x) - fabsf(touchTranslation.x)),
fabsf(fabsf(originalCenter.y) - fabsf(touchTranslation.y)));
// Update cells.
//
CGFloat currentIncrement = MAX(pointsPhotoIsMovedBy.x, pointsPhotoIsMovedBy.y);
for (NSIndexPath *indexPath in self.galleryCollectionView.indexPathsForVisibleItems) {
UICollectionViewCell *cell = [self.galleryCollectionView cellForItemAtIndexPath:indexPath];
if ([indexPath isEqual:[NSIndexPath indexPathForItem:0 inSection:0]]) {
//
// Front.
//
CGFloat frontScalePercent = MIN(GalleryCollectionViewLayoutFrontRatioDefault,
MAX(GalleryViewFrontScaleMinimum, GalleryCollectionViewLayoutFrontRatioDefault - (currentIncrement / 250.0f)));
[cell setTransform:CGAffineTransformMakeScale(frontScalePercent, frontScalePercent)];
} else if ([indexPath isEqual:[NSIndexPath indexPathForItem:1 inSection:0]]) {
//
// Next.
//
CGFloat nextScalePercent = MAX(GalleryCollectionViewLayoutBackRatioDefault,
MIN(GalleryViewNextScaleMaximum, (currentIncrement / 150.0f)));
[cell setTransform:CGAffineTransformMakeScale(nextScalePercent, nextScalePercent)];
} else {
//
// Background.
//
[cell setTransform:CGAffineTransformMakeScale(GalleryCollectionViewLayoutBackRatioDefault,
GalleryCollectionViewLayoutBackRatioDefault)];
}
}
} else if (gesture.state == UIGestureRecognizerStateEnded) {
//
// Ended.
//
if (gesture.view.transform.a == GalleryViewFrontScaleMinimum) {
//
// Next photo.
//
[self.galleryCollectionView setUserInteractionEnabled:NO];
[self.galleryCollectionView sendSubviewToBack:gesture.view];
[UIView animateWithDuration:0.3f
animations:^{
[gesture.view setCenter:self.galleryCollectionView.center];
[gesture.view setTransform:CGAffineTransformMakeScale(GalleryCollectionViewLayoutBackRatioDefault,
GalleryCollectionViewLayoutBackRatioDefault)];
}
completion:^(BOOL finished) {
//
// Data source
//
NSMutableArray *photos = [self.photos mutableCopy];
[photos addObject:[photos objectAtIndex:0]];
[photos removeObjectAtIndex:0];
[self setPhotos:photos];
// Contents
//
[self.galleryCollectionView performBatchUpdates:^{
[self.galleryCollectionView deleteItemsAtIndexPaths:#[[NSIndexPath indexPathForItem:0 inSection:0]]];
[self.galleryCollectionView insertItemsAtIndexPaths:#[[NSIndexPath indexPathForItem:(self.photos.count - 1) inSection:0]]];
} completion:^(BOOL finished) {
[self.galleryCollectionView reloadData];
[self.galleryCollectionView setUserInteractionEnabled:YES];
}];
}];
} else {
//
// Stay.
//
[UIView animateWithDuration:0.3f
delay:0.0f
options:UIViewAnimationOptionCurveEaseIn
animations:^{
//
// Front cell.
//
[gesture.view setCenter:self.galleryCollectionView.center];
[gesture.view setTransform:CGAffineTransformMakeScale(GalleryCollectionViewLayoutFrontRatioDefault,
GalleryCollectionViewLayoutFrontRatioDefault)];
// Next cell.
//
for (NSIndexPath *indexPath in self.galleryCollectionView.indexPathsForVisibleItems) {
if ([indexPath isEqual:[NSIndexPath indexPathForItem:1 inSection:0]]) {
//
// Next.
//
UICollectionViewCell *cell = [self.galleryCollectionView cellForItemAtIndexPath:indexPath];
[cell setTransform:CGAffineTransformMakeScale(GalleryCollectionViewLayoutBackRatioDefault,
GalleryCollectionViewLayoutBackRatioDefault)];
}
}
}
completion:^(BOOL finished) {
[galleryCollectionViewLayout setFrontRatio:GalleryCollectionViewLayoutFrontRatioDefault];
[galleryCollectionViewLayout setNextRatio:GalleryCollectionViewLayoutBackRatioDefault];
[galleryCollectionViewLayout invalidateLayout];
}];
}
}
And here is my prepareLayout: code for the collection view layout:
NSMutableDictionary *newLayoutInfo = [NSMutableDictionary dictionary];
NSMutableDictionary *cellLayoutInfo = [NSMutableDictionary dictionary];
NSIndexPath *indexPath;
for (NSInteger section = 0; section < [self.collectionView numberOfSections]; section++) {
NSInteger itemCount = [self.collectionView numberOfItemsInSection:section];
for (NSInteger item = 0; item < itemCount; item++) {
indexPath = [NSIndexPath indexPathForItem:item inSection:section];
UICollectionViewLayoutAttributes *itemAttributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
[itemAttributes setSize:[self sizeForPhotoAtIndexPath:indexPath inContainerOfSize:self.collectionView.bounds.size]];
[itemAttributes setCenter:self.collectionView.center];
// Z Index.
//
NSInteger zIndex = itemCount - item;
[itemAttributes setZIndex:zIndex];
// Scale cells based on z position.
//
if (zIndex == itemCount) {
//
// Foreground.
//
[itemAttributes setTransform:CGAffineTransformMakeScale(self.frontRatio, self.frontRatio)];
} else if (zIndex == itemCount - 1) {
//
// Next.
//
[itemAttributes setTransform:CGAffineTransformMakeScale(self.nextRatio, self.nextRatio)];
} else {
//
// Background.
//
[itemAttributes setTransform:CGAffineTransformMakeScale(GalleryCollectionViewLayoutBackRatioDefault,
GalleryCollectionViewLayoutBackRatioDefault)];
}
cellLayoutInfo[indexPath] = itemAttributes;
}
}
newLayoutInfo[GalleryCollectionViewLayoutCellKind] = cellLayoutInfo;
self.layoutInfo = newLayoutInfo;
And as an additional reference, here is my code for sizeForPhotoAtIndexPath: inContainerOfSize:
UIImage *photo = [[(DIGalleryViewController *)self.collectionView.delegate photos] objectAtIndex:indexPath.row];
CGSize size = CGSizeMake(photo.size.width, photo.size.height);
UIGraphicsBeginImageContext(containerSize);
[photo drawInRect:CGRectMake(0.0f, 0.0f, containerSize.width, containerSize.height)];
UIImage *resizedPhoto = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
CGFloat ratio = photo.size.width / resizedPhoto.size.width;
size = CGSizeMake(resizedPhoto.size.width, photo.size.height / ratio);
return size;

TableView scrolls to middle when a new row is inserted

I have a table view that displays messages that I fetch from over the network.
When a new message comes in, it should be added to the tableview at the bottom.
When a new messages comes in, it is added to the bottom of the tableview properly, but the tableview then immediately scrolls to the middle. (So if I had say 100 cells in there now, cell 50 would be in the middle of the scroll view now).
The weird thing is that this only happens if the scroll point before the insert is in the lower half of the tableview.
So, if I'm looking at cell 10 of 99, and I add a row, it gets added to the bottom just fine.
But, if I'm looking at cell 75 of 99, and I add a row, the scroll position jumps up to the middle (~cell 50) again.
Here's my code for adding a new message to the tableview:
[self.tableView beginUpdates];
[self.messages addObject:message];
NSArray *indexPaths = #[[NSIndexPath indexPathForRow:(self.messages.count - 1) inSection:0]];
[self.tableView insertRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationFade];
[self.tableView endUpdates];
Why is the tableview scrolling automatically like this?
I too have this problem.
NSTableView and UITableView are probably similar... their default scrolling behavior both SUCK.
TableView is not designed to be used as a spreadsheet style editor,
so over the years I've added thousands of lines of code to make it
work sort of like a spreadsheet.
I wish Apple would just create a NSSpreadsheetView subclass of NSTableView to fix all this stuff:
- cursor keys navigate cells
- sensible scroll behavior
- add hoc cell selection
Would also like to see a NSLogView subclass of NSTableView which behaves like Console.app, with a search field.
My solution was to add several category methods:
#implementation NSTableView (VNSTableViewCategory)
-(NSInteger)visibleRow
{
NSRect theRect = [self visibleRect];
NSRange theRange = [self rowsInRect:theRect];
if ( theRange.length == 0 )
return -1;
else if ( NSLocationInRange( [self editedRow], theRange ) )
return [self editedRow];
else if ( NSLocationInRange( [self clickedRow], theRange ) )
return [self clickedRow];
else if ( NSLocationInRange( [self selectedRow], theRange ) )
return [self selectedRow];
else
return theRange.location + (theRange.length/2);
}
-(NSRange)visibleRows
{
NSRect theRect = [self visibleRect];
NSRange theRange = [self rowsInRect:theRect];
return theRange;
}
-(void)scrollRowToVisibleTwoThirds:(NSInteger)row
{
NSRect theVisRect = [self visibleRect];
NSUInteger numRows = [self numberOfRows];
NSRange visibleRows = [self rowsInRect:theVisRect];
//NSUInteger lastVisibleRow = NSMaxRange(visibleRows);
if ( NSLocationInRange( row, visibleRows ) )
{
if ( row - visibleRows.location < (visibleRows.length * 2 / 3) )
{
// may need to scroll up
if ( visibleRows.location == 0 )
[self scrollRowToVisible:0];
else if ((row - visibleRows.location) > 2 )
return;
}
}
NSRect theRowRect = [self rectOfRow:row];
NSPoint thePoint = theRowRect.origin;
thePoint.y -= theVisRect.size.height / 4; // scroll to 25% from top
if (thePoint.y < 0 )
thePoint.y = 0;
NSRect theLastRowRect = [self rectOfRow:numRows-1];
if ( thePoint.y + theVisRect.size.height > NSMaxY(theLastRowRect) )
[self scrollRowToVisible:numRows-1];
else
{
[self scrollPoint:thePoint]; // seems to be the 'preferred' method of doing this
// kpk note: these other approaches cause redraw artifacts in many situations:
// NSClipView *clipView = [[self enclosingScrollView] contentView];
// [clipView scrollToPoint:[clipView constrainScrollPoint:thePoint]];
// [[self enclosingScrollView] reflectScrolledClipView:clipView];
// [self scrollRowToVisible:row];
// [[[self enclosingScrollView] contentView] scrollToPoint:thePoint];
// [[self enclosingScrollView] reflectScrolledClipView:[[self enclosingScrollView] contentView]];
}
}
#end // NSTableView (VNSTableViewCategory)
Usage Example:
NSRange visRows = [toScenesTable visibleRows];
visRows.length -= 2;
if ( iAlwaysScroll == YES || !NSLocationInRange(row, visRows) )
{
[toScenesTable scrollRowToVisibleTwoThirds:row]; // VNSTableViewCategory
}
I think you need to change the row animation to none-
[self.tableView
insertRowsAtIndexPaths:indexPaths
withRowAnimation:
UITableViewRowAnimationNone];
Try this
[self.tableView scrollToRowAtIndexPath:indexpath atScrollPosition:UITableViewScrollPositionBottom animated:YES];
also you can change the position to middile,top or none.

How can I drag a UICollectionViewCell from one UICollectionView to another UICollectionView?

I am making an iPad application. On one page of this application, there is a UICollectionView on the left-hand side and another UICollectionView on the right hand side. Each UICollectionView is one column wide.
The functionality I desire is as follows:
Each UICollectionViewCell on the left hand side should be able to be dragged to the UICollectionView on the right hand side. If this is not possible, then at least a UICollectionViewCell should be able to be dragged out of the left UICollectionView and then I'll handle having it appear in the righthand UICollectionView.
Is this functionality possible? If so, how would I go about implementing it?
You would want to attach a long press gesture recognizer to the common superview of both collectionViews. The drag operation is triggered by the long-press, and the entire transaction is handled within that recognizer. Because the pan gesture is used for scrolling the collectionviews, you will run into problems in trying to use the pan recognizer.
The key thing is the gesture recognizer needs to be attached the COMMON superview, and all points and rectangles are converted to the coordinate system of the superview.
This isn't the exact code (this moves from a CV to another view) but the process would be similar (NOTE: I have tried to strip out some code that would be irrelevant to your app, so I could have messed something up in the process -- but the concept holds):
- (void) processLongPress:(UILongPressGestureRecognizer *)sender
{
if (sender.state == UIGestureRecognizerStateChanged)
{
if (!dragView)
return;
CGPoint location = [sender locationInView:self.view];
CGPoint translation;
translation.x = location.x - dragViewStartLocation.x;
translation.y = location.y - dragViewStartLocation.y;
CGAffineTransform theTransform = dragView.transform;
theTransform.tx = translation.x;
theTransform.ty = translation.y;
dragView.transform = theTransform;
[self.view bringSubviewToFront:dragView];
return;
}
if (sender.state == UIGestureRecognizerStateBegan)
{
// if point gives a valid collectionView indexPath we are doing a long press on a picture item to begin a drag
// & drop operation.
CGPoint point = [sender locationInView:collectionView];
dragViewIndexPath = [collectionView indexPathForItemAtPoint:point];
if (dragViewIndexPath) // i.e., selected item in collection view.
{
UICollectionViewCell *cell = [collectionView cellForItemAtIndexPath:dragViewIndexPath];
dragView = [cell.contentView viewWithTag:cell.tag];
[dragView removeFromSuperview];
[self.view addSubview:dragView];
dragView.center = [collectionView convertPoint:point toView:self.view];
dragViewStartLocation = dragView.center;
[self.view bringSubviewToFront:dragView];
}
return;
}
if (sender.state == UIGestureRecognizerStateEnded)
{
if (dragView)
{
dragView.center = CGPointMake(dragView.center.x + dragView.transform.tx, dragView.center.y + dragView.transform.ty);
CGAffineTransform theTransform = dragView.transform;
theTransform.tx = 0.0f;
theTransform.ty = 0.0f;
UIView *dropTarget = [self mapDisplayModeToReceiverView]; // get drop target
CGRect convertedTargetFrame = [self.view convertRect:dropTarget.frame fromView:dropTarget.superview];
if (CGRectContainsPoint(convertedTargetFrame, dragView.center)) // if so, then drop it.
{
ImageWithAttachedLabel *i = (ImageWithAttachedLabel *) dragView;
[speakStrings addObject:[i.labelText stringByAppendingString:#". "]];
UserData *uData = (UserData *)i.userDataObject;
UIImage *image = [[UIImage alloc] initWithData:uData.image];
CGRect newFrame = CGRectMake(0.0f, 0.0f, 140.0f, 140.0f);
ImageWithAttachedLabel *newImage = [[ImageWithAttachedLabel alloc] initWithFrame:newFrame withImage:image withLabel:uData.itemName];
newImage.tag = RECEIVERVIEW_MAGIC_NUMBER;
[self.view addSubview:newImage];
newImage.center = [receiverView convertPoint:dropTarget.center toView:self.view];
[UIView animateWithDuration:0.35f animations:^{ newImage.transform = CGAffineTransformMakeScale(1.15f, 1.15f); newImage.transform = CGAffineTransformIdentity; }
completion:^(BOOL finished) { if (finished)
{
[newImage removeFromSuperview];
newImage.frame = newFrame;
[dropTarget addSubview:newImage];
[dragView removeFromSuperview];
dragView=nil; }
}];
}
else
{
[dragView removeFromSuperview];
dragView = nil;
}
[self reloadData];
return;
}
}
There's no way to actually 'pass' a cell from a collection to the other, but you can do the following:
1) Once you detect that the user dragged a cell in the other collection, delete the cell from the first collection (let's call it Collection 1). You can maybe use a nice fade animation to make the cell disappear.
2) Add a cell to the second table with a nice animation (see the UICollectionView and UICollectionViewLayout methods and delegate methods for this).

Trying to move a UIImageView within a UITableView in iOS

I have developed a simple UITableView that displays a list of items. On top of this UITableView, I have created a selection bar in the form of a UIImageView that moves to whichever row is selected by the user. I have created two buttons (one up, one down) which also controls the movement of this selection bar. When the user clicks the up button, the selection bar moves up exactly one row, and when the user clicks the down button, the selection bar moves down exactly one row. My problem is that when I reach the very top of the table, I want the selection bar to move to the very bottom of the table if the user clicks the up button, and I want the selection bar to go to the very top of the table if the user clicks the down button. Both buttons call the same method (I distinguish between the two buttons based on their tag values). However, when I do this, I am getting the following runtime exception:
2013-06-06 11:34:03.124 SimpleTable[4982:c07] *** Assertion failure in -[UITableViewRowData rectForRow:inSection:], /SourceCache/UIKit_Sim/UIKit-2380.17/UITableViewRowData.m:1630
2013-06-06 11:34:03.207 SimpleTable[4982:c07] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'request for rect at invalid index path (<NSIndexPath 0x9489850> 2 indexes [0, 10])'
I am not sure why this is happening. Here is my relevant code which is called by my up and down buttons:
- (IBAction)buttonClicked:(id)sender {
if([sender tag] == 1){
if (_index.row == 0) {
_index = [NSIndexPath indexPathForRow:[_tableData count] inSection:_index.section];
}
else
_index = [NSIndexPath indexPathForRow:_index.row - 1 inSection:_index.section];
[UIView animateWithDuration:.3 animations:^{
CGRect rect = [self.view convertRect:[_table rectForRowAtIndexPath:_index] fromView:_table];
CGFloat floatx = _imageView.frame.origin.x - rect.origin.x;
_imageView.frame = CGRectMake(rect.origin.x + floatx, rect.origin.y, _imageView.frame.size.width, _imageView.frame.size.height);
}];
}
else if([sender tag] == 2){
if (_index.row == [_tableData count]) {
_index = [NSIndexPath indexPathForRow:0 inSection:_index.section];
}
else
_index = [NSIndexPath indexPathForRow:_index.row + 1 inSection:_index.section];
[UIView animateWithDuration:.3 animations:^{
CGRect rect = [self.view convertRect:[_table rectForRowAtIndexPath:_index] fromView:_table];
CGFloat floatx = _imageView.frame.origin.x - rect.origin.x;
_imageView.frame = CGRectMake(rect.origin.x + floatx, rect.origin.y, _imageView.frame.size.width, _imageView.frame.size.height);
}];
}
}
Can anyone see what I'm doing wrong?
Thanks in advance to all who reply.
if (_index.row == [_tableData count])
Assuming _tableData is the array that feeds your table, the count of the data in there will be one more than the row of your last index, since indexes are zero-based.
By this I mean, if there are 10 objects in your array, the last row is row 9.
So your check needs to be
if (_index.row + 1 == [_tableData count])
Instead.

Resources