0

I'm getting a weird jerking motion whenever I reload my row and scroll up. It's fine if it's scrolling down, but scrolling back up is causing a horrible jerking effect, rendering the app useless.

I've attached the code for not only the cellforrowatindex path, but the buttons as well.

cell for row:

override func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!, object: PFObject!) -> PFTableViewCell! {
    let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! TableViewCell
    cell.nopebutton.tag = indexPath.row
    cell.nicebutton.tag = indexPath.row
    cell.killText.text = object.valueForKey("text") as! String
    cell.layoutMargins = UIEdgeInsetsZero
    cell.killText.numberOfLines = 0
    let score = object.valueForKey("count") as! Int
    cell.count.text = "\(score)"
    if cell.count.text == "\(0)"
    {
        cell.count.textColor = UIColor.grayColor()
    }
    if cell.count.text > "\(0)"
    {
        cell.count.textColor = UIColor(red: 42.0/255, green: 204.0/255, blue: 113.0/255, alpha: 1)
    }
    if cell.count.text < "\(0)"
    {
        cell.count.textColor = UIColor(red: 231.0/255, green: 76.0/255, blue: 50.0/255, alpha: 1)
    }
    if cell.count.text == "\(50)"
    {
        cell.count.textColor = UIColor(red: 249.0/255, green: 191.0/255, blue: 59.0/255, alpha: 1)
    }


    if let dict : NSDictionary = NSUserDefaults.standardUserDefaults().objectForKey("userNiceNopeDictionary") as? NSDictionary {
        cell.nicebutton.enabled = true
        cell.nopebutton.enabled = true
        if let nice  = dict[object.objectId] as? Bool{
            if nice {
                cell.nicebutton.enabled = false
            }
            else {
                cell.nopebutton.enabled = false
            }
        }
    }

    let dateUpdated = object.createdAt as NSDate
    let dateFormat = NSDateFormatter()
    dateFormat.dateFormat = "h:mm a"
    cell.time.text = (NSString(format: "%@", dateFormat.stringFromDate(dateUpdated)) as String) as String
    let replycnt = object.objectForKey("replies") as! Int

    if cell.count.text == "\(-10)"
    {
        object.deleteInBackground()
    }


    return cell

}

buttons

@IBAction func topButton(sender: AnyObject) {

  var button = sender as! UIButton
//        var view = button.superview
//        
//        var otherButton = view?.viewWithTag(102) as! UIButton
//        var label = button.superview!.viewWithTag(110) as! UILabel
//        otherButton.enabled = true
//        button.enabled = false

    var rowNumber = button.tag

    var mutableDict = NSMutableDictionary()
    if let dict = NSUserDefaults.standardUserDefaults().objectForKey("userNiceNopeDictionary") {
        mutableDict = dict.mutableCopy() as! NSMutableDictionary
    }

    let obj = self.objects[rowNumber] as! PFObject

    mutableDict.setValue(true, forKey: obj.objectId)
    NSUserDefaults.standardUserDefaults().setObject(mutableDict, forKey: "userNiceNopeDictionary")
    NSUserDefaults.standardUserDefaults().synchronize()
    let hitPoint = sender.convertPoint(CGPointZero, toView: self.tableView)
    let hitIndex = self.tableView.indexPathForRowAtPoint(hitPoint)
    let object = objectAtIndexPath(hitIndex)
    object.incrementKey("count")
    //label.text = object.objectForKey("count") as! String
//        self.tableView.reloadData()
    object.save()
    self.tableView.reloadRowsAtIndexPaths([hitIndex!], withRowAnimation: UITableViewRowAnimation.None)
}


@IBAction func bottomButton(sender: AnyObject) {

    var button = sender as! UIButton
    var rowNumber = button.tag

    var mutableDict = NSMutableDictionary()
    if let dict = NSUserDefaults.standardUserDefaults().objectForKey("userNiceNopeDictionary") {
        mutableDict = dict.mutableCopy() as! NSMutableDictionary
    }

    let obj = self.objects[rowNumber] as! PFObject

    mutableDict.setValue(false, forKey: obj.objectId)
    NSUserDefaults.standardUserDefaults().setObject(mutableDict, forKey: "userNiceNopeDictionary")
    NSUserDefaults.standardUserDefaults().synchronize()

    let hitPoint = sender.convertPoint(CGPointZero, toView: self.tableView)
    let hitIndex = self.tableView.indexPathForRowAtPoint(hitPoint)
    let object = objectAtIndexPath(hitIndex)
    object.incrementKey("count", byAmount: -1)
    self.tableView.reloadRowsAtIndexPaths([hitIndex!], withRowAnimation: UITableViewRowAnimation.None)
    object.save()

}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
    if(segue.identifier == "killDetail"){
        let indexPath = self.tableView.indexPathForSelectedRow()
        let obj = self.objects[indexPath!.row] as! PFObject
        let detailVC = segue.destinationViewController as! DetailViewController
        detailVC.kill = obj
    }
}

}

