I'm struggling to figure out how to allow user interaction with a view as it's being animated.
Here's the situation: I have a UIView cardView which holds card subviews. The cards are draggable tiles, similar to how the cards in Tinder are draggable/swipeable.
I am trying to fade out the card using animateWithDuration by animating to cardView.alpha = 0. Logically, this will also fade out all of the subviews (card objects). In this specific case, I am only targeting one card subview. However, during the animation, I am unable to drag/interact with the card.
Here is the code I'm using:
UIView.animateWithDuration(
duration,
delay: 0,
options: UIViewAnimationOptions.AllowUserInteraction,
animations: {self.cardView.alpha = 0}
) {
_ in
println("Card faded out")
card.removeFromSuperview()
}
Why doesn't this work? Any help will be appreciated. Thank you!!
I think you can find the answer in this previous post.
The interesting bit of the post is:
UIView's block animation by default blocks user interaction, and to get around it you need to pass UIViewAnimationOptionAllowUserInteraction as one of the options.
I fixed this problem by setting alpha to 0.1 instead of 0.0. I'm not sure if that will work in your case, but it shows that the event handling code thought that the view was not visible and disabled interaction even with the UIViewAnimationOptionAllowUserInteraction flag set. Oddly, setting the alpha to 0.01 did not work, so there is a threshold of visibility you have to stay above.
Swift 5
UIView.animateKeyframes(withDuration: 0.5, delay: 0, options: [.repeat, .autoreverse, .allowUserInteraction], animations: {
self.customButton.backgroundColor = .none
}, completion: nil)
The issue is with the Alpha value of 0. Alpha values of a certain proximity to Zero will remove the view from the view responder hierarchy. The fix here is to make the alpha setting to this:
self.cardView.alpha = 0.011
The view will still be invisible but not removed from the responder chain. From my testing the minimum amount is the following:
extension CGFloat {
static let minAlphaForTouchInput: CGFloat = 0.010000001
}
Related
Normally, we can do an interactive transitioning with animateTransition of UIViewControllerAnimatedTransitioning and updating progress via UIPercentDrivenInteractiveTransition.
Question:
How to have the interactive transitioning at first, then as we pass a certain threshold, perform a different ending animation?
What I want to achieve here is something like dismissing App store's Today card (https://gph.is/2qgcGHd). We can interactively shrink the card by panning a left edge of the screen. Then when it reaches the point, the card animates back to home page without any interactivity. It seems like a combination of interactive + animate transition to me.
What I've tried:
I tried doing this in UIView.animateWithKeyFrames by dividing into two parts of animation with 0.5 relative times for each. Then as the progress reach 0.5, I call finish() (of UIPercentDrivenInteractiveTransition) to have the second animation performing. It has some glitches there and it's like a hack. Want to know if there's a better way to do this.
In the end, I use UIView.animateKeyFramesand dividing the interactive transition into two-part animation (as explained in the question):
let progressUntilDismissing = 0.4
UIView.animateKeyframes(withDuration: 0.5, delay: 0, options: [], animations: {
UIView.addKeyframe(withRelativeStartTime: 0.0,
relativeDuration: progressUntilDismissing,
animations: {
// interactive dismissing animation...
})
UIView.addKeyframe(withRelativeStartTime: progressUntilDismissing,
relativeDuration: (1 - progressUntilDismissing),
animations: {
// closing dismissing animation...
})
}) { (finished) in
//...
}
Then in the pan gesture recognizer, I calculate the pan progress and determine if it passes progressUntilDismissing or not.
If yes, call finish() on UIPercentDrivenInteractiveTransition subclass, it will animate the closing dismissing animation automatically.
In case anyone is curious, this is what I'm playing with:
AppStoreTodayInteractiveTransition
In the AppStore (iOS 11) on the left "today"-tab, there are several card views. If you highlight one, it shrinks a little bit. How can I rebuild this animation?
I guess changing the constraints of the card view during an animation will not be what we need, since you would also have to adapt all the other constraints (e.g. of the labels) to match the new size.
Is there an easier way to shrink a view with all its subviews?
Also, when you click the card, it increases to fullscreen with an animation. Do you have any ideas how to achieve this effect?
For tapping and shrinking card, I also wrote about this in detail. Here's the idea:
Use a scaling transform to animate shrinking (like in accepted answer)
Disable delaysContentTouch to make it shrink faster upon touch
(scrollView.delaysContentTouch = false)
Always allow users to scroll using .allowUserInteraction animation option:
UIView.animate(withDuration: 1.0,
delay: 0.0,
options: [.allowUserInteraction],
animations: ...,
completion: ...)
(By default when you use transform, it disables the interaction a bit. User can't scroll successively without doing that)
About the expanding to full screen with animation, I have tried to replicate it with the native's transition APIs which you can check out here: https://github.com/aunnnn/AppStoreiOS11InteractiveTransition
In short, I use UIViewControllerAnimatedTransitioning to do custom animation. Hide the original card and create a new dummy card view just for animation. Then setup AutoLayout constraints of that card, including 4 to each of the screen edges. Then animate those constraints to make it fill the screen.
After everything is done, hide that dummy view and show the destination detail page.
Note: The exact implementation detail is a bit different and involved.
You can get an easy scale animation using transform:
UIView.animate(withDuration: 0.2) {
view.transform = CGAffineTransform.identity.scaledBy(x: 0.9, y: 0.9)
}
As to the fullscreen animation, you want to check out some tutorials on how to create custom transition animations.
If you are interested in a more complete functionality you can use this library:
https://github.com/PaoloCuscela/Cards
this is also a good rebuild of that animation when you press a card:
UIView.animate(withDuration: 0.5, delay: 0.0, usingSpringWithDamping: 0.8, initialSpringVelocity: 0.8, options: .beginFromCurrentState, animations: {
self.transform = .init(scaleX: 0.95, y: 0.95)
}, completion: nil)
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
}
I was wondering how can i show a 'No Internet Connection' Just how like Instagram does it,
As an Example :
That see-through custom message animating to show under the navigationController . Would really love to get this to my project ,
thank you for you help
So here's a pic of the storyboard like this :-
"No internet connection" is a label, and the red view underneath is just to test the see through property of the label. If you are designing the UI in code, you can probably make a label similar to mine and place it to the top of the Navigation bar by using it's frame property.
The button here I'm using is just to show the label pop up on the scene (since it's just a demo answer). In your case, if the internet is not available, you will proceed to show the pop up.
So if you are making the UI in code, make sure to make the label in the viewDidLoad method. I have made an IBOutlet and the viewDidLoad now looks like this:-
override func viewDidLoad() {
super.viewDidLoad()
let transform = CGAffineTransform(translationX: 0, y: -label.frame.height)
label.alpha = 0
label.transform = transform
}
On the view loading, I'm moving the label behind the navigation bar, using CGAffineTransform. The distance, how much to move up is the label's height, since we don't want any part to be clipped on the scene.
Next step, is just a fix. I'm making alpha = 0, because navBar is translucent is nature and hence will change it's colour, since our label is behind it. So setting alpha to 0, takes care of it, and in third step apply the transform.
Now, if the internet connection is not available, we should pop out the label under the navBar. The code will look something like this:-
fun checkInternet() {
// called by some of your observer, which checks for changes in internet connection
if !isInternetAvailable {
UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 0.3, initialSpringVelocity: 0, options: .curveLinear, animations: {
self.label.alpha = 0.5
self.label.transform = .identity
}, completion: nil)
}
}
So here, I'll show the pop up with an animation using UIView.animate with some spring damping, so it has a nice bouncy effect to it. I'm setting the alpha to 0.5, since you mentioned you want a see through label, and I'm setting the label to a transform which will bring it back to it's original position when it was created, that's why I'm using .identity.
You can play around usingSpringWithDamping values and change options to have different effects.
I'm currently working on a iOS (swift 3) app. I have a simple vertical stack view containing 2 horizontal stack views. In some cases I want to hide the bottom one. I do so by using the following code
UIView.animate(withDuration: 3) {
self.bottomStackView.isHidden = true;
};
The animation shown below doesn't really do what I would expect:
While the border of the buttons is animated properly when hiding, the text inside each button doesn't seem to be affected until the very end. Any idea as to how I could fix this?
I kept doing some research on the subject, and it seems like most articles were suggesting that using stacks to perform animation would work fine. However I have also found that animations would only work with animatable properties, isHidden not being one of them.
In the end after some trial and errors I have found that isHidden can be animated with stack views, but you can expect children to misbehave. So far the only workaround I have found is like so:
let duration = 0.5;
let delay = 0;
UIView.animate(withDuration: duration, delay: delay, animations: {
self.bottomStack.isHidden = self.hideBottomStack;
})
UIView.animate(withDuration: duration/2, delay: delay, animations: {
self.bottomStack.alpha = 0;
})
You'll note here that I basically "turn" the alpha property down to 0 in half the time I take to hide the stack. This has the effect to hide the text before it overlaps with the upper stack. Also note that I could also have decided to do something like this:
UIView.animate(withDuration: duration, delay: delay, animations: {
self.bottomStack.alpha = 0;
}, completion: { (_) in
self.bottomStack.isHidden = true;
})
This would also hide the bottom stack, but you lose the hiding motion in favor of a fading motion and hide the stack once the fading is done.
I'm not sure about this, I think stackviews can cause weird behaviour sometimes. Have you tried adding "self.view.layoutIfNeeded()" inside the UIView.animate block? Like this:
UIView.animate(withDuration: 3) {
self.bottomStackView.isHidden = true
self.view.layoutIfNeeded()
}
I think it should also work if you put the "self.bottomStackView.isHidden = true" above the UIView.animate, not sure though, not an expert at it.
Also, I don't think you need to use ";" after your line of code in swift :)