Reputation: 125
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
Upvotes: 6
Views: 7033
Reputation: 71
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
Upvotes: 0
Reputation: 608
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)})})}
Upvotes: -3
Reputation: 10286
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
}
Upvotes: 5