Drawer comes little late using MCPanelViewController - ios

I used MCPanelViewController in an iOS app for drawing left and right controllers (As android has inbuilt control drawer). It works good but it does not come with gesture it comes little late. if anyone had an answer to problem it would be great.
This is the code in UIViewController (MCPanelViewControllerInternal) category:
- (void)handlePan:(UIPanGestureRecognizer *)pan {
// initialization for screen edge pan gesture
if ([pan isKindOfClass:[UIScreenEdgePanGestureRecognizer class]] &&
pan.state == UIGestureRecognizerStateBegan) {
__weak UIViewController *controller = objc_getAssociatedObject(pan, &MCPanelViewGesturePresentingViewControllerKey);
if (!controller) {
return;
}
MCPanelAnimationDirection direction = [objc_getAssociatedObject(pan, &MCPanelViewGestureAnimationDirectionKey) integerValue];
[self setupController:controller withDirection:direction];
CGPoint translation = [pan translationInView:pan.view];
CGFloat width = direction == MCPanelAnimationDirectionLeft ? translation.x : -1 * translation.x;
[self layoutSubviewsToWidth:0];
__weak typeof(self) weakSelf = self;
[UIView animateWithDuration:MCPanelViewAnimationDuration delay:0 options:0 animations:^{
typeof(self) strongSelf = weakSelf;
[strongSelf layoutSubviewsToWidth:width];
} completion:^(BOOL finished) {
}];
CGFloat offset = self.maxWidth - width;
if (direction == MCPanelAnimationDirectionLeft) {
offset *= -1;
}
[pan setTranslation:CGPointMake(offset, translation.y) inView:pan.view];
}
if (!self.parentViewController) {
return;
}
CGFloat newWidth = [pan translationInView:pan.view].x;
if (self.direction == MCPanelAnimationDirectionRight) {
newWidth *= -1;
}
newWidth += self.maxWidth;
CGFloat ratio = newWidth / self.maxWidth;
switch (pan.state) {
case UIGestureRecognizerStateBegan:
case UIGestureRecognizerStateChanged: {
[self layoutSubviewsToWidth:newWidth];
break;
}
case UIGestureRecognizerStateEnded:
case UIGestureRecognizerStateCancelled: {
CGFloat threshold = MCPanelViewGestureThreshold;
// invert threshold if we started a screen edge pan gesture
if ([pan isKindOfClass:[UIScreenEdgePanGestureRecognizer class]]) {
threshold = 1 - threshold;
}
if (ratio < threshold) {
[self dismiss];
}
else {
__weak typeof(self) weakSelf = self;
[UIView animateWithDuration:MCPanelViewAnimationDuration delay:0 options:UIViewAnimationOptionBeginFromCurrentState animations:^{
typeof(self) strongSelf = weakSelf;
[strongSelf layoutSubviewsToWidth:strongSelf.maxWidth];
} completion:^(BOOL finished) {
}];
}
break;
}
default:
break;
}
}

This is a known issue of the library.
To quote matthewcheok:
The delay is mainly due to way the panel captures the background image to apply the image blur in advance. It does this exactly once, when the gesture is triggered or when opened programatically, and then crops the image using UIView content modes, to show only the relevant parts obscured by the panel.

Related

Animations not working properly

