Animate at re-selected index not working - ios

I implemented corePlot in my xcode project using the pie chart. I'm having trouble the animation. When a slice gets selected, the radial offset changes with animation. Here is the code:
- (void)pieChart:(CPTPieChart *)plot sliceWasSelectedAtRecordIndex:(NSUInteger)idx
{
if (self.selectedIndex == idx)
{
[self animateSelectedIndex:20 :0];
self.selectedIndex = -1;
}
else
{
self.selectedIndex = idx;
[self animateSelectedIndex:0 :20];
}
}
- (void)animateSelectedIndex:(CGFloat)from :(CGFloat)to
{
[CPTAnimation animate:self
property:#"sliceOffset"
from:from
to:to
duration:0.25
animationCurve:CPTAnimationCurveCubicInOut
delegate:nil];
}
- (CGFloat)radialOffsetForPieChart:(CPTPieChart *)pieChart recordIndex:(NSUInteger)idx
{
return idx == self.selectedIndex ? sliceOffset : 0.0;
}
- (void)setSliceOffset:(CGFloat)newOffset
{
if (newOffset != sliceOffset) {
sliceOffset = newOffset;
[self.graph reloadData];
}
}
The problem is, when a slice gets re-selected, the slice's radial offset goes to 0, but it doesn't animate to 0. (When it goes from 0 to 20 it does animate. The only problem is when it goes from 20 to 0)

Clearing the selectedIndex drives the radial offset to zero right away. Don't clear it until after the animation finishes. You can use an animation delegate or clear it in -setSliceOffset: after the offset reaches zero.

Related

UIAccessibilityScrollDirection won't scroll good

