How to stop UISegmentedControl segments animation - ios

Is there any way to stop the animation of the method insertSegment(withTitle:at:animated)? Even if I set animated:false the segment animates, look this:
Please ignore the red ball.
The code that I am using to create the component is:
let segmentedControl = UISegmentedControl()
segmentedControl.sendActions(for: UIControlEvents.valueChanged)
segmentedControl.tintColor = UIColor.white
segmentedControl.selectedSegmentIndex = 0
view.addSubview(segmentedControl)
The constraint setup is ok, the frame width (100% - 16 pixels both sides) is the same all the time. The problem is related of the segments, not the parent view.
To add segments (this happens after a settings check):
segmentedControl.insertSegment(withTitle: "Bolinha 1", at: 0, animated: false)
Setting the frame doesn't work, since the problem is related to the inner views.
I tried to remove it using setAnimationsEnabled(false) but doesn't work.
Edit 1: Using segmentedControl.setWidth(segmentedControl.frame.width/3, forSegmentAt: 0) doesn't work.
Edit 2: Starting the view with UISegmentedControl(items: ["", "", ""]) does it replacing the whole component, this is a very heavy solution for this problem btw...
The project uses Swift 3.1.
Thank you.

I discovered that a animation has modifying the behaviour of the inner components.
Removing this animation fixed the problem:
view.setNeedsUpdateConstraints()
view.updateConstraintsIfNeeded()
UIView.animate(
withDuration: 0.5,
delay: 0.5,
usingSpringWithDamping: 1.0,
initialSpringVelocity: 2,
animations: { [weak self] in
self?.view.layoutIfNeeded()
}, completion: nil)
The correct solution for this is to avoid any animation during the build of constraints.

Related

Glitch in UITableView when UIBarButtonItem is animated

I use the second right bar button item as an indicator when there is a successful CloudKit sync. However, if the tableView is held in scroll (with items now under the navigation bar) when the indicator appears, the tableView bounces in sync with the animation. This does not happen if the user is not interacting with the tableView.
Here is a GIF demonstrating the effect.
The other UIBarButtonItems are set up in the storyboard. The one for my iCloud sync indicator is set up in code in viewDidLoad():
var cloudIndicator = UIImageView()
cloudIndicator.frame = CGRect(x: 0, y: 0, width: 25, height: 25)
cloudIndicator.contentMode = .center
cloudIndicator.transform = CGAffineTransform.identity
// Get existing right bar button item which was set up in storyboard
var rightButtonItems = self.navigationItem.rightBarButtonItems ?? []
let customButtonItem = UIBarButtonItem(customView: cloudIndicator)
rightButtonItems.append(customButtonItem)
self.navigationItem.rightBarButtonItems = rightButtonItems
This is the method that animates the cloud sync indicator:
func cloudLabelImageAlert(_ image: UIImage, tintColor: UIColor = .darkGray) {
DispatchQueue.main.async {
self.cloudIndicator.alpha = 0
self.cloudIndicator.transform = CGAffineTransform(scaleX: 0.1, y: 0.1)
self.cloudIndicator.tintColor = tintColor
self.cloudIndicator.image = image
// Animate icon appearing
UIView.animate(withDuration: 0.4, delay: 0, usingSpringWithDamping: 0.5, initialSpringVelocity: 10, options: [], animations: {
self.cloudIndicator.alpha = 1
self.cloudIndicator.transform = CGAffineTransform.identity
}, completion: { didFinish in
// Animate icon disappearing
UIView.animate(withDuration: 0.4, delay: 2.0, usingSpringWithDamping: 1.0, initialSpringVelocity: 0, options: [], animations: {
self.cloudIndicator.alpha = 0
self.cloudIndicator.transform = CGAffineTransform(scaleX: 0.1, y: 0.1)
}, completion: nil)
})
}
)
Presumably this problem relates to the frame of the image view changing during the animation, but it seems strange that it only happens while the tableView is being interacted with.
Is there a way to prevent this happening, or a better way to animate an image view as a bar button item?
Edit
Thanks to advice in the comments, it turns out this is due to reloading the tableview and nothing to do with the animation.
I found the problematic code, which is called after a CloudKit sync:
if let index = self.tableView.indexPathForSelectedRow {
self.tableView.deselectRow(at: index, animated: true)
DispatchQueue.main.asyncAfter(deadline: .now()+1) {
self.tableView.reloadData() // This is delayed as it was causing problems with autoselection (e.g. after coming from Spotlight or notification)
let image = UIImage(named: "cloudTick")!
self.cloudLabelImageAlert(image, tintColor: self.colors[0])
}
} else {
self.tableView.reloadData()
let image = UIImage(named: "cloudTick")!
self.cloudLabelImageAlert(image, tintColor: self.colors[0])
}
Commenting out the self.tableview.reloadData() lines stopped the glitch but the animation continued as expected.
I need to update the data at this point for the user. Is there a better way to do this?
It seems for some mysterious reason your navigation bar is showing "large title" for a moment which leads to contentInset change of the tableView.
So try to manually disable large titles at all in viewDidLoad:
if #available(iOS 11.0, *) {
navigationItem.largeTitleDisplayMode = .never
navigationController?.navigationBar.prefersLargeTitles = false
}
As mentioned on the comment section, you where probably invoking cloudLabelImageAlert(image:, tintColor:) method while loading new table cells.
About updating the data, i would suggest invoking table view reloadData() method after the cloud animation completes.
Hi i had somwhat similar glitch not same but for me it got fixed by putting self.automaticallyScrollInsets to false in viewDidLoad and while animating the imageView for hide and show in the final UIView.animate completion block i called reloadData(). Hope this helps and thanks !

