Cross-Disolve transition just changes instantly - Swift 3.0 - swift

I'm trying to change the color of a button's background using the cross dissolve animation, however, whenever I run this it instantly change the color instead of having a smooth transition.
UIView.transition(with: self.SignIn,
duration:3,
options: UIViewAnimationOptions.transitionCrossDissolve,
animations: { self.SignIn.backgroundColor? = UIColor(hexaString: "#" + hex) },
completion: nil)

Instead of setting backgroundColor in animations block I have set it before the view transition and then start the transition of view and it will work perfectly.
self.btnAnimate.backgroundColor = UIColor.red
UIView.transition(with: self.btnAnimate,
duration: 3,
options: .transitionCrossDissolve,
animations: nil,
completion: nil)
Output
Note: I have set red color as backgroundColor you can set what ever color you want.

This is happening because your animations are conflicting with the default button animations.
To solve this, you can create a custom child class of UIButton:
import UIKit
class CustomButton: UIButton {
func animateButton(hex: String) {
// This block disables the default animations
UIButton.performWithoutAnimation {
// This is where you define your custom animation
UIView.transition(with: self, duration:3, options: UIViewAnimationOptions.transitionCrossDissolve, animations: {
self.backgroundColor? = UIColor(hexaString: "#" + hex) }, completion: nil)
// You must call layoutIfNeeded() at the end of the block to prevent layout problems
self.layoutIfNeeded()
}
}
}
Then to call this function:
signInButton.animateButton("ffffff") // Pass in your hex string
Of course, be sure to change the class of your loginButton on the story board from UIButton to CustomButton.
The downside to this approach is that the text is still darkened during the animation process. There may be a way to override this (I wasn't able to quickly determine how), so an alternative approach to this solution is to use a UILabel instead of a UIButton and set an on click listener.
Or another approach you might want to consider, is put a customLabel behind a UIButton and perform animations on the label when the button is clicked. Although this approach seems somewhat "hacky", the advantage is that the button is not disabled while the label is being animated, meaning you can register rapid sequential button presses (although it looks like the purpose of your button is to log the user in, in which case this probably wouldn't be necessary for you).

Related

Showing ViewController's View when animating upper ViewController

I have ViewController(1) presented over another ViewController(2), I want to accomplish the effect that when I dismiss viewController1 I perform a reduction of size through CGAffineTransform and I dismiss it.
The effect I want to accomplish though is that when I reduce the size of ViewController1's view I want to see also the view of ViewController2 behind it, while now I'm only seeing a black background.
The code I'm using is really simple:
UIView.animate(withDuration: 2, animations: {
self.view.transform = CGAffineTransform(scaleX: 0.1, y: 0.1)
}) { (_) in
self.dismiss(animated: true, completion: nil)
}
I don't know how to reach that effect
Make sure to set this presentation for the top animated vc
vc.modalPresentationStyle = .overCurrentContext
This will guarantee transparency behind when you change frame / transform the view of that top vc

UIStackView subviews animating only when isHidden is set to false

I am trying to hide/show my subview of a UIStackView within an animation block like so:
UIView.animate(withDuration: 0.3) {
self.unpairSensorButton.isHidden = isHidden
}
Showing animation works perfectly, but when I try to hide it, it just waits for the animation duration and then just immediately disappears. Any idea why?
I have tried to use the layoutIfNeeded() and putting it into DispatchQueue.main.async block bot neither helped.
isHidden property is not animatable by itself.
UIStackView basically changes its internal constraints to adapt its size when you hide some subview. Since constraints are animatable, you would do this:
unpairSensorButton.isHidden = isHidden
UIView.animate(withDuration: 0.3) {
self.view.layoutIfNeeded()
}
Calling layoutIfNeeded() in an animation block will animate the layout process.

How to trigger action when textfield becomes active

I have a CollectionViewController made up of custom cells. At this point my custom cells are only made up of UITextFields. When I click inside one of the textFields to begin typing I want to trigger an animation on the cell. For some reason I can't figure out why my animation isn't being triggered when I click inside the textField. When I conform to the UItextFieldDelegate protocol and try to trigger the action through the DidBeginEditing method it doesn't work. When I try to trigger the animation through UIControlEvents.touchDown it isn't working either.
#objc func animateCell(textField: UITextField) {
print("TextField active")
let cell = collectionView.cellForItem(at: indexPath)
UIView.animate(withDuration: 0.5, delay: 0, options: .allowAnimatedContent, animations: ({
cell?.frame = collectionView.bounds
collectionView.isScrollEnabled = false
}), completion: nil)
}
Instead of textFieldDidBeginEditing, try using textFieldShouldBeginEditing: (and make sure to return true from that method since you want to allow the user to type :-).
Also, since you say the method doesn't fire until you click on a different textfield outside of the first one selected, fire the animation manually for the initially selected text field as the collection view is displayed.

Strange animation when changing height constraint on a CollectionView (autolayout)

I have a collectionView which I need to animate height changes for, so it expands up from the foot of the screen, & back down again. It's hard to explain what's going wrong so I've attached a recording of the simulator. The animation is fine for expanding, but very odd for the collapse - the content partially disappears as it animates, then reappears when it's done. The animation code is triggered during the scrollView 'scrollViewDidScroll(_ scrollView: UIScrollView)' delegate method, with logic to determine whether to collapse or expand. The expand animation, whose code is pretty much identical, works fine.
Animation code -
private func collapseTable() {
guard self.isTableChangingSize == false else { return } // as the delegate method is triggered during the animation, we use a flag to prevent multiple collapse calls
guard self.tableHeightConstraint.constant != self.tableCollapsedHeight else { return } // don't collapse if we're already collapsed
self.isTableChangingSize = true
self.tableHeightConstraint.constant = self.tableCollapsedHeight
UIView.animate(withDuration: 0.5, animations: {
self.view.layoutIfNeeded()
}) { _ in
self.isTableChangingSize = false
}
}
Here are a couple of videos of what's happening -
Normal speed
Slow motion
After far too long messing about trying different approaches, & tweaking everything, it seems that the problem was allowing scrolling during the animation block. If you set -
self.collectionView.isScrollEnabled = false
before the animation block, and -
self.collectionView.isScrollEnabled = true
in the completion, all is good.

Change button background color dynamically like progress bar

I am trying to implement buttons with changeable background colors in Swift. But I do not want to change color of whole button. I want it to change like progress bar. For example, there will be a specific time value, and background color of button should change from right to left with animation in that time.
I saw apps like that. Can anyone give a clue for how to do that?
Don't be constrained by the idea that a button is only a UIButton. A button can be a view with a gesture recogniser, or a view with a transparent button over the top of it. It could be a button with a background image that you keep updating. There are many options for how to construct what you describe. It depends exactly what effect you want as to how it should be done. Buttons showing progress would often be disabled until progress is complete so you could easily use a progress indication view which is replaced by a 'real' button when progress is complete.
Maybe this helps you.
func changeButtonColors(){
//let myBtn
let timer = NSTimer.scheduledTimerWithTimeInterval(2.0, target: self, selector: "changeColor", userInfo: nil, repeats: true)
timer.fire()
}
//cloros to iterate over each time
let colorChoice = [UIColor.blackColor(), UIColor.blueColor(), UIColor.redColor()]
var counter = 0 //tracking variable
//this gets called when by the timer
//currently it is supposed to go on forever.
func changeColor(){
//suppose i have the button outlet named myBtn
UIView.animateWithDuration(2.0, animations: { () -> Void in
myBtn.backgroundColor = colorChoice[counter]
if counter < colorChoice.count{
counter++
}else{
counter = 0
//or disable the alert
//timer.invalidate()
}
})
}
Here myBtn is a outlet to the View Controller. We use Timer to trigger the function call at a specific time in our case every 2 second. This can be derived by other app logic.
The called function then iterates over a array of UIColor and applies one to the background of the button or view. The workflow is this. Change things where you require.