Reordering rotated uitableviewcell - ios

I've got a uitableview that I've rotated 180 degrees but whose cells I've rotated back -180 degrees. The effect is that I am loading my data from the bottom of the table view. This works great. The problem comes when I try to reorder the cells. If I try to drag a cell its movement is inverted and jumps immediately to the top/bottom of the table view, depending on which direction I was dragging.
The easiest way to see this is by starting a new project using the master detail template with core data. In the OIMasterViewConroller.m file place
CGAffineTransform transform = CGAffineTransformMakeRotation(-3.14159);
self.tableView.transform = transform;
in the viewDidLoad method, and place
CGAffineTransform transform = CGAffineTransformMakeRotation(-3.14159);
cell.transform = transform;
in the configureCell method. Finally, change the canMoveRowAtIndexPath to return YES and implement a blank
-(void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath {
}
Run the project and add a few items to the tableview with the + button. Then Click on the edit button and try to reorder.
I believe the problem lies with the cell since removing the rotation on the cell will stop this strange behavior, even though the table is upside down.
I thought about just rotating the text inside the cell, but then the delete buttons are upside down, as well as on the wrong side.

So, I ended up subclassing both the tableview and the cell used in the table view. Here is some of what I've done:
In the tableview subclass I overload the layoutsubviews:
- (void)layoutSubviews
{
[super layoutSubviews];
for (UIView *subview in self.subviews) {
if ([NSStringFromClass([subview class]) isEqualToString:#"UIShadowView"]) {
subview.hidden = YES;
}
}
}
This removes the drop shadow. If you don't the shadow shows at the top of the tableviewcells when they are being reordered.
For the tableviewcell subclass I changed its layoutSubviews like this:
- (void)layoutSubviews
{
[super layoutSubviews];
for (UIView *subview in self.subviews) {
if ([NSStringFromClass([subview class]) isEqualToString:#"UITableViewCellDeleteConfirmationControl"]) {
CGRect newFrame = subview.frame;
newFrame.origin.x = 0;
subview.frame = newFrame;
subview.transform = CGAffineTransformMakeRotation(-M_PI);
}
else if ([NSStringFromClass([subview class]) isEqualToString:#"UITableViewCellEditControl"]) {
CGRect newFrame = subview.frame;
newFrame.origin.x = 320-newFrame.size.width;
subview.frame = newFrame;
subview.transform = CGAffineTransformMakeRotation(-M_PI);
}
else if ([NSStringFromClass([subview class]) isEqualToString:#"UITableViewCellReorderControl"]) {
CGRect newFrame = subview.frame;
newFrame.origin.x = 0;
subview.frame = newFrame;
}else if([NSStringFromClass([subview class]) isEqualToString:#"UIButton"]) {
CGRect newFrame = subview.frame;
newFrame.origin.x = 0;
subview.frame = newFrame;
subview.transform = CGAffineTransformMakeRotation(-M_PI);
}else{
subview.transform = CGAffineTransformMakeRotation(-M_PI);
}
}
}
This rotates and moves all the delete buttons/indicators ect. The important thing to note is that we DO NOT rotate the UITableViewCellReorderControll. Rotating this subview is what makes the reordering messed up.

Related

UIKit Dynamics: Attachment inside UITableViewCell

My table view cells contain a circle in an UIView, indicating a value. I want to add the UIKit Dynamics attachment behaviour to that circle in order to for it to lag a bit when scrolling.
I don't want to attach the individual cells to each other but only the circle view to the UITableViewCell. The rest of the cell should scroll as usual.
Problem: The UITableViewCell has its origin always at (0, 0). How can I add the circle to a view that actually does move when scrolling?
I finally got it to work. The UITableView moves the coordinate system of every cell and of all views contained within that cell. Therefor I needed to manually move my view inside the UITableViewCell during scrolling while still referring to the initial anchor point.
The table view controller:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
BOOL scrollingUp = '\0';
if (self.lastContentOffset > scrollView.contentOffset.y) {
scrollingUp = YES;
}
else if (self.lastContentOffset < scrollView.contentOffset.y) {
scrollingUp = NO;
}
NSInteger offset = 64; // To compensate for the navigation bar.
if (scrollingUp) {
offset = offset - scrollView.contentOffset.y;
}
else {
offset = offset + scrollView.contentOffset.y;
}
// Limit the offset so the views will not disappear during fast scrolling.
if (offset > 10) {
offset = 10;
}
else if (offset < -10) {
offset = -10;
}
// lastContentOffset is an instance variable.
self.lastContentOffset = scrollView.contentOffset.y;
for (UITableViewCell *cell in self.tableView.visibleCells) {
// Use CoreAnimation to prohibit flicker.
[UIView beginAnimations:#"Display notification" context:nil];
[UIView setAnimationDuration:0.5f];
[UIView setAnimationBeginsFromCurrentState:YES];
cell.view.frame = CGRectMake(cell.view.frame.origin.x, offset, cell.view.frame.size.width, cell.view.frame.size.height);
[UIView commitAnimations];
[cell.dynamicAnimator updateItemUsingCurrentState:cell.view];
}
}
The table view cell:
-(void)layoutSubviews {
[super layoutSubviews];
// _view is the animated UIView.
UIDynamicItemBehavior *viewBehavior = [[UIDynamicItemBehavior alloc] initWithItems:#[_view]];
viewBehavior.elasticity = 0.9f;
UIAttachmentBehavior *attachmentBehaviorView = [[UIAttachmentBehavior alloc] initWithItem:_view attachedToAnchor:CGPointMake(_anchorView.frame.origin.x + _anchorView.frame.size.width / 2.0f, _anchorView.frame.origin.y + _anchorView.frame.size.height / 2.0f)];
attachmentBehaviorView.damping = 8.0f;
attachmentBehaviorView.frequency = 4.0f;
attachmentBehaviorView.length = 0.0f;
[_dynamicAnimator addBehavior:viewBehavior];
[_dynamicAnimator addBehavior:attachmentBehaviorView];
}
You can change the anchorPoint of UIAttachmentBehavior during -[scrollViewDidScroll:]. You may refer to the following code snippet:
- (void)viewDidLoad
{
UIDynamicAnimator *animator = [[UIDynamicAnimator alloc] initWithReferenceView:self.view];
UIAttachmentBehavior *behavior1 = [[UIAttachmentBehavior alloc] initWithItem:self.circleView
attachedToAnchor:[self tableViewAnchor]];
behavior1.length = 10.0;
behavior1.damping = 0.3;
behavior1.frequency = 2.5;
[animator addBehavior:behavior1];
}
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
behavior1.anchorPoint = [self.tableView convertPoint:[self tableViewAnchor] toView:self.view];
}
- (CGPoint)tableViewAnchor
{
return CGPointMake(160.0, 154.0); // return your target coordination w.r.t. the table view
}
Preview:

Changing the position of the red minus button in a UITableViewCell while in Edit Mode

This code worked great inside a UITableViewCell subclass on iOS 5 and 6:
if ([NSStringFromClass([subview class]) isEqualToString:#"UITableViewCellEditControl"]) {
CGRect newFrame = subview.frame;
//Use your desired x value
newFrame.origin.x = 280;
subview.frame = newFrame;
}
While debugging my app on iOS 7 i've found that all the subviews above are called UITableViewCellContentView and there is no way knowing where the UITableViewCellEditControl subview is.
Is there a better solution for doing the above?
While debugging this I've found that all the subviews in iOS 7 are now called 'UITableViewCellEditControl". I tried logging all the subviews subviews and found that UITableViewCellEditControl is now a subview of a subview. This is an ugly temporary solution:
for (UIView *subview in self.subviews)
{
if ([NSStringFromClass([subview class]) isEqualToString:#"UITableViewCellEditControl"]) {
CGRect newFrame = subview.frame;
newFrame.origin.x = 280;
subview.frame = newFrame;
}
else
{
if(IS_OS_7_OR_LATER)
{
for(UIView *subsubview in subview.subviews)
{
if ([NSStringFromClass([subsubview class]) isEqualToString:#"UITableViewCellEditControl"]) {
CGRect newFrame = subsubview.frame;
newFrame.origin.x = 280;
subsubview.frame = newFrame;
}
}
}
}

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

Change UITableViewCell's minus sign animation

I have an issue with editing table view:
I changed the minus sign location in edit mode to the right side of the cell.
The problem is that the button is sliding from the left and it makes it looks weird.
Is there a way to change this animation?
Also, there is an animation when undoing the delete option (clicking the minus sign when the red delete button is displayed), any idea why?
Here is a video showing the issue:
---edit---
This is how I changed the position:
- (void)layoutSubviews {
[super layoutSubviews];
//Indent to the left instead of right
self.contentView.frame = CGRectMake(0,
self.contentView.frame.origin.y,
self.contentView.frame.size.width,
self.contentView.frame.size.height);
if ((self.editing
&& ((state & UITableViewCellStateShowingEditControlMask)
&& !(state & UITableViewCellStateShowingDeleteConfirmationMask))) ||
((state & UITableViewCellStateShowingEditControlMask)
&& (state & UITableViewCellStateShowingDeleteConfirmationMask)))
{
float indentPoints = self.indentationLevel * self.indentationWidth;
self.contentView.frame = CGRectMake(indentPoints - 35,
self.contentView.frame.origin.y,
self.contentView.frame.size.width - indentPoints,
self.contentView.frame.size.height);
}
//Change editAccessoryView location
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView setAnimationDuration:0.0f];
//If can't use private classes (UITableViewCellDeleteConfirmationControl..), use [self.subviews objectAtIndex:0];
for (UIView *subview in self.subviews) {
if ([NSStringFromClass([subview class]) isEqualToString:#"UITableViewCellDeleteConfirmationControl"]) {
CGRect newFrame = subview.frame;
newFrame.origin.x = 10;
subview.frame = newFrame;
}
else if ([NSStringFromClass([subview class]) isEqualToString:#"UITableViewCellEditControl"]) {
CGRect newFrame = subview.frame;
newFrame.origin.x = 280;
subview.frame = newFrame;
}
else if ([NSStringFromClass([subview class]) isEqualToString:#"UITableViewCellReorderControl"]) {
CGRect newFrame = subview.frame;
newFrame.origin.x = 200;
subview.frame = newFrame;
}
}
[UIView commitAnimations];
}
- (void)willTransitionToState:(UITableViewCellStateMask)aState {
[super willTransitionToState:aState];
self.state = aState;
}
---edit2---
I identified that the part that causes the minus sign jumping issue is the
//Change editAccessoryView location.
Without it there is no jumping but the minus button is back on the left side of the cell.
Any way around that?
Just a guess, but have you tried turning the view upside-down? You might be able to use a UIView transform.
(Code from the Internet, not sure how reliable it is)
CGAffineTransform rotateTransform;
rotateTransform = CGAffineTransformRotate(CGAffineTransformIdentity, M_PI);
myView.transform = rotateTransform;

Custom UITableCell: Why delete icon appears on the content of the cell view?

I have custom cell in my tableview. when uitable view is in the editing mode. the rounded delete icon appears on the content of the cell.
How do I move the content to the right so to make place for the that little red rounded button.
In custom cell view layoutsubview should happen something. which subview of cell view should i resize?
thanks in advance
please take a look at my current code
- (void) layoutSubviews
{
[super layoutSubviews];
CGFloat width = self.width - _productImage.right - 20.f;
if (self.accessoryView)
{
width -= self.accessoryView.width;
}
_productName.width = width;
_productDescription.width = width;
_productPrice.width = width;
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView setAnimationDuration:3.0f];
for (UIView *subview in self.subviews) {
if ([NSStringFromClass([subview class]) isEqualToString:#"UITableViewCellDeleteConfirmationControl"]) {
CGRect newFrame = self.contentView.frame;
self.contentView.frame = CGRectMake(40, self.contentView.frame.origin.y, newFrame.size.width, newFrame.size.height);
}
else if ([NSStringFromClass([subview class]) isEqualToString:#"UITableViewCellEditControl"]) {
CGRect newFrame = self.contentView.frame;
self.contentView.frame = CGRectMake(40, self.contentView.frame.origin.y, newFrame.size.width, newFrame.size.height);
}
else if ([NSStringFromClass([subview class]) isEqualToString:#"UITableViewCellReorderControl"]) {
CGRect newFrame = self.contentView.frame;
self.contentView.frame = CGRectMake(40, self.contentView.frame.origin.y, newFrame.size.width, newFrame.size.height);
}
}
[UIView commitAnimations];
}
You can check the tableview.isEditing property.
If that is YES, then update the cell.contentView frame to x-difference otherwise use the default one.
Hope this is what you required.
Enjoy Coding :)

Resources