I'm under the impression that there's an issue in my heights so I've included that code as well.

 override func viewDidLoad() {
    super.viewDidLoad()
    self.tableView.separatorInset = UIEdgeInsetsZero
    self.tableView.separatorInset = UIEdgeInsetsMake(0, 0, 0, 0)
    self.tableView.separatorColor = UIColor(red: 236.0/255, green: 240.0/255, blue: 241.0/255, alpha: 1)
    navigationController?.tabBarController?.tabBar.hidden = true
    tabBarController?.tabBar.hidden = true
    self.tableView.estimatedRowHeight = 60
    self.tableView.rowHeight = UITableViewAutomaticDimension
    locationManager.desiredAccuracy = 1000
    locationManager.delegate = self
    locationManager.requestWhenInUseAuthorization()
    locationManager.startUpdatingLocation()
}

I can't for the life of me figure out what's going on causing this jerk. Like I said I think it might have to do with the automaticdimension in the row height, but when I change that the issue is still apparent.

James Chen
  • 387
  • 1
  • 4
  • 17
  • you can't really tell from your gif jerk motion but I have seen this happen before. To be honest I am surprised that you don't see this behavior scrolling down. Should happen both ways. Anyway In my case I was doing a lot of processing every time a cell was displayed. Your problem is only in override func tableView. Try commenting out code like NSDictionary and dateFormat and see if it helps. – Sam B Aug 08 '15 at 13:57
  • @SamB Those were the first things I've tried. Prior to me rebuilding this, I left out the dictionaries and dateformats however I still got the problem. – James Chen Aug 08 '15 at 14:04
  • Since I see nothing wrong with your code, at this point its a matter of process of elimination. Comment everything out from let cell = tableView down and start adding code couple of lines at a time. Also, try testing this on an actual device. Perhaps your simulator or Xcode is running out memory or space. Sorry dude, these are the hardest kind of problems to debug – Sam B Aug 08 '15 at 14:10
  • @SamB Yeah, I've been testing it on my phone as well, same problem. It's a damn shame honestly, this is the only hiccup stopping me from moving forward to distribution. I'm going to have to really pick everything apart to see whats going on. – James Chen Aug 08 '15 at 14:18
  • Is the estimated height close to the actual row height? When I switch to large dynamic type sizes I get this when I scroll back up. It seems to fail to calculate some row sizes on the way down, but does when it goes back up, causing the jump. No fix (yet!), but I have seen comments elsewhere [1](http://stackoverflow.com/a/30199373/1320544) and [2](http://stackoverflow.com/a/27031282/1320544). In my case it is one of my large (tall) table views that exhibits this - the cells near the top seem to be calculated fine, but once you get down so far it falls to bits. Might even just be an iOS bug. – Rob Glassey Aug 09 '15 at 08:50

1 Answers1

0
  1. Don't read that dictionary from user defaults every time you dequeue a cell. Do it once, outside of tableView(cellForRowAtIndexPath:. Do it in viewDidLoad or something. Picking out an element from the dictionary once it's in memory is fine though... You just don't want to read from disk (or flash, I guess) every time a cell is dequeued.

    If the dictionary is being changed, for any reason, in other places and you want to maintain a consistent state, read the dictionary in once and just pass it around between controllers/view controllers (through, e.g. prepareForSegue) and write it to user defaults when it's changed.

  2. The date formatter is probably not that big or complex of an object to make, but you only need to make one once, and then can reuse it, so I suggest you only create one once too.

edit: If you have a problem just scrolling up, it could be that the estimated row height doesn't match the actual row height. Try removing the estimated height.

Community
  • 1
  • 1
GabrielB
  • 21
  • 2
  • 1
    NSDateFormatter is *incredibly* expensive to create. You should not create NSDateFormatter instances in tableView:cellForRowAtIndexPath. http://nshipster.com/nsformatter/ – Harlan Haskins Aug 09 '15 at 12:28