I'm having an annoying problem with my UIView animation. Basically I'm doing a UIView rotation animation by following the finger position. Everything is working fine when the view is fully loaded but if the user drags his finger while new data (view content) is incoming from my web service the view is jumping off the screen. When the users scrolls a bit the view jumps back into it's position and everything is fine. So what can I do to prevent this jumping.
I think the incoming data triggers something like layoutIfNeeded and this effects the animation position.
Another idea is that it clashes with my autoLayout constraints.
The animation is done like this:
- (IBAction)handlePan:(UIPanGestureRecognizer *)gesture {
UIView *currentView = self.viewToAnimate;
CGPoint translation = [gesture translationInView:gesture.view.superview];
CGFloat percent = translation.x / gesture.view.bounds.size.width;
CATransform3D transform = CATransform3DIdentity;
transform.m34 = 1.0 / -800;
currentView.layer.transform =
CATransform3DRotate(transform, M_PI * percent, 0.0, 1.0, 0.0);
//Snap back if the users lifts his finger
if (gesture.state == UIGestureRecognizerStateEnded ||
gesture.state == UIGestureRecognizerStateCancelled ||
gesture.state == UIGestureRecognizerStateFailed) {
[UIView animateWithDuration:0.45
delay:0.0
options:UIViewAnimationOptionCurveEaseInOut
animations:^{ currentView.transform = CGAffineTransformIdentity; }
completion:nil];
}
}
There is way too little information on what and how you are doing to solve this directly. What type are these views of, are you generating views programatically...
Most likely what is going on is one of 2 scenarios.
First the view itself is calling self to refresh in which case it sets its transform ti identity. If this is the case all you need to do is add this view to another superview and apply transform to that superview rather then the view itself.
Second possibility is the superview is calling the view to reposition. In this case you might want to break such operations by possibly setting some parameters on one of the views for this to stop doing automatically or try to analyse where this call comes from.
To analyse what is calling this view to reposition I suggest you to subclass your view and if needed even the layer and override the methods such as setFrame, setTransform and add breakpoints there.
I managed to get it working with [self.viewToAnimate setTranslatesAutoresizingMaskIntoConstraints:YES];.
I think my constraints repositioned the view during the loading because the view was growing. After reducing the constraint priority in IB and setting translatesAutoresizingMaskIntoConstraints to YES everything is working as expected.
Related
I have scrollview and inside scrollview I have 2 views as MainView and another as SideMenuView.
What I want to make animation like below.
Any idea what needs to be done to get this working?
Psuedo Code below:
- (void)animateSideMenu{
homeView.frame = CGRectMake(sideMenuWidth, 0.0, (self.view.frame.size.width - sideMenuWidth), self.view.frame.size.height);
[UIView animateWithDuration:1.0 delay:0.0
options:UIViewAnimationOptionCurveLinear
animations:^{
sideMenu.frame = CGRectMake(0.0, 0.0, sideMenuWidth, sideMenuHeight);
[self flipAnimation];
} completion:^(BOOL finished) {
}];
}
- (void)flipAnimation{
CABasicAnimation *yRotate = [CABasicAnimation animationWithKeyPath:#"transform.rotation.y"];
yRotate.fromValue = [NSValue valueWithCATransform3D:CATransform3DMakeRotation(M_PI_2, 0, 1, 0)];
yRotate.toValue = #(M_PI * 1.5);
yRotate.duration = 0.5;
yRotate.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
[sideMenu.layer addAnimation:yRotate forKey:#"yRotate"];
}
Below are the steps to develop this type of animation:
set the frame of homeView(Red colour view) of screen size & add pan gesture to this view.
set frame of sideMenu view in negative x-axis.
Create a responder function for pan gesture recogniser, in this function call the above mentioned animateSideMenu function.
Adjust the animation parameters accordingly.
Try with this & let me know if anything comes up.
I would not use a scrollview but rather a UIPanGestureRecognizer in which I would detect your current "pan offset" and calculate a current fraction of the animation and positions of those two views (let's simplify it so that the menu is view1 and the rest view2).
Then I would simply set an appropriate transform in the .Changed state.
In .Ended state I would see whether the menu is closer to being open or close and create an animation with .toValue set accordingly. You should use CAAnimations or better - Facebook pop animations because you can pause and remove them and the views stay put and not jump to their initial positions (as is often the case with UIViewAnimate).
If you need help with writing exact code you can write here and I'll edit my answer.
There's tutorial for exactly that menu, though it's in Swift:
https://www.raywenderlich.com/87268/3d-effect-taasky-swift
Problem: I have a pinch gesture recognizer on a View Controller which I'm using to scale an image nested inside the View Controller. The transform below works fine, except that the image is scaled from the upper left instead of the center. I want it to scale from the center.
Setup:
a UIImageView set to Aspect Fill mode (nested within a few views, origin set to center).
a UIPinchGestureRecognizer on the container View Controller
I verified:
anchorPoint for image view is (0.5, 0.5)
the center is moving after every transform
no auto layout constraints on the view or its parent (at least at build time)
Also, I tried setting center of the UIImageView after the transform, the change doesn't take effect until after the user is done pinching.
I don't want to center the image on the touch because the image is smaller than the view controller.
CGFloat _lastScale = 1.0;
- (IBAction)pinch:(UIPinchGestureRecognizer *)sender {
if ([sender state] == UIGestureRecognizerStateBegan) {
_lastScale = 1.0;
}
CGFloat scale = 1.0 - (_lastScale - [sender scale]);
_lastScale = [sender scale];
CGAffineTransform currentTransform = self.imageView.transform;
CGAffineTransform newTransform = CGAffineTransformScale(currentTransform, scale, scale);
[self.imageView setTransform:newTransform];
NSLog(#"center: %#", NSStringFromCGPoint(self.imageView.center));
}
Here's a complete project demonstrating the issue.
https://github.com/adamloving/PinchDemo
no auto layout constraints on the view or its parent (at least at build time)
If your nib / storyboard uses auto layout, then there are certainly auto layout constraints, even if you did not deliberately construct them. And let's face it, auto layout does not play nicely with view transforms. A scale transform should scale from the center, but auto layout is fighting against you, forcing the frame to be reset by its top and its left (because that is where the constraints are).
See my essay on this topic here:
How do I adjust the anchor point of a CALayer, when Auto Layout is being used?
See also the discussion of this problem in my book.
In your case, the simplest solution is probably to apply the transform to the view's layer rather than to the view itself. In other words, instead of saying this sort of thing:
self.v.transform = CGAffineTransformMakeScale(1.3, 1.3);
Say this sort of thing:
self.v.layer.transform = CATransform3DMakeScale(1.3, 1.3, 1);
I was trying to mimic the yahoo weather app screen transition between cities, I couldn't figure out what transition it is. Any clue?
I really appreciate you time.
Thanks
Edit:
I have slideView ctrler which has a subview. The sliderview has an image and the subview has text. When I make a swipe, the text view with text must be moving and dragging way the view ctrler with it at a slower rate and this intern should start dragging in the next view ctrler which is an instance of slider Ctrler.
There is no built-in transition that does this for you (I assume you're talking about the images that are transitioning their frame/center at a different rate than the view itself). You'd probably have to write it yourself. Some basic familiarity with gesture recognizers and view animation is needed.
The basic effect is by simultaneously adjusting the center property for two image views as you change the frame of those views (or their super views). (Or, you can achieve this by having image views whose contentMode is UIViewContentModeCenter and just changing the frame.) I'd suggest you start with some simple tests of the effect and build from there.
For example, I created a scene that has two image views, whose autolayout constraints were defined as follows:
H:|[leftImageView][rightImageView]|
V:|[leftImageView]|
V:|[rightImageView]|
I then defined a width constraint for the leftImageView, and hooked it up to an IBOutlet for that constraint, e.g. leftImageWidthConstraint. I then have a UIPanGestureRecognizer that could handle the gesture, simply changing this leftImageWidthConstraint accordingly (and with auto layout, the rest of the frame is calculated automatically for me from that):
- (void)handlePan:(UIPanGestureRecognizer *)gesture
{
CGPoint translate = [gesture translationInView:gesture.view];
static CGFloat width;
if (gesture.state == UIGestureRecognizerStateBegan)
{
width = self.leftImageWidthConstraint.constant;
}
CGFloat newWidth = width + translate.x;
if (newWidth < 0)
newWidth = 0;
else if (newWidth > self.view.bounds.size.width)
newWidth = self.view.bounds.size.width;
self.leftImageWidthConstraint.constant = newWidth;
// if you let go, animate the views to their final position
if (gesture.state == UIGestureRecognizerStateEnded)
{
// if more than half way, set left view's target width to take up full width,
// otherwise set left view's target width to zero
if (newWidth > (self.view.bounds.size.width / 2.0))
newWidth = self.view.bounds.size.width;
else
newWidth = 0;
// animate the changing of the constraint (and thus the `frame`) accordingly
self.leftImageWidthConstraint.constant = newWidth;
[UIView animateWithDuration:0.5 delay:0.0 options:UIViewAnimationOptionCurveEaseOut animations:^{
[self.view layoutIfNeeded];
} completion:nil];
}
}
Thus, as I pan across, the two images are centered within their clipped frames:
This is a very basic implementation of the idea. There are, though, a ton of implementation details (custom container vs subviews, autolayout vs not, etc.), so until you answer some of those questions, it's going to be hard to be more specific.
It is not default but i achieved similar to it in my old app
Register gesture on your view and on detection set isFromLeftSide accordingly
Call following. do fine tune this as per your requirements
[self.view addSubview:mySlidingView];
mySlidingView.frame = // set offscreen frame, in the direction you want it to appear from depending on flag isFromLeftSide
[UIView animateWithDuration:8.0
animations:^{
mySlidingView.frame = // desired end location
}];
I have one UIView that I rotate manually when orientation changes, this UIView hosts a number of buttons, I want to rotate them too based on the change in orientation.
I am doing this :
CGAffineTransform rotation = CGAffineTransformMakeRotation(M_PI / -4);
[UIView animateWithDuration:0.25 animations:^
{
self.parentView.firstButton.transform = CGAffineTransformConcat(self.parentView.firstButton.transform, rotation);
self.parentView.secondButton.transform = CGAffineTransformConcat(self.parentView.secondButton.transform, rotation);
self.parentView.thirdButton.transform = CGAffineTransformConcat(self.parentView.thirdButton.transform, rotation);
self.parentView.transform = CGAffineTransformConcat(self.parentView.transform, rotation);
}
completion:^(BOOL finished)
{
}];
parentView does rotate properly, but the other buttons do not !!
do I need to perform the rotation in another call? as in I cannot rotate a button and its parentview in the same block?
Thanks
It should be enough to rotate the parentView. Make sure that you assign self.parentView.transform first. Try directly assigning a proper transform with CGAffineTransformMakeRotation() and do not rely on a current state of the transform property (like using CGAffineTransformConcat()). Don't forget to make sure the subviews are really subviews of the parentView - a [self.parentView addSubview:...]should occur somewhere. If you use IB to set up subviews this can be tricky; check the view hierarchy tree of IB!
I have two UIViews (A and B) are added as subviews to a UIViewcontroller Main View (the superview).
I am trying to animate View A (scale/rotate/move center point to that of View B). If I do the scale/rotate the animation is smooth. If I just do the center point the animation is smooth. However, when I try to include changing the center point, rotation and scaling of View A then View A's center point animation is instant and jerky, but the rotation/scaling is smooth.
Here is my animation block:
[UIView animateWithDuration:2.0
animations:^ {
self.customView.frame = CGRectMake(self.containerSubview.frame.origin.x, self.containerSubview.frame.origin.y, 200, 300);
self.containerSubview.alpha = 0.2;
self.customView.transform = CGAffineTransformConcat(CGAffineTransformMakeRotation(M_PI / 2), CGAffineTransformMakeScale(1.5, 1.5));
self.customView.center = self.containerSubview.center;
}
completion:^(BOOL finished) {
NSLog(#"center point: %#", NSStringFromCGPoint(self.customView.center));
}
];
I should have read the documentation more carefully when it comes to transforms. Since transforms are dependent on a view's center point. I can't have simultaneous animations occurring modifying a transform and center, at the uiview level. Since the center is a dependency for a transform it has to be set immediately and then the rest of the animations can occur.
The solution was to animate at a view's layer. Using group animations I was able to animate: the center point, scaling and rotation.
Blog and sample project to come shortly.