我正在尝试将一些ObjC转换为水平滚动UITableViewCell到Swift。最初的ObjC版本使用了单元格内容的显式大小,但我试图在我的应用程序中使用autolayout来保持一致性。
一切顺利,直到我想要在屏幕的左侧和右侧创建按钮,可以通过以与Mail应用程序中的删除按钮相同的方式滑动来隐藏/取消隐藏按钮(但我想要一个更多的功能因此是一个自制的解决方案)。原始代码通过在左侧和右侧创建与按钮大小相同的scrollview内容插件实现了这一点,但我有一些概念和程序问题。
首先,我将contentInset理解为滚动视图周围的“缓冲区”,人们经常使用它来防止内容区域被状态或导航栏隐藏(即最大化屏幕空间的方式)。因此,无论contentInsets如何,滚动视图的内容区域应保持不变,因为它们本质上是一个边距。然而,原始编码器创建了一个负面插图,这些似乎神奇地增加了内容区域,但插图中的视图隐藏在屏幕外。这是怎么回事?为什么我们这样做而不仅仅是改变偏移来补偿屏幕外按钮的宽度?
其次,通过向远或足够快地滑动来显示按钮,以便滚动视图的最终静止点显示隐藏按钮。将scrollview锁定到位并停止重新隐藏按钮的代码位于scrollViewWillEndDragging函数内,并更改targetContentOffset以实现此目的。这在使用显式视图和按钮大小时有效,但在使用autolayout时无法工作。但是,如果您调用scrollview.setContentOffset它可以工作。为什么不同?当然这是一回事,但我猜测必须有一个不同的方法调用序列。
代码如下(我已经编辑了一些不那么重要的材料)。这是概念验证,所以不是很优雅!
创建scrollView:
let scrollView = UIScrollView()
scrollView.backgroundColor = UIColor.blueColor()
scrollView.delegate = self;
scrollView.scrollsToTop = false
scrollView.showsHorizontalScrollIndicator = false
scrollView.showsVerticalScrollIndicator = false
self.contentView.addSubview(scrollView)
// pin scrollView to cell contentView
scrollView.setTranslatesAutoresizingMaskIntoConstraints(false)
self.contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|[sv]|", options: nil, metrics: nil, views: ["sv" : scrollView]))
self.contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|[sv]|", options: nil, metrics: nil, views: ["sv" : scrollView]))
let scrollViewContent = UIView()
scrollView.addSubview(scrollViewContent)
// add content sequentially (L>>R) to the content subview of scrollView
var lastObjectAdded: UIView? = nil // allows constraints to be created between adjacent contentView subviews
// add button on the left
let leftButton = UIButton.buttonWithType(UIButtonType.Custom) as! UIButton
leftButton.backgroundColor = UIColor.purpleColor()
leftButton.setTitle("Click Me!", forState: UIControlState.Normal)
leftButton.setTitleColor(UIColor.whiteColor(), forState: UIControlState.Normal)
// width needs to be set explicitly so inset for scroll view can be set in this class
// (when using autolayout, frame dimensions are not available until after all subviews are laid out)
leftButton.frame.size.width = CGFloat(100)
scrollViewContent.addSubview(leftButton)
// size using autolayout
leftButton.setTranslatesAutoresizingMaskIntoConstraints(false)
// give button a fixed width
// TODO: Enable this to be defined by function call
scrollViewContent.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:[but(==100)]", options: nil, metrics: nil, views: ["but" : leftButton]))
// pin height to height of cell's contentView
self.contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:[but(==cv)]", options: nil, metrics: nil, views: ["but" : leftButton, "cv" : self.contentView]))
// pin to top left of the scollView subview
scrollViewContent.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|[but]", options: nil, metrics: nil, views: ["but" : leftButton]))
scrollViewContent.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|[but]", options: nil, metrics: nil, views: ["but" : leftButton]))
// store a reference to the button to be used for positioning other subviews
lastObjectAdded = leftButton
testView = leftButton
< SNIP ---
--- SNIP>
// test that I can add a label that matches the width of the tableViewCell's contentView subview'
let specialLab = UILabel()
specialLab.setTranslatesAutoresizingMaskIntoConstraints(false)
specialLab.backgroundColor = UIColor.orangeColor()
specialLab.text = "WIDTH MATCHES TABLEVIEWCELL WIDTH"
scrollViewContent.addSubview(specialLab)
scrollViewContent.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-(10)-[lab]", options: nil, metrics: nil, views: ["lab" : specialLab]))
// this is the important constraint
self.contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:[lab(==cv)]", options: nil, metrics: nil, views: ["lab" : specialLab, "cv" : contentView]))
scrollViewContent.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:[last]-(10)-[lab]", options: nil, metrics: nil, views: ["lab" : specialLab, "last" : lastObjectAdded!]))
lastObjectAdded = specialLab
// pin last label to right which dictates content size width
scrollViewContent.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:[lab]-10-|", options: nil, metrics: nil, views: ["lab" : lastObjectAdded!]))
// set scrollView's content view height and frame-to-superview constraints
// (width comes from subview constraints)
// content view is calculated for us
scrollViewContent.setTranslatesAutoresizingMaskIntoConstraints(false)
scrollView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|[svc]|", options: nil, metrics: nil, views: ["svc" : scrollViewContent]))
scrollView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|[svc]|", options: nil, metrics: nil, views: ["svc" : scrollViewContent]))
// create inset to hide button on left
scrollView.contentInset = UIEdgeInsetsMake(0, -leftButton.bounds.width, 0, 0)
第二位代码涉及取消隐藏左侧的按钮:
extension SwipeableViewCell: UIScrollViewDelegate {
func scrollViewWillEndDragging(scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
let left = CGFloat(100.0) // this is the inset required to hide the left button(s)
let kSwipeableTableViewCellOpenVelocityThreshold = CGFloat(0.6) // minimum velocity req to reveal buttons
let kSwipeableTableViewCellMaxCloseMilliseconds = CGFloat(300) // max time for the button to be hidden
let x = scrollView.contentOffset.x
// check to see if swipe from left is far enough and fast enough to reveal button
if (left > 0 && (x < 0 || (x < left && velocity.x < -kSwipeableTableViewCellOpenVelocityThreshold))) {
// manually set the offset to reveal the whole button but no more
// targetContentOffset.memory = CGPointZero // this should work but doesn't - offset is not retained
dispatch_async(dispatch_get_main_queue()) {
scrollView.setContentOffset(CGPointZero, animated: true) // this works!
}
} else {
// if not, hide the button
dispatch_async(dispatch_get_main_queue()) {
scrollView.setContentOffset(CGPoint(x: CGFloat(100), y: CGFloat(0)), animated: true)
}
}
}
}