I'm using MBXPageViewController as a UIPageViewController.
I'm trying to make the app more accessible with the UIAccessibilityScrollDirection method.
This works almost fine, but has a couple of issues:
The scrollDirection makes 2 steps to the right at first. I've logged it and give a currentIndex from 0 -> 2.
The scrollDirection won't go back to the first childViewController, the currentIndex 0. Voice-over tells there is no object left.
Here is how the accessibilityScroll method look like.
The setPageControllerForIndex:(NSInteger)index direction:(UIPageViewControllerNavigationDirection)direction currentMBXViewController:(id)weakSelf destionation:(NSInteger)destination method is described here
-(BOOL)accessibilityScroll:(UIAccessibilityScrollDirection)direction {
__weak __typeof(&*self)weakSelf = self;
NSInteger tempIndex = _currentPageIndex;
NSLog(#"%ld", (long)tempIndex);
if (direction == UIAccessibilityScrollDirectionRight) {
//Previous Page
if (direction < tempIndex) {
NSLog(#"links");
for (int i = (int)tempIndex-1; i >= direction; i--) {
[self setPageControllerForIndex:i direction:UIPageViewControllerNavigationDirectionReverse currentMBXViewController:weakSelf destionation:direction];
NSLog(#"%d", i);
}
NSLog(#"%ld", direction);
UIAccessibilityPostNotification(UIAccessibilityPageScrolledNotification, #"links");
}
} else if (direction == UIAccessibilityScrollDirectionLeft) {
//Next Page
if (direction > tempIndex) {
NSLog(#"rechts");
for (int i = (int)tempIndex+1; i <= direction; i++) {
[self setPageControllerForIndex:i direction:UIPageViewControllerNavigationDirectionForward currentMBXViewController:weakSelf destionation:direction];
NSLog(#"%d", i);
}
NSLog(#"%ld", direction);
UIAccessibilityPostNotification(UIAccessibilityPageScrolledNotification, #"rechts");
}
}
UIAccessibilityPostNotification(UIAccessibilityPageScrolledNotification, nil);
return YES;
}

how to move one direction in UIScrollView when scrolling

I'm new in objective-c. I create UIScrollView object and add in my view with this code:
height = self.view.frame.size.height;
width = self.view.frame.size.width;
scrollbar = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, width, height)];
scrollbar.delegate = self;
scrollbar.backgroundColor = [UIColor whiteColor];
scrollbar.maximumZoomScale = 1.0;
scrollbar.minimumZoomScale = 1.0;
scrollbar.clipsToBounds = YES;
scrollbar.showsHorizontalScrollIndicator = YES;
scrollbar.pagingEnabled = YES;
[scrollbar setContentSize:CGSizeMake(width*4, height*4)];
[self.view addSubview:scrollbar];
for (int i = 1; i <= 4; i++) {
first = [[FirstViewController alloc]initWithNibName:#"FirstViewController" bundle:nil];
first.view.frame = CGRectMake((i-1)*width, 0, width, height*4);
[scrollbar addSubview:first.view];
switch (i) {
ase 1:
first.view.backgroundColor = [UIColor blueColor];
break;
case 2:
first.view.backgroundColor = [UIColor redColor];
break;
case 3:
first.view.backgroundColor = [UIColor greenColor];
break;
case 4:
first.view.backgroundColor = [UIColor grayColor];
break;
default:
break;
}
}
in my code I add 4 view in my ScrollView with different color now I want when scrolling on my ScrollView detect dx & dy (dx: driving distance on Axis.x & dy:driving distance on Axis.y) and check these two variable and when :
Notic: I want when any one touch on ScrollView and moving touch on Axis (x or y) or touch on Both Axis (x and y) check this :
if (dx > dy) disable horizontal scroll and moving in vertical side!!!
else moving in horizontal side and disable vertical scroll!!!
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
CGRect visibleRect = CGRectMake(scrollView.contentOffset.x, scrollView.contentOffset.y, scrollView.contentOffset.x + scrollView.bounds.size.width, scrollView.contentOffset.y + scrollView.bounds.size.height);
NSLog(#"%f,%f",visibleRect.origin.x,visibleRect.origin.y);
/*NSLog(#"x : %f",scrollView.contentOffset.x);
NSLog(#"y : %f",scrollView.contentOffset.y);*/
if (fabsf(scrollView.contentOffset.x) > fabsf(scrollView.contentOffset.y)) {
NSLog(#"Vertical Side");
}
else {
NSLog(#"Horizontal Side");
}
}
please guide me guys. I can't disable one side and move another side!!! thanks
If i understood you right, you want to disable scrolling in the direction that has been dragged less by the user. If so, UIScrollView already offers an option to do so:
scrollView.directionalLockEnabled = YES;
When this option is set to true UIScrollView will handle the correct direction lock by itself.
For more information look at the documentation: link
You can achieve the result you want adding some code to your delegate. This is my ViewController.m file. -viewDidLoad, #import statements and the other methods are omitted.
#interface ViewController () {
CGPoint initialOffset;
NSInteger direction; // 0 undefined, 1 horizontal, 2 vertical
}
#end
#implementation ViewController
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
// retrieve current offset
CGPoint currentOffset = scrollView.contentOffset;
// do we know which is the predominant direction?
if (direction == 0) {
// no.
CGFloat dx = currentOffset.x - initialOffset.x;
CGFloat dy = currentOffset.y - initialOffset.y;
// we need to decide in which direction we are moving
if (fabs(dx) >= fabs(dy)) {
direction = 1; // horizontal
} else if (fabs(dy) > fabs(dx)) {
direction = 2;
}
}
// ok now we have the direction. update the offset if necessary
if (direction == 1 && currentOffset.y != initialOffset.y) {
// remove y offset
currentOffset.y = initialOffset.y;
// update
[scrollView setContentOffset:currentOffset];
} else if (direction == 2 && currentOffset.x != initialOffset.x) {
currentOffset.x = initialOffset.x;
[scrollView setContentOffset:currentOffset];
}
}
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
{
// store the current offset
initialOffset = scrollView.contentOffset;
// reset flag
direction = 0; // AKA undefined
}
#end
When you start dragging, the delegate will reset the flag direction to "unknown" state, and store the current content offset. After every dragging move, your -scrollViewDidScroll: will be called. There, you decide which is the predominant direction (if this hasn't been done yet) and correct the current scrolling offset accordingly, by removing the x (or y) offset.
I tested this with the same settings you provided, only I used a UIImageView inside UIScrollView and I set up everything via InterfaceBuilder, but it should work fine. Theoretically, with this method you could replace directionLock, but remember that -scrollViewDidScroll is called many times during an action, and every time it rewrites the content offset (if the scrolling is happening in both directions). So if you leave directionLock enabled, you save many of the calls to setContentOffset: that the delegate performs.

UICollectionView interactiveTransitionToCollectionViewLayout crashes with exception

I have a collectionViewController with horizontal collectionView like in Paper App. I added pan gesture to change from one layout to other and I used interactive transition. It works well if you drag and wait when animation is finished, but if you drag faster several times and don't wait animation to be finished or canceled app throw an exception:
Assertion failure in -[UICollectionView _finishInteractiveTransitionShouldFinish:finalAnimation:], /SourceCache/UIKit_Sim/UIKit-2935.137/UICollectionView.m:2691
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'the collection was not prepared for an interactive transition. see startInteractiveTransitionToCollectionViewLayout:completion:'
Gesture handler code :
- (void)oneFingerGesture:(UIPanGestureRecognizer *)sender
{
if (sender.state == UIGestureRecognizerStateEnded ||
sender.state == UIGestureRecognizerStateCancelled)
{
if (self.transitionLayout.transitionProgress > 0.2) {
[self.collectionView finishInteractiveTransition];
} else {
[self.collectionView cancelInteractiveTransition];
}
}else
{
CGPoint point = [sender locationInView:sender.view];
if (sender.state == UIGestureRecognizerStateBegan && !self.transitionLayout && !_isInTransition)
{
invertPan = self.largeLayout == self.collectionView.collectionViewLayout;
UICollectionViewLayout *toLayout = invertPan ? self.smallLayout : self.largeLayout;
self.transitionLayout = [self.collectionView startInteractiveTransitionToCollectionViewLayout:toLayout
completion:^(BOOL completed, BOOL finish) {
self.transitionLayout = nil;
_isInTransition = NO; }];
self.initialTapPoint = point;
_isInTransition = YES;
}else if(sender.state == UIGestureRecognizerStateChanged && self.transitionLayout && _isInTransition)
{
CGFloat distance = _initialTapPoint.y - point.y;
if (invertPan) {
distance = -distance;
}
CGFloat dimension = self.collectionView.bounds.size.height - 200;
CGFloat progress = MAX(MIN(((distance)/ dimension), 1.0), 0.0);
[self.transitionLayout setTransitionProgress:progress];
}
}
}
The documentation states that calls to finishInteractiveTransition: and cancelInteractiveTransition: will install the layout object the transition is going to and from respectively. However, this appears to not happen immediately. Thus, if the gesture triggering and driving the transition can happen in quick succession, it is not sufficient to check whether the current layout is a UICollectionViewTransitionLayout or a subclass thereof before calling one of the two methods to end the transition.
I solved the problem by introducing a BOOL ivar (_isFinishingOrCancellingTransition) to avoid ending a transition that is already in the process of ending:
if (_isFinishingOrCancellingTransition) return;
if (!self.transitionLayout) return;
if (self.collectionView.collectionViewLayout != self.transitionLayout) return;
_isFinishingOrCancellingTransition = YES;
if (self.transitionLayout.transitionProgress > 0.5)
{
[self.collectionView finishInteractiveTransition];
}
else
{
[self.collectionView cancelInteractiveTransition];
}
and then resetting the BOOL when the transition is completed:
self.transitionLayout = [self.collectionView startInteractiveTransitionToCollectionViewLayout:toLayout completion:^(BOOL completed, BOOL finish) {
self.transitionLayout = nil;
_isFinishingOrCancellingTransition = NO;
}];

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.

Sorting views in a grid after a view is moved via gesture to a new position

This is turing into a bit of a brain buster, but I assume there is a very simple and efficient method to do this.
In my app I have views laid out in a grid. Views can be moved to any location via gesture. The problem is making the view sort to just the right location after the gesture event. Basically I am doing this. Works sort of but not correct yet.
Note: the tag of the views will always update to the current position of the view in the grid.
Below is the code I am using to sort, and here is a link to the actual project in a zip file.
-(void)sortViews:(UIView*)myView {
__block int newIndex;
// myView is the view that was moved.
[viewsArray removeObject:myView];
[viewsArray enumerateObjectsUsingBlock:^(UIView *view, NSUInteger idx, BOOL *stop){
if (myView.center.x > view.center.x) {
if (myView.center.y > view.center.y) {
newIndex = view.tag -1;
*stop = YES;
} else {
newIndex = view.tag +1;
*stop = YES;
}
} else if (myView.center.x < view.center.x) {
if (myView.center.y > view.center.y) {
newIndex = view.tag -1;
*stop = YES;
} else {
newIndex = view.tag +1;
*stop = YES;
}
}
}];
if (newIndex < 0) {
newIndex = 0;
} else if (newIndex > 5) {
newIndex = 5;
}
[viewsArray insertObject:myView atIndex:newIndex];
[UIView animateWithDuration:.4 animations:^{
[self arrangeGrid];
}];
}
You can store the original positions of all the views in an array (lets say positionArray), and then when you are moving a view "A" just go on checking the current position of view "A" with the stored positions in the positionArray, if a view is in range of any stored location just swap the position of view "A" with the view in whose range view "A" has come.
Hope this will help you
Found an easier solution that seems to work just fine. I replace the code within the viewsArray enumeration with this:
if (CGRectContainsPoint(view.frame, myView.center)) {
newIndex = view.tag;
*stop = YES;
}

Resources