Animating position of UIButtons causes them to stop receiving touch actions - ios

In my app I have several buttons that, after a separate button is pressed move to new positions in a UIView animation. The animation itself works perfectly, but the problem is, after the animation, the four buttons stop responding to touch actions. Here's my current code:
UIView.animate(withDuration: 1.0, delay: 0.0, options: [.allowUserInteraction], animations: {
self.hexagon.transform = CGAffineTransform(rotationAngle: CGFloat.pi / 2)
self.tileB.center.x += self.tileB.bounds.width
self.tileB.center.y += self.tileB.bounds.height
self.tileF.center.x -= self.tileF.bounds.width
self.tileF.center.y += self.tileF.bounds.height
self.tileH.center.x -= self.tileH.bounds.width
self.tileH.center.y -= self.tileH.bounds.height
self.tileD.center.x += self.tileD.bounds.width
self.tileD.center.y -= self.tileD.bounds.height
}, completion: nil)
I have tried many other methods, including setting frames and bounds, but none of them have worked correctly.

The animation itself works perfectly, but the problem is, after the animation, the four buttons stop responding to touch actions
The reason is probably that after the animation, the buttons have moved out of their superview's bounds. A view outside of its superview is untouchable, even if it is visible.
One way to confirm this is to set the superview's clipsToBounds to true and do the animation. The buttons will vanish by the end of the animation, proving that they have moved outside their superview.

Related

swift: Move animation

I have viewController in storyboard. And 4 squares. I want to place my squares on view. At first I want to show two squares. If I press on button I want to my red 2 squares move left and I show next 2 blue squares. Like this animation.
Do I need to create a scrollView or collectionView or something else to move the squares? What the best way to create this?
Use UiVew.animate and CGAffineTransform
Setup
Create a UIView that will contain your squares. Make sure that UIView has clip to bounds enabled. Then, add your four squares in, with the blue ones nested inside the UIView, but with their coordinates outside of the container so that they're getting clipped.
Then, when you want to move them, you simply translate all of your squares x to the left. By putting this movement inside of a UIView.animate block, iOS will perform the animation for you.
UIView.animate(withDuration: 2.0) {
self.redTopView.transform = CGAffineTransform(translationX: -160, y: 0)
self.redBottomView.transform = CGAffineTransform(translationX: -160, y: 0)
self.blueTopView.transform = CGAffineTransform(translationX: -160, y: 0)
self.blueBottomView.transform = CGAffineTransform(translationX: -160, y: 0)
}
Final result: http://g.recordit.co/SToMSZ77wu.gif

CATransform3D Animation for Rotation+Scaling not Smooth

I'm trying to add this 3D animation but it's not animating it properly. There is a jerk during the animation.
var currentTransform = CATransform3DIdentity
UIView.animate(withDuration: 0.3, animations: {
currentTransform = CATransform3DMakeScale(0.5, 0.5, 1)
currentTransform.m34 = 1.0 / -800
currentTransform = CATransform3DRotate(currentTransform, CGFloat(M_PI) * 50.0/180.0, 0, 1, 0)
self.containerView.layer.transform = currentTransform
})
What can be the problem and how to solve it?
UPDATE (Answer):
This piece of code works fine. The only problem was, the containerView was an immediate subView of my main view and this was causing the jerk.
The solution to this is, I added another view to my main view and made the containerView a subview of the newly added view.
Though I'm not sure why this happened. I think, the transformation I was doing on my containerView, also affected the layer of the main view through transformation.

Choppy animation in iOS