I present a view controller modally in my application. I'd like for the user to be able to "flick" the view away with a gesture. I wrote the code below for that:
- (void)handlePan:(UIPanGestureRecognizer *)recognizer {
CGFloat elasticThreshold = 100;
CGFloat dismissThreshold = 200;
CGPoint translation = [recognizer translationInView:self.view];
CGFloat newY = 0;
CGFloat translationFactor = 0.5;
if (recognizer.state == UIGestureRecognizerStateEnded) {
if (translation.y < dismissThreshold) {
newY = 0;
}
} else {
if (translation.y > elasticThreshold) {
CGFloat frictionLength = translation.y - elasticThreshold;
CGFloat frictionTranslation = 30 * atan(frictionLength/120) + frictionLength/10;
newY = frictionTranslation + (elasticThreshold * translationFactor);
} else {
newY = translation.y*translationFactor;
}
}
if (translation.y > dismissThreshold) {
[UIView animateKeyframesWithDuration:0.5 delay:0.0 options:0 animations:^{
[UIView addKeyframeWithRelativeStartTime:0.0 relativeDuration:0.5 animations:^{
self.overlay.effect = nil;
self.collectionView.transform = CGAffineTransformMakeTranslation(0, self.view.frame.size.height);
}];
[UIView addKeyframeWithRelativeStartTime:0.1 relativeDuration:0.1 animations:^{
self.pageControl.frame = CGRectMake((self.view.frame.size.width-200)/2, self.view.frame.size.height, 200, 20);
}];
} completion:^(BOOL finished) {
if (finished) {
[self dismissViewControllerAnimated:YES completion:nil];
}
}];
} else {
self.collectionView.transform = CGAffineTransformMakeTranslation(0, newY);
self.pageControl.transform = CGAffineTransformMakeTranslation(0, (newY+self.collectionView.frame.size.height)-20);
}
}
This is hooked up to a UIGestureRecognizer:
self.pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(handlePan:)];
self.pan.delegate = self;
self.pan.maximumNumberOfTouches = 1;
[self.view addGestureRecognizer:self.pan];
However, what happens is that the completion block executes immediately. So you'll see the view move down (because of dismissViewControllerAnimated) and at the same time see the overlay.effect go away. However, what I would like is for my animations to happen and then for the view controller to dismiss itself silently.
Any ideas what's going wrong here?
This occurs because you are nesting UIView animation blocks. You should use dispatch_after for this:
double delayInSeconds = 0.5;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
[self dismissViewControllerAnimated:YES completion:nil];
});
A UIView keyframe (or standard) animation will execute its completion block immediately if there is no work to be done. It won't wait for the duration of the animation if there is nothing to animate.
It seems that in your case when you are kicking off the dismiss animation, the views are already in their final state, hence there is no animation and the completion block is run immediately.

How to avoid tilt/flip on CATransform3D rotation in iOS?

I used below function for 3dRotation on view. But i don't want tilt/flip on view, I just want to left/right/up/down movement on view.
How i avoid tilt and flip rotation on my view ?
- (void)Move3dPan:(UIPanGestureRecognizer *)gesture
{
if (gesture.state == UIGestureRecognizerStateChanged)
{
CGPoint displacement = [gesture translationInView:self.view];
CATransform3D currentTransform = self.popUpView.layer.sublayerTransform;
if (displacement.x==0 && displacement.y==0)
{
// no rotation, nothing to do
return;
}
CGFloat totalRotation = sqrt(displacement.x * displacement.x + displacement.y * displacement.y) * M_PI / 360.0;
CGFloat xRotationFactor = displacement.x/totalRotation;
CGFloat yRotationFactor = displacement.y/totalRotation;
CATransform3D rotationalTransform = CATransform3DRotate(currentTransform, totalRotation,
(xRotationFactor * currentTransform.m12 - yRotationFactor * currentTransform.m11),
(xRotationFactor * currentTransform.m22 - yRotationFactor * currentTransform.m21),
(xRotationFactor * currentTransform.m32 - yRotationFactor * currentTransform.m31));
[CATransaction setAnimationDuration:0];
self.popUpView.layer.sublayerTransform = rotationalTransform;
[gesture setTranslation:CGPointZero inView:self.view];
}
}
-(void) startAnimatingCupView
{
[self stopAnimatingCupView];
timer = [NSTimer scheduledTimerWithTimeInterval:0.8 repeats:true block:^(NSTimer * _Nonnull timer) {
[self animateCupView];
}];
}
-(void) animateCupView{
[UIView animateWithDuration:0.4 animations:^{
self.imgCup.transform = CGAffineTransformMakeRotation(DEGREES_TO_RADIANS(180));
} completion:^(BOOL finished) {
[UIView animateWithDuration:0.4 animations:^{
self.imgCup.transform = CGAffineTransformMakeRotation(DEGREES_TO_RADIANS(360));
}];
}];
}
-(void) stopAnimatingCupView{
if (timer != nil) {
[timer invalidate];
timer = nil;
}
[self.imgCup.layer setTransform:CATransform3DIdentity];
}
i hope this will work

UIView/Drawer using Pan/Swipe Gesture

