I'm trying to simulate a shimmering animation in each cell of a UICollectionView.
When I start the app it works fine:
But when I scroll to the left each cell gets itself animation:
What I want is when I to scroll it to left, it works like the first image. For a while, I've just put this code below to do the animation:
func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
guard let cell = cell as? ShimmeringCell else { return }
UIView.animate(withDuration: 1.0, delay: 0, options: [.autoreverse, .repeat], animations: {
cell.alpha = 0.4
}, completion: nil)
}
This is the full code: Github
Can anyone help me?
I found the answer.
To solve this issue, I had to create a new UIView and add this in a subview of UICollectionView:
let newCollectionView: UICollectionView = {
// Here was creating a collectionView
return collection
}()
let viewExample: UIView = {
// Here was creating a new view
return view
}()
override func viewDidLoad() {
super.viewDidLoad()
newCollectionView.delegate = self
newCollectionView.dataSource = self
newCollectionView.register(ShimmeringCell.self, forCellWithReuseIdentifier: cellId)
self.view.addSubview(self.newCollectionView)
setupCollection()
}
private func setupCollection() {
// Constraints...
self.newCollectionView.addSubview(self.viewExample)
setupViewExample()
}
private func setupViewExample() {
// Constraints...
}
After that, I just put the animation in this view:
override func viewDidLoad() {
super.viewDidLoad()
newCollectionView.delegate = self
newCollectionView.dataSource = self
newCollectionView.register(ShimmeringCell.self, forCellWithReuseIdentifier: cellId)
self.view.addSubview(self.newCollectionView)
setupCollection()
self.viewExample.alpha = 0.6
UIView.animate(withDuration: 1.0, delay: 0, options: [.repeat, .autoreverse, .allowUserInteraction], animations: {
self.viewExample.alpha = 0
}, completion: nil)
}
It's resulted:
Thanks for all and see you somewhere on the world.
Github with this solution: Github
Related
So I'm using a custom animation for how the table cells appear. They sort of fade in and slide up a little. I'm populating the table with data from an API call.
What I'm noticing is that it doesn't always happen. Sometimes it works as expected, other times the cells/data just appear abruptly, no animation/movement. They just sort of pop in at what would would be the end of the animation. I don't really see any consistent pattern in when it works and when it doesn't. Seems pretty random.
Any idea what is causing this behavior?
Here's the code for the animation and where it is in the tableview function:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell: RecentActivityCell = recentActivityView.dequeueReusableCell(withIdentifier: "cellId", for: indexPath) as! RecentActivityCell
var lastInitialDisplayableCell = false
//change flag as soon as last displayable cell is being loaded (which will mean table has initially loaded)
if userActivityArray.count > 0 && !finishedLoadingInitialTableCells {
if let indexPathsForVisibleRows = tableView.indexPathsForVisibleRows,
let lastIndexPath = indexPathsForVisibleRows.last, lastIndexPath.row == indexPath.row {
lastInitialDisplayableCell = true
}
}
if !finishedLoadingInitialTableCells {
if lastInitialDisplayableCell {
finishedLoadingInitialTableCells = true
}
//animates the cell as it is being displayed for the first time
cell.transform = CGAffineTransform(translationX: 0, y: 40/2)
cell.alpha = 0
UIView.animate(withDuration: 0.5, delay: 0.05*Double(indexPath.row), options: [.curveEaseInOut], animations: {
cell.transform = CGAffineTransform(translationX: 0, y: 0)
cell.alpha = 1
}, completion: nil)
}
cell.backgroundColor = UIColor.clear
if userActivityArray[indexPath.row].status == "Success" {
cell.statusImage.image = UIImage(named: "passImage")
} else {
cell.statusImage.image = UIImage(named: "failImage")
}
cell.activity = userActivityArray[indexPath.row]
return cell
}
Try to use your animation in "yourTableViewCell" by overriding:
override func prepareForReuse() {
super.prepareForReuse()
\\ Animation code over here
}
dont use in "CellForRowAt"
I've a custom view that is an outlet in prototype cell.
In the view, i make an animation on certain constraint constant.
This animation works only for the first cell, but not on the other cells.
override open func awakeFromNib() {
super.awakeFromNib()
self.xibSetup()
self.setViews()
}
private func animateTitleUp(){
if self.titleTopConstraint.constant == self.titleOriginConstant{
self.titleTopConstraint.constant -= self.titleChangeConstant
self.title.alpha = 1
UIView.animate(withDuration: 4, animations:{ [weak self]() in
self?.contentView?.layoutIfNeeded()
})
}
}
func xibSetup() {
guard let view = loadViewFromNib() else { return }
view.frame = bounds
self.contentView = view
addSubview(self.contentView!)
}
func loadViewFromNib() -> UIView? {
setBundle()
let nib = UINib(nibName: "CustomPicker", bundle: bundle)
return nib.instantiate(
withOwner: self,
options: nil).first as? UIView
}
I would like to give press effect for collectionViewCell on didSelectItem. On click I would like to add some color and after some delay I would like to revert the color. While trying the code below, press effect is added only after some delay. Can anyone suggest how to achieve this?
DispatchQueue.global(qos: .utility).async {
let cell = collectionView.cellForItem(at: indexPath) as ..cell
// Change bg color for foreground layer
DispatchQueue.main.async {
Timer.scheduledTimer(timeInterval: 0.10, target: self, selector: #selector(self.updateCell(timer:)) , userInfo: ["cell":cell,"collectionview":collectionView], repeats: false)
}
// Some calculations
}
.cellForItem(at: indexPath) as ..cell should usually be called in the main thread, you might see weird behavior if you are doing it from your utility queue
I suggest also to not use DispatchQueues and handle animation timing by yourself, there's already a nice function that does exactly what you want. This would be my suggestion:
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let cell = collectionView.cellForItem(at: indexPath) as ..cell
UIView.animate(withDuration: 0.2, delay: 0.4, options: .curveEaseInOut, animations: {
cell.backgroundColor = UIColor.red
}, completion: { _ in
// here the animation is done
})
}
Update with a nicer solution
Anyway, i think a nicer approach than handling the animation in the didSelectItemAt function of your UICollectionViewDelegate would be overriding the isSelected property of the cell.
In this example, I animate the backgroundColor whenever the selection state of the UICollectionViewCell changes - red when selected, green otherwise. Just replace that with whatever style you desire.
class YourCell: UICollectionViewCell {
override var isSelected: Bool {
willSet(newValue) {
let newColor = isSelected ? UIColor.red : UIColor.green
UIView.animate(withDuration: 1, delay: 0.4, options: .curveEaseInOut, animations: {
self.backgroundColor = newColor
}, completion: { _ in
// here the animation is done - nothing to do here probably
})
}
}
}
I have a UITableView with more cells than fit on screen. When I get a notification from my data model I want to jump to a specific row and show a very basic animation.
My code is:
func animateBackgroundColor(indexPath: NSIndexPath) {
dispatch_async(dispatch_get_main_queue()) {
NSLog("table should be at the right position")
if let cell = self.tableView.cellForRowAtIndexPath(indexPath) as? BasicCardCell {
var actColor = cell.backgroundColor
self.manager.vibrate()
UIView.animateWithDuration(0.2, animations: { cell.backgroundColor = UIColor.redColor() }, completion: {
_ in
UIView.animateWithDuration(0.2, animations: { cell.backgroundColor = actColor }, completion: { _ in
self.readNotificationCount--
if self.readNotificationCount >= 0 {
var legicCard = self.legicCards[indexPath.section]
legicCard.wasRead = false
self.reloadTableViewData()
} else {
self.animateBackgroundColor(indexPath)
}
})
})
}
}
}
func cardWasRead(notification: NSNotification) {
readNotificationCount++
NSLog("\(readNotificationCount)")
if let userInfo = notification.userInfo as? [String : AnyObject], let index = userInfo["Index"] as? Int {
dispatch_sync(dispatch_get_main_queue()){
self.tableView.scrollToRowAtIndexPath(NSIndexPath(forRow: 0, inSection: index), atScrollPosition: .None, animated: true)
self.tableView.layoutIfNeeded()
NSLog("table should scroll to selected row")
}
self.animateBackgroundColor(NSIndexPath(forRow: 0, inSection: index))
}
}
I hoped that the dispatch_sync part would delay the execution of my animateBackgroundColor method until the scrolling is done. Unfortunately that is not the case so that animateBackgroundColor gets called when the row is not visible yet -> cellForRowAtIndexPath returns nil and my animation won't happen. If no scrolling is needed the animation works without problem.
Can anyone tell my how to delay the execution of my animateBackgroundColor function until the scrolling is done?
Thank you very much and kind regards
Delaying animation does not seem to be a good solution for this since scrollToRowAtIndexPath animation duration is set based on distance from current list item to specified item. To solve this you need to execute animateBackgroudColor after scrollToRowAtIndexPath animation is completed by implementing scrollViewDidEndScrollingAnimation UITableViewDelegate method. The tricky part here is to get indexPath at which tableview did scroll. A possible workaround:
var indexPath:NSIndexpath?
func cardWasRead(notification: NSNotification) {
readNotificationCount++
NSLog("\(readNotificationCount)")
if let userInfo = notification.userInfo as? [String : AnyObject], let index = userInfo["Index"] as? Int{
dispatch_sync(dispatch_get_main_queue()){
self.indexPath = NSIndexPath(forRow: 0, inSection: index)
self.tableView.scrollToRowAtIndexPath(self.indexPath, atScrollPosition: .None, animated: true)
self.tableView.layoutIfNeeded()
NSLog("table should scroll to selected row")
}
}
}
func scrollViewDidEndScrollingAnimation(scrollView: UIScrollView) {
self.animateBackgroundColor(self.indexPath)
indexPath = nil
}
Here are my solution
1) Create a .swift file, and copy code below into it:
typealias SwagScrollCallback = (_ finish: Bool) -> Void
class UICollectionViewBase: NSObject, UICollectionViewDelegate {
static var shared = UICollectionViewBase()
var tempDelegate: UIScrollViewDelegate?
var callback: SwagScrollCallback?
func startCheckScrollAnimation(scroll: UIScrollView, callback: SwagScrollCallback?){
if let dele = scroll.delegate {
self.tempDelegate = dele
}
self.callback = callback
scroll.delegate = self
}
func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) {
callback?(true)
if let dele = self.tempDelegate {
scrollView.delegate = dele
tempDelegate = nil
}
}
}
extension UICollectionView {
func scrollToItem(at indexPath: IndexPath, at scrollPosition: UICollectionView.ScrollPosition, _ callback: SwagScrollCallback?){
UICollectionViewBase.shared.startCheckScrollAnimation(scroll: self, callback: callback)
self.scrollToItem(at: indexPath, at: scrollPosition, animated: true)
}
}
2) Example:
#IBAction func onBtnShow(){
let index = IndexPath.init(item: 58, section: 0)
self.clv.scrollToItem(at: index, at: .centeredVertically) { [weak self] (finish) in
guard let `self` = self else { return }
// Change color temporarily
if let cell = self.clv.cellForItem(at: index) as? MyCell {
cell.backgroundColor = .yellow
cell.lbl.textColor = .red
}
// Reset
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
self.clv.reloadData()
}
}
}
3) My github code example: github here
I has similar problem. I just do this.
UIView.animateWithDuration(0.2,delay:0.0,options: nil,animations:{
self.tableView.scrollToRowAtIndexPath(self.indexPath!, atScrollPosition: .Middle, animated: true)},
completion: { finished in UIView.animateWithDuration(0.5, animations:{
self.animateBackgroundColor(self.indexPath)})})}
In swift, I am transitioning an object in to the view, and I need it to slide in, or fade in. How can I obtain this type of animation in my program?
I have all my view elements made programmatically without IB (Interface Builder)
Is there documentation I can look at for reference? I could not find any.
I'm used to using [UIView animateWithDuration ...], but I found it a bit tricky switching the block syntax over to Swift at first. Here's a quick lazy code:
view.alpha = 0.0
UIView.animateWithDuration(0.25, animations: {
view.alpha = 1.0
}, completion: {
(value: Bool) in
println(">>> Animation done.")
})
Or to animate the position appearing from the left of the screen:
// some variables for the UIView
let width = 200
let height = 100
let yPosition = 10
// create view and position it offscreen to the left
let view = UIView()
// you could have also used the new more verbose Swift way of making a CGRect:
// CGRect(x: xValue, y: yValue, width: widthValue, height: heightValue)
view.frame = CGRectMake(-width, yPosition, width, height)
view.backgroundColor = UIColor.blueColor() // etc...
// animation position over 1.0 second duration
UIView.animateWithDuration(1.0, animations: {
view.frame = CGRectMake(0, yPosition, width, height)
})
If you're completely new to the UIView animation APIs I wrote post that covers a bunch of animation options in swift here: http://mathewsanders.com/prototyping-iOS-iPhone-iPad-animations-in-swift/
Swift 3 & 4
Please Add necessary objects like tableView
import UIKit
class SecondViewController: UIViewController,UITableViewDelegate,UITableViewDataSource {
#IBOutlet var tabl: UITableView!
var name = ["tony","abu"]
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return name.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell()
cell.textLabel?.text = name[indexPath.row]
return cell
}
override func viewWillAppear(_ animated: Bool) {
tabl.center.x = self.view.frame.width/5
UIView.animate(withDuration: 1.0, delay: 0, usingSpringWithDamping: 1.0, initialSpringVelocity:0.45, options: [], animations: ({
self.tabl.center.x = self.view.frame.width/3
//self.buttonAction.center.x = self.view.frame.width/2
}), completion: nil)
}
}
Apples Core animation seen here https://developer.apple.com/library/Mac/DOCUMENTATION/Cocoa/Conceptual/CoreAnimation_guide/Introduction/Introduction.html
its very fun. youtube it