I have an animation in iOS with the following code:
view.layoutIfNeeded()
UIView.animateWithDuration(0.3, delay: 0, options: .CurveEaseInOut, animations: {
// constraints which are unrelated to drawingResultsController
self.leadingSpaceToContainerViewConstraint.constant = 0
self.trailingSpaceToContainerViewConstraint.constant = 0
// This controller doesn't have any constraints
drawingResultsController.view.frame.origin = CGPoint(x: self.view.bounds.width, y: 0)
self.view.layoutIfNeeded()
}, completion: { finished in
drawingResultsController.view.removeFromSuperview()
drawingResultsController.removeFromParentViewController()
drawingResultsController.didMoveToParentViewController(nil)
})
however, the animation does not look very fluid. It's not terrible by any means but you can definitely tell it is way under 30 fps. Does anybody have any idea as to why could this be? I have already verified everything is on the main thread.
Extra details:
The parent controller's view has a subview which is pinned to the edges of it's superview (the main view), those are the constraints seen above. This subview contains the main views of the controller (i.e. it's there to create a parallax effect as the drawingResultsController slides in and out of the screen). drawingResultsController is a child view controller added as a subview of the main view, and it doesn't have any constraints. It slides in and out of the screen, and this is the code to slide it out.
This way, the view moved by the constraints is a sibling of the view of the drawingResultsController. Both are direct subviews of the main view.
EDIT:
Reducing the animation to just this
UIView.animateWithDuration(0.3, delay: 0, options: .CurveEaseInOut, animations: {
drawingResultsController.view.frame.origin = CGPoint(x: self.view.bounds.width, y: 0)
}, completion: nil)
improves the frame-rate a little, but it continues to be an issue. It is more noticeable when setting longer animation times. The usage of CPU and memory look completely normal at the time of the animation.
Thank you
I had a similar issue, turns out it is solved by rebooting the device.
However the issue also seems to be solved by having a CADisplayLink updating a random layer that's part of your window's hierarchy. I noticed that, because we had an animation running somewhere in our app that was triggered by a CADisplayLink and that solved all slow animations elsewhere in the app!
The hack below solves the issue. Add the code to your main window class (assuming you use a custom UIWindow sub class for your main window):
//Hack for slow animation problem that occurs after long uptime of the device, updating a view's layer position that's in the view hierarchy using a display link solves the slow animations.
#property (nonatomic, strong) UIView *dummyView;
#property (nonatomic, strong) CADisplayLink *displayLink;
- (void)enableSlowAnimationHack {
if (self.dummyView == nil) {
self.dummyView = [[UIView alloc] initWithFrame:CGRectZero];
[self addSubview:self.dummyView];
}
if (self.displayLink == nil) {
self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:#selector(animateWithLink:)];
[self.displayLink addToRunLoop: [NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
}
}
- (void)animateWithLink:(CADisplayLink *)sender {
self.dummyView.layer.position = CGPointMake((arc4random() % 100) / 100.0, (arc4random() % 100) / 100.0);
}
Apparently it solved on its own. It doesn't make much sense since it happened in both the simulator and the device, and no code was changed.

Crossfade multiple UIViews without background peeking through alpha?

I need to crossfade from one set of views to another set of views. There is a background sometimes behind these views. If I do a naive crossfade with animateWithDuration: v1.alpha = 1.0, v2.alpha = 0.0, then during the animation you will briefly see the background, because two views with alpha 0.5 stacked on top of each other don't have a final alpha of 1.0, they have a final alpha of I believe 0.75, hence you can see the background.
There is a UIView method transitionFromView:toView:... that allows you to achieve an ideal cross fade if you specify the animation option CrossDissolve, however, this method fails if you run two of them at once, even on fully separate views, which is a requirement. That is, if I start transitionFromView from A to B, and then transitionFromView from C to D right after, the first one will crap out.
Is there another way to achieve a proper crossfade that won't break? Does CoreAnimation have any libraries for proper crossfading? Or is there some trickery I can use to achieve it with regular UIView animations?
I assume v1 is above v2 (after it in the subviews array if they have the same parent), and you want v1 to appear and v2 to disappear.
There's no need to animate the alpha v2 at all. Just animate the alpha of v1 from 0 to 1. When the animation ends, you can set v2's alpha to 0 if you want, and remove it from the view hierarchy.
[UIView animateWithDuration:0.2 animations:^{
v1.alpha = 1;
} completion:^{
v2.alpha = 0;
[v2 removeFromSuperview];
}];
This is how you cross dissolve between two views:
UIView.transition(from: fromView, to: toView, duration: 0.3, options: .transitionCrossDissolve) { (_ finished) in
// Completion block here
}

UIPangesture <--> UIView animation glitch

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.

Resources