How would I go about coding a UIView fade-in animation for bringSubviewToFront

I am attempting to add a UIView Animation/fade-in effect to my function:
self.view.bringSubview(toFront: self.webView)
I've tried implementing it programmatically, but the UIView doesn't animate; instead, it just shows it immediately without the fade-in effect:
UIView.animate(withDuration: 1, animations: {
self.view.bringSubview(toFront: self.webView)
}, completion: nil)
How would I go about implementing an animation for the bringSubview(_: ) and sendSubview(_ : ) functions? I've tried looking everywhere, but no one seems to have the answer.
You can't use UIView.animate like that. It only works on certain properties. In this case, you need to animate the alpha property.
Before you start animating, set the view's alpha to 0 then bring it to front:
self.webView.alpha = 0
self.view.bringSubview(toFront: self.webView)
// After that you animate the alpha:
UIView.animate(withDuration: 1, animations: {
self.webView.alpha = 1
}, completion: nil)

How to make a Tableview or another view move up while maintaining constraints to to the bottomlayout?

I want to have my tableview increase in size and moves up when scrolled down while also keeping the constraint to the bottom layout. I thought I would try CGAffineTransform.
func MoveUP() {
// pop up login screen
let bottom = CGAffineTransform(translationX: 0, y: -30)
UIView.animate(withDuration: 0.7, delay: 0.2, options: [], animations: {
// Add the transformation in this block
// self.container is your view that you want to animate
self.numberOfProperties.transform = bottom
self.tableview.transform = bottom
}, completion: nil)
}
The problem with this is that it moves up the tableview but does not keep the proper constraints to the bottom. Many apps seems to have this behavior, what tool am I missing in order to achieve this result?

Can all constraints in swift 3 be animated?

I want to animate a view like using the following code:
UIView.animate(withDuration: 2.7, delay: 0.1, options: .allowAnimatedContent, animations: {
self.AVCenterY.constant = 0.8
}, completion: nil)
But it happens so fast it seems like it is not animated. On the other hand, when I animate the property alpha it is animated (it takes the 2.7 seconds to change). I used 2.7 sec to make sure the problem was that I was using a small duration time.
Constraints cannot be animated at all. It is the act of layout that can be animated:
UIView.animate(withDuration: 2.7, delay: 0.1, options: .allowAnimatedContent, animations: {
self.AVCenterY.constant = 0.8
theView.superview?.layoutIfNeeded() // *
}, completion: nil)
When we animate the act of layout (or when the runtime does so), then any constraint changes are also automatically animated.
Note that what I animate is the layout of the superview of the view that is to move. I called it theView but that is just something I made up. You will need an outlet to that view so that you can get its superview, and use the name of that outlet.

How to change the default layout animation curve in a custom UICollectionView layout engine?

I've got a collection view setup to run with a custom collection view layout. I can animate cells appearing and disappearing by changing their transformations and alpha on the UICollectionViewLayoutAttributes, however, I would like to change the animation curve to use springs + damping in order to make the cells "bounce" when they appear.
Does anybody have an idea of how I can achieve this?
Thanks,
Wrap your UICollectionView's deleteItems/insertItems/reloadItems calls inside UIView.animateWithDuration:delay:options:animations:completion: call.
Example:
let duration = 0.25 // Hardcoded keyboard animation duration and curve
let curve = UIViewAnimationOptions(rawValue: 7 << 16) // ↑
UIView.animate(withDuration: duration, delay: 0, options: curve, animations: ({ [weak self] in
self?.collectionView?.performBatchUpdates({
self?.collectionView?.deleteItems(at: deletedIPs)
self?.collectionView?.insertItems(at: insertedIPs)
self?.collectionView?.reloadItems(at: updatedIPs)
}, completion: nil)
}), completion: nil)

Resources