ios - UIScrollView animation to targetContentOffset erratic -


i've implemented uiscrollview within uitableviewcell enables user scroll left , right reveal buttons in same fashion ios mail app. original implementation set frames , positions explicitly worked i've refactored code use autolayout throughout. animation hide/reveal 'container' buttons on left (accessory buttons) works animation brings scrollview rest when right container (edit buttons) slows before reaching desired offset before jerking final position.

all calculations use same math transformed (e.g. + rather - value, > rather < in tests) depending on side container located , values displayed logging correct. can't see obvious code errors , there no constraints cells set in ib. bug or there obvious i've missed through staring @ code last hour?

class swipeytableviewcell: uitableviewcell {      // mark: constants     private let thresholdvelocity = cgfloat(0.6)     private let maxclosureduration = cgfloat(40)      // mark: properties     private var buttoncontainers = [buttoncontainertype: buttoncontainer]()     private var leftcontainerwidth: cgfloat {         return buttoncontainers[.accessory]?.containerwidthwhenopen ?? cgfloat(0)     }     private var rightcontainerwidth: cgfloat {         return buttoncontainers[.edit]?.containerwidthwhenopen ?? cgfloat(0)     }     private var buttoncontainerrightanchor = nslayoutconstraint()     private var isopen = false      // mark: subviews     private let scrollview = uiscrollview()       // mark: lifecycle methods     override func awakefromnib() {         super.awakefromnib()         // initialization code         scrollview.delegate = self         scrollview.showshorizontalscrollindicator = false         scrollview.showsverticalscrollindicator = false         contentview.addsubview(scrollview)         scrollview.translatesautoresizingmaskintoconstraints = false         scrollview.topanchor.constraintequaltoanchor(contentview.topanchor).active = true         scrollview.leftanchor.constraintequaltoanchor(contentview.leftanchor).active = true         scrollview.rightanchor.constraintequaltoanchor(contentview.rightanchor).active = true         scrollview.bottomanchor.constraintequaltoanchor(contentview.bottomanchor).active = true          let scrollcontentview = uiview()         scrollcontentview.backgroundcolor = uicolor.cyancolor()         scrollview.addsubview(scrollcontentview)         scrollcontentview.translatesautoresizingmaskintoconstraints = false         scrollcontentview.topanchor.constraintequaltoanchor(scrollview.topanchor).active = true         scrollcontentview.leftanchor.constraintequaltoanchor(scrollview.leftanchor).active = true         scrollcontentview.rightanchor.constraintequaltoanchor(scrollview.rightanchor).active = true         scrollcontentview.bottomanchor.constraintequaltoanchor(scrollview.bottomanchor).active = true         scrollcontentview.widthanchor.constraintequaltoanchor(contentview.widthanchor, constant: 10).active = true         scrollcontentview.heightanchor.constraintequaltoanchor(contentview.heightanchor).active = true          buttoncontainers[.accessory] = buttoncontainer(type: .accessory, scrollcontentview: scrollcontentview)         buttoncontainers[.edit] = buttoncontainer(type: .edit, scrollcontentview: scrollcontentview)         bc in buttoncontainers.values {             scrollcontentview.addsubview(bc)             bc.widthanchor.constraintequaltoanchor(contentview.widthanchor).active = true             bc.heightanchor.constraintequaltoanchor(scrollcontentview.heightanchor).active = true             bc.topanchor.constraintequaltoanchor(scrollcontentview.topanchor).active = true             bc.containertocontentconstraint.active = true         }          scrollview.contentinset = uiedgeinsetsmake(0, leftcontainerwidth, 0, rightcontainerwidth)     }       func closecontainer() {         scrollview.contentoffset.x = cgfloat(0)     }  }   extension swipeytableviewcell: uiscrollviewdelegate {      func scrollviewwillenddragging(scrollview: uiscrollview, withvelocity velocity: cgpoint,         targetcontentoffset: unsafemutablepointer<cgpoint>) {             let xoffset: cgfloat = scrollview.contentoffset.x             isopen = false             bc in buttoncontainers.values {                 if bc.iscontaineropen(xoffset, thresholdvelocity: thresholdvelocity, velocity: velocity) {                     targetcontentoffset.memory.x = bc.offsetrequiredtoopencontainer()                     nslog("target offset \(targetcontentoffset.memory.x)")                     isopen = true                     break /// 1 container can open @ time cn exit here                 }             }             if !isopen {                 nslog("closing container")                 targetcontentoffset.memory.x = cgfloat(0)                 let ms: cgfloat = xoffset / velocity.x  /// if scroll isn't on fast path zero, animate closed                 if (velocity.x == 0 || ms < 0 || ms > maxclosureduration) {                     nslog("animating closed")                     dispatch_async(dispatch_get_main_queue()) {                         scrollview.setcontentoffset(cgpointzero, animated: true)                     }                 }             }     }  /** defines position of container view buttons assosicated swipeytableviewcell  - edit:      identifier uiview acts container buttons right of cell - accessory: identifier uiview acts container buttons left of vell */ enum buttoncontainertype {     case edit, accessory }  extension buttoncontainertype {     func getconstraints(scrollcontentview: uiview, buttoncontainer: uiview) -> nslayoutconstraint {         switch self {         case edit:             return buttoncontainer.leftanchor.constraintequaltoanchor(scrollcontentview.rightanchor)         case accessory:             return buttoncontainer.rightanchor.constraintgreaterthanorequaltoanchor(scrollcontentview.leftanchor)         }     }       func containeropenedtest() -> ((scrollviewoffset: cgfloat, containerfullyopenwidth: cgfloat, thresholdvelocity: cgfloat, velocity: cgpoint) -> bool) {         switch self {         case edit:             return {(scrollviewoffset: cgfloat, containerfullyopenwidth: cgfloat, thresholdvelocity: cgfloat, velocity: cgpoint) -> bool in                 (scrollviewoffset > containerfullyopenwidth || (scrollviewoffset > 0 && velocity.x > thresholdvelocity))             }         case accessory:             return {(scrollviewoffset: cgfloat, containerfullyopenwidth: cgfloat, thresholdvelocity: cgfloat, velocity: cgpoint) -> bool in                 (scrollviewoffset < -containerfullyopenwidth || (scrollviewoffset < 0 && velocity.x < -thresholdvelocity))             }         }     }       func transformoffsetforcontainerside(containerwidthwhenopen: cgfloat) -> cgfloat {         switch self {         case edit:             return containerwidthwhenopen         case accessory:             return -containerwidthwhenopen         }     } }   /// uiview subclass acts container buttongs associated swipeytablecellview class buttoncontainer: uiview {      private let scrollcontentview: uiview     private let type: buttoncontainertype      private let maxnumberofbuttons = 3     let buttonwidth = cgfloat(65)     private var buttons = [uibutton]()     var containerwidthwhenopen: cgfloat { //        return cgfloat(buttons.count) * buttonwidth         return buttonwidth // todo: multiple buttons not yet implements - cause bug!!     }     var containertocontentconstraint: nslayoutconstraint {         return type.getconstraints(scrollcontentview, buttoncontainer: self)     }     var offsetfromcontainer = cgfloat(0) {         didset {             let delta = abs(oldvalue - offsetfromcontainer)             containertocontentconstraint.constant = offsetfromcontainer             if delta > (containerwidthwhenopen * 0.5) { /// number arbitary - can more formal?                 animateconstraintwithduration(0.1, delay: 0, options: uiviewanimationoptions.curveeaseout, completion: nil) /// ensure large changes animated rather snapped             }         }     }       // mark: initialisers      init(type: buttoncontainertype, scrollcontentview: uiview) {         self.type = type         self.scrollcontentview = scrollcontentview         super.init(frame: cgrectzero)         backgroundcolor = uicolor.bluecolor()         translatesautoresizingmaskintoconstraints = false     }       required init?(coder adecoder: nscoder) {         fatalerror("init(coder:) has not been implemented")     }       // mark: public methods      func iscontaineropen(scrollviewoffset: cgfloat, thresholdvelocity: cgfloat, velocity: cgpoint) -> bool {         let closure = type.containeropenedtest()         return closure(scrollviewoffset: scrollviewoffset, containerfullyopenwidth: containerwidthwhenopen, thresholdvelocity: thresholdvelocity, velocity: velocity)     }       func offsetrequiredtoopencontainer() -> cgfloat {         return type.transformoffsetforcontainerside(containerwidthwhenopen)     } } 

ok - found error , typo left earlier experimentation uiscrollview. clue in earlier comment 'snap' occurring within 10pt of desired targetcontentoffset...

the scrollcontentview width constraint set incorrectly follows:

scrollcontentview.widthanchor.constraintequaltoanchor(contentview.widthanchor, constant: 10).active = true 

before found out force uiscrollview scroll setting contentinset, made subview larger cell's contentview uiscrollview pinned to. i've been refactoring code verbatim use new anchor properties, old code propagated , got bug!

so, not ios @ fault...just me not paying attention. lesson learnt! have other ideas on how implement things might little tidier.


Comments

Popular posts from this blog

html - Outlook 2010 Anchor (url/address/link) -

javascript - Why does running this loop 9 times take 100x longer than running it 8 times? -

Getting gateway time-out Rails app with Nginx + Puma running on Digital Ocean -