I created custom transition, NSZoomTransitionAnimator.
I have a problem that the transition is not working when goingBack in only second time.
For example, I touched a cell (indexPath.row = 0) in collection view. Working correctly in first time , but not working in second time.
I debug code and found sourceImageView.frame is (0.0, 0.0, 360.0, 480.0) in second time.
(0.0, 0.0, 360.0, 480.0) is correct.
Here is my code,NSZoomTransitionAnimator.swift.
UIView.animateWithDuration(kBackwardAnimationDuration, delay: 0, options: .CurveEaseOut, animations: {
sourceImageView.frame = destinationImageViewFrame
println(sourceImageView.frame)//(0.0, 64.0, 187.5, 187.5)
}, completion: {(finished: Bool) in
if finished {
destinationImageView.removeFromSuperview()
println(sourceImageView.frame)//(0.0, 0.0, 360.0, 480.0)
sourceImageView.removeFromSuperview()
}
transitionContext.completeTransition(finished)
})
Here is my repository.
https://github.com/naoyashiga/NSZoomTransitionAnimator
Touch a image in collection view and touch a close button in DetailViewController. So transition will start.
please tell me why transition is not working in only second time.
You can't re-use the sourceViewController.transitionSourceImageView() which has two constraints:
(lldb) po sourceImageView.constraints()
[
<NSContentSizeLayoutConstraint:0x7feaeb7097a0 H:[UIImageView:0x7feaeb577230(360)] Hug:251 CompressionResistance:750>,
<NSContentSizeLayoutConstraint:0x7feaeb704680 V:[UIImageView:0x7feaeb577230(480)] Hug:251 CompressionResistance:750>
]
So its size will be incorrect. Instead, you can change
sourceImageView = sourceViewController.transitionSourceImageView()
to
sourceImageView.image = sourceViewController.transitionSourceImageView().image
sourceImageView.frame = sourceViewController.transitionSourceImageView().frame
This will solve your problem.
Related
I have a custom view that acts as a progress view, which is basically a simple set of views where I animate the leading constraints of the container that holds the value and of the view that acts as a 'filled' view, so it looks like this:
This is a reusable view and is used in several places in the app. Some of the users suffer a crash that is related to the animation in the update method of this custom view, and I can't reproduce it nor I can find the issue. The method looks like this:
extension GradientProgressView {
/// Animates the progress view to the desired % value and changes the value label
/// - Parameter percentage: the desired % value in range [0.0 - 1.0]
func updateProgress(percentage: Double) {
UIView.animate(withDuration: 0.4, delay: 0, options: .transitionCrossDissolve) {
self.labelProgress.text = "\(Int(percentage * 100))%"
}
// limit to 100% for correct constraint calculation
let percentageAdapted = CGFloat(min(percentage, 1.0))
// the available width for the value container to move left/right
let availableValueContainerWidth = backgroundView.frame.width - labelGradientView.frame.width
labelContainerLeadingConstraint.constant = min(availableValueContainerWidth * percentageAdapted, backgroundView.frame.width - 50)
foregroundLeadingConstraint.constant = labelContainerLeadingConstraint.constant
UIView.animate(withDuration: 0.6, delay: 0, options: .curveEaseOut) {
self.layoutIfNeeded()
}
}
}
More precisely, the crash happens in the animation block of the first UIView.animate call, which corresponds to the line 93 in the stacktrace (see below):
UIView.animate(withDuration: 0.4, delay: 0, options: .transitionCrossDissolve) {
self.labelProgress.text = "\(Int(percentage * 100))%"
}
Here's the stacktrace of the crash:
I've tried using the self as a weak reference in both animation blocks, but the crash reappeared. Could someone point out to me what am I doing wrong here?
It's very tough to debug a crash that can't be reproduced, but a couple comments (that may or may not have anything to do with the crash)...
First, this block:
UIView.animate(withDuration: 0.4, delay: 0, options: .transitionCrossDissolve) {
self.labelProgress.text = "\(Int(percentage * 100))%"
}
doesn't really do anything, other than change the text.
If we want to cross-dissolve the text change in a label, we need to use UIView.transition(...):
UIView.transition(with: self.pctLabel, duration: 0.4, options: .transitionCrossDissolve, animations: {
self.pctLabel.text = "\(Int(percentage * 100))%"
})
Second, you might be running into a problem with your availableValueContainerWidth calculation, as you are allowing the label width to change based on its text. If the current value is only 1-digit, and we change it to 100%, we might get something like this:
Giving the label a fixed width (calculate the width needed for the max value text of "100%"), it can look like this:
Of course, we could use a variable-width label and "get around" the issue by using the center instead of leading ... but the cross-dissolve and width-change animation may also look a little quirky.
Again, really tough to debug a non-reproducible crash, but those two things may be something to look at.
I'm trying to achieve some kind of "parallax" effect.
Let's say i have an image at the top of the screen, with height: 180 and a scrollview under it.
The initial state is:
state initial
When i scroll up more than 32 px, the image should "dock" up, like making his height 45px, and doing this animated.
The final state should be:
state final
In scrollViewDidEndDragging, i want to dock the image up, animated. I used the following code:
self.imageConstraint?.constant = 45.0
UIView.animate(withDuration: 1.0,
delay: 0,
options: [.beginFromCurrentState,
.allowAnimatedContent,
.allowUserInteraction],
animations: {
self.superview?.layoutIfNeeded()
}, completion: { _ in
completion()
})
The problem is that the image is set to 45 height, but only the image, and a blank space remains ( the initial height-final height ) and that space is animated.
Basically, with no deceleration, when scrollDidEndDragging i want to animate the height of the image and this does not work as expected.
Looks like this:
during animation
What i am doing wrong ?
Write your code in this way it will work hopefully,
UIView.animate(withDuration: 0.5)
{
self. self.imageConstraint.constant = 45.0
self.view.layoutIfNeeded();
}
Do let me if it doesn't. Thanks
I am working on an apple watch application. In the application, I have a view where the user may swipe left and right between 3 given results. I am using WKInterfaceLabel to show result information. On each swipe, labels are updated with new text.
View screenshot:
I want to animate the change of text on swipe. How can I do this?
Any help provided will be appreciated.
Thanks!
This is not very elegant, but it should work:
You can fade out the contents of a WKInterfaceLabel, and fade in another label in its place. So, place 2 WKInterfaceLabel objects at the same place. One of them is visible (alpha = 1.0) and the other invisible (alpha = 0.0).
When you swipe, determine the new value that should be shown, and set it to the invisible label.
Then, animate the transition using the function animate(withDuration:animations:) of the WKInterfaceController. In the animation block, change the alpha values as required, something like
animateWithDuration(1.0) {
self.visibleLabel.setAlpha(0.0)
self.invisibleLabel.setAlpha(1.0)
}
Hope this helps!
try:-
func labelimage(img: UIImageView) {
print(labelrate.hidden)
if (labelrate.hidden) {
UIView.animateWithDuration(0.5, delay: 0, options: UIViewAnimationOptions.CurveEaseInOut, animations: {
self.labelrate.alpha = 1
}, completion: nil)
}
else {
UIView.animateWithDuration(0.5, delay: 0, options: UIViewAnimationOptions.CurveEaseInOut, animations: {
self.labelrate.alpha = 0
}, completion: nil)
}
self.labelrate.hidden = !self.labelrate.hidden
}
Try to animate the width of a UIView like this. But it does not do anything. Why? The commented part changes alpha of the view, and that works.
UIView.animate(withDuration: 1, delay: 0, options: [.curveEaseInOut], animations: {
self.constraintLookupWidth.constant = 300
self.viewLookup.setNeedsLayout()
//self.viewLookup.alpha = 0.2
}, completion: {complete in
self.constraintLookupWidth.constant = 200
self.viewLookup.setNeedsLayout()
//self.viewLookup.alpha = 1.0
})
I guess that you need to integrate the call with a layoutIfNeeded(), this ask auto layout to force the layout right now.
To make proper animation of a uiview with constraints u must set the new constraint value and then call theView.layoutIfNeeded() , for swift 3 this doesnt work and instead of calling from the view's whos constraint is changed . it must be called from the upper view like this : self.view.layoutIfNeeded().
ex :
UIView.animate(withDuration: 0.1,
delay: 0.1,
options: UIViewAnimationOptions.curveEaseIn,
animations: { () -> Void in
constraintHeight.constant = 10.00
// instead of myView.layoutIfNeeded() // Swift 2
self.view.layoutIfNeeded() // Swift 3
}, completion: { (finished) -> Void in
// ....
})
The problem is, in my case i did the change and the way im using this animation is for a bottomview (a bottom bar/ banner view) that hides when scrolling a tableview down and comes up when going all the way to the top in the tableview. now that i have changed the proper code for swift 3 using self.view.layoutIfNeeded() , the tableview acts wierd, slows down, rows start appearing as fading in or is just way slow to present, when scrolling down and up the tableview's sections comes jumping or moving in slow motion, also have seem memory gone up from 80mb to 100mb . if i eliminate the line in the code, i dont get the animation, the view just appears and dissapears with the scrolling of the tableview, but... i dont get the strange behavior. i have also checked the views hierarchy to check somehow is not creating wierd 100 views replicating or something.. any hints on how can i fix this . all of this was just working fine in swift 2 using theView.layoutIfneeded() but now that the call is being madein the upper view.. omg..wierd acting
Question comes from Swift 3 UIView animation solution.
Try this to force layout changes to superview
//Force to complete all changes.
self.view.superview.layoutIfNeeded()
//Update constant
constraintHeight.constant = 10.00
UIView.animate(withDuration: 0.1,
delay: 0.1,
options: UIViewAnimationOptions.curveEaseIn,
animations: { () -> Void in
self.view.superview.layoutIfNeeded()
}, completion: { (finished) -> Void in
// ....
})
Solution to my needs . thanks all for sharing your answers!.
my view hierarchy
- root view
- uitableView
- bottomBarView
- bottomBannerView
Beacause of that hierarchy i couldnt use self.view.layoutIfNeeded() or self.bottomBarView.superview?.layoutIfneeded(), as it was calling to layout the same superview which also host the tableview and for which im triggering this function if scrolling is more than 10 and also if it less.. so its always trigerring the layoufIfneededmethod. i had to do what i thought from the beginning.
The correct way is to make a container view hosting the bottombarview and the banner view, have it constraint to bottom of the root super view and constraint the bottomBarView and bottomBannerView to IT.
View hierarchy now is .
-root view
-uitableView
-containerBottomView
-bottomBarView
-bottomBannerView
This way i can call self.bottomBarView.superview?.layoutIfNeeded() , and it wont be triggering on the root view which also host the uitableview. it correctly triggers to layout the containerBottomView.
UIView.animate(withDuration: 0.5, delay: 0.3, options: [.repeat, .curveEaseOut, .autoreverse], animations: {
// perform your animation here .
self.username.center.x += self.view.bounds.width
self.view.layoutIfNeeded()
}, completion: nil)