Thoms Thoms - 2 months ago 11
Swift Question

How to update tableView only if it scrolls slowly?

I want to call

tableView.reloadData()
only, if the tableView is scrolled slowly to avoid flickering.
To achieve this I monitor the scrolling speed in the
scrollViewDidScroll
delegate Function.

func scrollViewDidScroll(scrollView: UIScrollView) {
let scrollSpeed:CGFloat = scrollView.contentOffset.y - previousScrollViewYOffset
previousScrollViewYOffset = scrollView.contentOffset.y
if scrollSpeed < 1.0
{
tViewMoves = false
// tableView.reloadData()
}
else
{
tViewMoves = true
}
}


The Problem is that the Bool tViewMoves is accessed and set very often but dont changes its value from true to false and vice versa. Nevertheless
tableView.reloadData()
is called every time the Bool is accessed and causes some flickering while the tableView is scrolled. I tried to overcome this issue by adding an observer with
.New
option to tViewMoves. But the
tableView.reloadData()
method is still called every time tViewMoves is set even if it doesn't changes its value.

self.addObserver(self, forKeyPath: "tViewMoves", options: .New, context: &observerContext)

override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
if context == &observerContext {
if let newValue = change?[NSKeyValueChangeNewKey] {
print("changed: \(newValue)")
tableView.reloadData()
}
}
else {
super.observeValueForKeyPath(keyPath, ofObject: object, change: change, context: context)

}
}

deinit {
self.removeObserver(self, forKeyPath: "tViewMoves", context: &observerContext)
}


Any suggestions how I could overcome this issue?

UPDATE EDIT:

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell:CalendarCell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! CalendarCell
cell.backgroundColor = UIColor.clearColor()

let buttonView = self.buttonViewArray[indexPath.row]
cell.aBackgroundView.addSubview(buttonView)

if tViewMoves == false
{

let buttonStarView = self.buttonStarViewArray[indexPath.row]
buttonStarView.myDelegate = cell
buttonStarView.kalenderVCDelegate = self
cell.aBackgroundView.addSubview(buttonStarView)


//add tapRecognizer
let tap = SubclassedTapRec(target: self, action: #selector(KalenderVC.tapButtonView(_:)))
tap.myTag = buttonStarView.tag
buttonStarView.addGestureRecognizer(tap)


}
return cell
}


UPDATE EDIT 2:

func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
let priority = DISPATCH_QUEUE_PRIORITY_DEFAULT
let myCell = cell as! CalendarCell

dispatch_async(dispatch_get_global_queue(priority, 0)) {
let buttonStarView = self.buttonStarViewArray[indexPath.row]
buttonStarView.myDelegate = myCell
buttonStarView.kalenderVCDelegate = self
myCell.aBackgroundView.addSubview(buttonStarView)

dispatch_async(dispatch_get_main_queue()) {

tableView.reloadData()
}
}
}

Answer

I would recommend something like this:

let priority = DISPATCH_QUEUE_PRIORITY_DEFAULT
dispatch_async(dispatch_get_global_queue(priority, 0)) {
    // Get data from db
    dispatch_async(dispatch_get_main_queue()) {
        // update the cell (Make all UI operations here)
    }
}

Edit Looking at your code i would change this.

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath)

    let priority = DISPATCH_QUEUE_PRIORITY_DEFAULT
    dispatch_async(dispatch_get_global_queue(priority, 0)) {

        let testView = UIView(frame: CGRectMake(0,0,100,100))
        testView.frame = CGRectMake(0, 0, 100, 10)
        testView.backgroundColor = UIColor.greenColor()

        dispatch_async(dispatch_get_main_queue()) {
            cell.addSubview(testView)
            //You should not reload the complete table view from inside the draw methods because it will cause a infinite loop
            //tableView.reloadData()
        }
    }



    return cell
}