Why does animated view start from wrong position? - swift

I'm using auto layout to animate the elements in a stack view. I've set the leading and trailing constraints, both with a priority of 1000 (required). However, as you can see from the animated gif, the stack view is initially attached on the left but not on the right.
UIView.animate(withDuration: 0.125, delay: animationDelay, options: [.curveEaseOut],
animations: {
someView.transform = CGAffineTransform.identity
someView.alpha = 1
someView.isHidden = false
}, completion: nil)

Related

When I scroll my UIScrollView, animations that are connected to a timer and animate views inside of the scrollview, suddenly stop ticking

I have a UIScrollView containing views, which are connected to timers that call a function every x seconds. Everything works perfectly, until I start scrolling the scroll view, upon which the timers stop ticking, meaning that the animations stop happening. I do not know if this is clear enough, but I will show you some code below to try to clarify.
#objc func lowBeatingAnimation(){
for i in lowWindow{
let List = i as? [Any] ?? []
let View = List[0] as! UIView
let width = List[1] as! NSLayoutConstraint
let height = List[2] as! NSLayoutConstraint
let label = List[3] as! UILabel
self.view.layoutIfNeeded()
UIView.animate(withDuration: 0.5, delay: 0, options: .curveEaseIn, animations: {
View.layer.shadowRadius = 50
width.constant += -20
height.constant += -20
label.alpha = 0.65
View.layer.cornerRadius += -10
self.view.layoutIfNeeded()
}, completion: { finished in
UIView.animate(withDuration: 0.5, delay: 0, options: .curveEaseOut, animations: {
View.layer.shadowRadius = 10
width.constant += 20
View.layer.cornerRadius += 10
label.alpha = 0.85
height.constant += 20
self.view.layoutIfNeeded()
}, completion: { finished in
})
})
}
}
That is the function I call every second. lowWindow is an array, consisting of arrays with the following format: [UIView, NSLayoutConstraint (belonging to the first element in the list), NSLayoutConstraint (also belonging to the first element in the list), UILabel]
The first element in lowWindow is a UIView, which is a subview of the scroll view, which causes the animation to stop whenever it gets scrolled.
I think the issue can be boiled down to the following question, although I am not entirely sure: Why does an external timer stop working whenever the position of the scrollview gets edited?
I have also tried different things in terms of whether the views that get animated are direct subviews of the scrollview, or if they are subviews of a subview of the scrollview. Nothing works so far. If you have any ideas on how to solve this and would like to share it, that would be highly appreciated. Thanks.
I solved this by adding the timers to Runloop.
RunLoop.main.add(lowTimer, forMode: .common)
RunLoop.main.add(mediumTimer, forMode: .common)
RunLoop.main.add(highTimer, forMode: .common)

UIDatePicker's Change Year Arrow's Animation Transition

What is the animation transition used in the right-arrow image to the down-arrow image when changing the year in the UIDatePicker control? The animation smoothly rotates the start image to the end image like so:
I am trying to mimic this transition for an image in my code. In viewDidLoad(), I have:
myButton.setImage(UIImage(named: "rightArrow")?.withRenderingMode(.alwaysTemplate), for: .normal)
and then within the function invoked when that button is tapped, I do:
UIView.transition(with: myButton, duration: 0.6, options: .transitionCrossDissolve, animations: {
self.myButton.setImage(UIImage(named: "downArrow")?.withRenderingMode(.alwaysTemplate), for: .normal)
}, completion: nil)
I am using .transitionCrossDissolve here, but obviously it isn't the right transition. I tried each of the seven available transition options, but none of them behave like in UIDatePicker. Is there an easy way to create this "rotating" transition?
I finally figured it out. This will animate the rotation of the arrow:
UIView.transition(with: myButton, duration: 0.2, options: [], animations: {
self.myButton.transform = CGAffineTransform(rotationAngle: .pi/2)
}, completion: nil)
EDIT -- shorter solution:
UIView.animate(withDuration: 0.2) {
self.myButton.transform = CGAffineTransform(rotationAngle: .pi/2)
}

Iphone X translation animation issue swift/xcode

Only occurs in iphone X
I'm making a view appear and disappear from bottom of screen using CGAffinetransform(translationY), everything works fine in every devices but in iphone x. Two labels in a stack view as a subview of a uiview when animating labels start from 20px higher then animation starts.
UIView.animate(withDuration: 0.5, delay: 0, options: .curveEaseInOut, animations: {
self.stackOrderDetailsContainer.transform = CGAffineTransform(translationX: 0, y: self.stackOrderDetailsContainer.frame.size.height)
self.blackTransparentView.transform = .identity
self.blackTransparentView.alpha = 0
self.viewArrowHolder.transform = CGAffineTransform(rotationAngle: CGFloat(-2*Double.pi))
}, completion: nil)
edit: Added code snippet

Changing UIButton alpha then doesn't recognize tap

I set up an interface very much like youtube in the sense that if a user presses on a UIView, I'll hide or show all of the subviews with an animation.
func setupControlViewVisiblity(playingVideo: Bool = false, duration: TimeInterval = 1.0, isExpanded: Bool = false){
var alpha:CGFloat = 1
if isShowingControls {
//If we're showing then we're about to hide everything so alpha is 0
alpha = 0
}
UIView.animate(withDuration: duration, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 1, options: .curveEaseOut, animations: {
self.pausePlayButton.alpha = alpha
self.backButton.alpha = alpha
self.infoLabel.alpha = alpha
self.fullScreenButton.alpha = alpha
self.currentTimeLabel.alpha = alpha
self.videoLengthLabel.alpha = alpha
self.videoSlider.alpha = alpha
}, completion:nil)
isShowingControls = !isShowingControls
}
The issue that I'm having is that if I call this method and I show the controls (isShowingControls is true), my UIButtons will not recognize the first tap. This literally only happens when the views alpha go from 0 to 1 then I try to tap a button.
I tried doing the animation on the main queue... doesn't work. If I change the alpha from 0 to 1.... wait a few seconds... then tap the button.. it works. How can I make it work right after the UIButtons alpha changed?
Thanks
EDIT: I guess it has to do with the animation duration because when I switch it down to 0.2 it works. How can I keep it a bit higher and get this to work?
By default UIVIew animations do not allow user interaction while the animation is animating.
Luckily there is an animation option allowUserInteraction. Not much more information but you can see it in documentation here. Also worth checking out all the options here.
Try this:
UIView.animate(withDuration: duration, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 1, options: [.curveEaseOut, .allowUserInteraction], animations: {
// Update the views
}, completion:nil)

How to not animate specific constraint changes with layoutIfNeeded() in Swift?

I have a UIView which contains subviews. The subviews are animated with constraints changes and layoutIfNeeded() function :
for i in 1...cardViews.count - 1 {
let currentCard = cardViews[i]
let previousCard = cardViews[i-1]
// Set new offset
currentCard.topConstraint?.constant = previousCard.contentView.frame.height + configuration.expandedOffset
}
// To animate constraints's changes
UIView.animate(withDuration: TimeInterval(duration), delay: 0, options: [.curveEaseOut], animations: {
self.layoutIfNeeded()
}, completion: nil)
But when I do this, it also animate the contraints changes of the parent. Something like this :
self.Height == containerView.Height
How can I call layoutIfNeeded() to animate my subviews but not the parent ?
EDIT : The side effect :
http://gph.is/2noI3w9
You can call this inside of your UIView.animate
for i in 1...cardViews.count - 1 {
let currentCard = cardViews[i]
currentCard.layoutIfNeeded()
}
Instead of
self.layoutIfNeeded()