I am trying to make a UIView open & close on Swipe/Pan Gesture & i found some help from following link
Link , it's close to what i m trying to make.
I want UIView to be open by 100 pixels default & User can swipe/pan the UIView using gesture till 75% of the parent UIViewController & back to 100 pixels but it's flicking in this below code. I want UIView's X position to be 0 so it can be like a drawer opening from top.
- (void)viewDidLoad {
[super viewDidLoad];
UIPanGestureRecognizer *panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(move:)];
drawerView = [self.storyboard instantiateViewControllerWithIdentifier:#"drawerVC"];
[drawerView.view setFrame:CGRectMake(0, -self.view.frame.size.height + 100, self.view.frame.size.width, self.view.frame.size.height * 0.75)];
[drawerView.view setBackgroundColor:[UIColor redColor]];
[drawerView.view addGestureRecognizer:panGesture];
}
-(void)move:(UIPanGestureRecognizer*)recognizer {
recognizer.view.center = CGPointMake(self.view.frame.size.width/2,
recognizer.view.center.y + translation.y);
[recognizer setTranslation:CGPointMake(0, 0) inView:self.view];
if (recognizer.state == UIGestureRecognizerStateEnded) {
CGFloat magnitude = sqrtf((velocity.x * velocity.x) + (velocity.y * velocity.y));
CGFloat slideMult = magnitude / 200;
NSLog(#"magnitude: %f, slideMult: %f", magnitude, slideMult);
float slideFactor = 0.1 * slideMult; // Increase for more of a slide
CGPoint finalPoint = CGPointMake(0,
recognizer.view.center.y + (velocity.y * slideFactor));
finalPoint.x = 0;
finalPoint.y = MIN(MAX(finalPoint.y, 0), drawerView.view.frame.size.height*.75);
if (fabs(recognizer.view.frame.origin.y) >= fabs(yOffset))
{
return;
}
NSLog(#"ended %f",finalPoint.y);
if (finalPoint.y < recognizer.view.frame.size.height/2) {
// [self movePanelToOriginalPosition];
}
else{
[self movePanelToCenterPosition];
}
}
}
-(void)movePanelToCenterPosition {
[UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationOptionCurveEaseIn animations:^{
drawerView.view.frame = CGRectMake(0, 0, drawerView.view.frame.size.width, drawerView.view.frame.size.height);
}
completion:^(BOOL finished) {
// Stuff to do
}];
}
Is there anything that can prevent user to pan UIView in Top(Up) direction if UIView is at default(100 pixels) & can only swipe down to desired CGPoint.
In your move: method you check if the move start or is in progress
else if (recognizer.state == UIGestureRecognizerStateBegin || recognizer.state == UIGestureRecognizerStateChanged)" and in the if view is at its limit. if so you can disabled/reenabled the gesture recognizer. This will cancel the pan...
- (void) move:(UIGestureRecognizer *)sender
{
if(sender.state == UIGestureRecognizerStateBegan || sender.state == UIGestureRecognizerStateChanged)
{
BOOL shouldEnablePan = NO; // TODO: do some logic here to figure out if you want to allow pan
if(!shouldEnablePan && [sender isKindOfClass:[UIPanGestureRecognizer class]])
{
sender.enabled = NO;
sender.enabled = YES;
}
} else ...
}

Swipe UIView off screen

I'd like to be able to have a UIView (or, if necessary, UIViewController), which can be 'dragged'/flicked/swiped off screen - but locked to the vertical axis (so the view would go up or down off the screen to show the view below). This is sort of like the Facebook app's web views.
Could this perhaps be achieved with UIGravityBehavior?
Any ideas would be fantastic.
I have made something similar a while ago by using a UIPanGestureRecognizer. Here is how this would work:
float slidingMenuOffsetSum;
- (void)handleSlidingMenuPan:(UIPanGestureRecognizer *)gestureRecognizer {
CGPoint translation = [gestureRecognizer translationInView:_slidingMenuView];
slidingMenuOffsetSum += translation.y;
CGPoint newSlidingMenuCenter = CGPointMake(_slidingMenuView.center.x, _slidingMenuView.center.y + translation.y);
if (_slidingMenuView.frame.origin.y <= 512 && _slidingMenuView.frame.origin.y >= 0) {
_slidingMenuView.center = newSlidingMenuCenter;
}
[gestureRecognizer setTranslation:CGPointZero inView:_slidingMenuView];
if (gestureRecognizer.state == UIGestureRecognizerStateEnded) {
[UIView animateWithDuration:0.3
animations:^{
[_slidingMenuView setUserInteractionEnabled:false];
if (slidingMenuOffsetSum > 0) {
if (slidingMenuOffsetSum > _slidingMenuView.frame.size.height / 4) {
[self setSlidingMenuHidden:false];
} else {
[self returnSlidingMenuToPosition];
}
} else {
if (slidingMenuOffsetSum < -_slidingMenuView.frame.size.height / 4) {
[self setSlidingMenuHidden:true];
} else {
[self returnSlidingMenuToPosition];
}
}
}
completion:^(BOOL finished) {
slidingMenuOffsetSum = 0;
[_slidingMenuView setUserInteractionEnabled:true];
}];
}
}
You can adjust the values at which the sliding menu can move, and the thresholds for detecting if the view should go back to its original position, or eg. go off the screen.

Animation from Pan Gesture begins from initial state instead of current state

In my application I have a view that sits on top of another view. The user is able to swipe the top view to the right, which fades it out and "fades in" the back view.
When the user pans to the right and lets go, the animation kicks in and it should translate from its current position to the new off screen position. The problem is that the animation instead snaps the view back to its starting point and then does the animation.
Here is my code for the pan gesture and animation:
- (void)handlePanGesture:(UIPanGestureRecognizer *)recognizer
{
CGPoint translation = [recognizer translationInView:recognizer.view];
CGPoint velocity = [recognizer velocityInView:recognizer.view];
switch (recognizer.state) {
case UIGestureRecognizerStateBegan: {
[recognizer setTranslation:CGPointMake(self.slidingView.frame.origin.x, 0) inView:recognizer.view];
break;
}
case UIGestureRecognizerStateChanged: {
[self.slidingView setTransform:CGAffineTransformMakeTranslation(MAX(0,translation.x), 0)];
CGFloat percentage = fmaxf(0,(translation.x/recognizer.view.bounds.size.width));
[self.backgroundBlurImageView setAlpha:1-percentage];
[self.slidingView setAlpha:1-percentage];
[self.backgroundImageView setTransform:CGAffineTransformMakeScale(1 + (percentage * 0.05), 1 + (percentage * 0.05))];
[self.backgroundBlurImageView setTransform:CGAffineTransformMakeScale(1 + (percentage * 0.05), 1 + (percentage * 0.05))];
break;
}
case UIGestureRecognizerStateEnded:
case UIGestureRecognizerStateCancelled: {
if (velocity.x > 5.0 || (velocity.x >= -1.0 && translation.x > kMenuViewControllerMinimumPanDistanceToOpen * self.slidingView.bounds.size.width)) {
CGFloat transformedVelocity = velocity.x/ABS(self.slidingView.bounds.size.width - translation.x);
CGFloat duration = 0.66;
[self showViewAnimated:YES duration:duration initialVelocity:transformedVelocity];
} else {
[self hideViewAnimated:YES];
}
}
default:
break;
}
}
- (void)showViewAnimated:(BOOL)animated duration:(CGFloat)duration
initialVelocity:(CGFloat)velocity;
{
// animate
__weak typeof(self) blockSelf = self;
[UIView animateWithDuration:animated ? duration : 0.0 delay:0
usingSpringWithDamping:0.8f initialSpringVelocity:velocity options:UIViewAnimationOptionAllowUserInteraction animations:^{
blockSelf.slidingView.transform = CGAffineTransformMakeTranslation(blockSelf.slidingView.bounds.size.width, 0);
[blockSelf.backgroundBlurImageView setTransform:CGAffineTransformMakeScale(1.05, 1.05)];
[blockSelf.backgroundImageView setTransform:CGAffineTransformMakeScale(1.05, 1.05)];
[blockSelf.backgroundBlurImageView setAlpha:0];
[blockSelf.slidingView setAlpha:0];
} completion:^(BOOL finished) {
blockSelf.tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(didTapOnAaron:)];
[blockSelf.view addGestureRecognizer:self.tapGestureRecognizer];
}];
}
- (void)hideViewAnimated:(BOOL)animated;
{
__weak typeof(self) blockSelf = self;
[UIView animateWithDuration:0.3f animations:^{
blockSelf.slidingView.transform = CGAffineTransformIdentity;
[blockSelf.backgroundBlurImageView setTransform:CGAffineTransformIdentity];
[blockSelf.backgroundImageView setTransform:CGAffineTransformIdentity];
[blockSelf.backgroundBlurImageView setAlpha:1];
[blockSelf.slidingView setAlpha:1];
} completion:^(BOOL finished) {
[blockSelf.view removeGestureRecognizer:self.tapGestureRecognizer];
blockSelf.tapGestureRecognizer = nil;
}];
}
I have already tried settings the animation options to:
UIViewAnimationOptionBeginFromCurrentState
with no effect.
Anyone have a clue what I am missing? Thanks
Kyle, sometimes this sort of thing happens if you are using autolayout. If you don't need it try disabling it and see if it fixes it.

Resources