2

我在 swift 中拼凑了一个 UITableView 子类,它复制了 Apple 自己的应用程序中使用的可滑动按钮的一些功能。它在 Xcode 7.0 beta 4 中运行良好,但由于使用 beta 5,当表格首次显示时,单元格最初显示的 contentOffset 不正确(等于用于隐藏按钮的 contentInset)。旋转视图或将单元格滚动到屏幕外可以更正该问题 - 此错误仅在表格视图的初始呈现时发生。

在创建单元格时检查它们的 contentOffset 表明 contentOffset 设置为零,但在生命周期的某个地方,此值被覆盖。我可以通过将以下内容添加到表视图控制器来解决问题:

override func viewDidAppear(animated: Bool) {
    super.viewDidAppear(animated)
    for cell in table.visibleCells {
        (cell as! SwipeableTableViewCell).scrollView.contentOffset.x = CGFloat(0)
    }
}

但是,它不是很优雅,因为我显然是在与系统作斗争。谁能解释自 Beta 4 以来发生了什么变化?

下面 UITableViewCell 子类的源代码(对于这个早期实现中的代码风格表示歉意 - 我是新手,仍在重构过程中)

class SwipeableTableViewCell: UITableViewCell, UITableViewDelegate {

    //MARK: Constants
    private let thresholdVelocity = CGFloat(0.6)
    private let maxClosureDuration = CGFloat(40)
    private let buttonCount = 3
    private let buttonWidth = CGFloat(50)

    //MARK: Computed properties
    private var buttonContainerWidth: CGFloat {
        return CGFloat(buttonCount) * buttonWidth
    }

    //MARK: Properties
    let scrollView = UIScrollView() // TODO: was originally private but have had to allow superclass access to correct contentOffset glitch
    private let scrollContentView = UIView() /// positioned using constraints
    private var buttonContainers: (left: ButtonContainer!, right: ButtonContainer!)
    private let labelView = UILabel() /// positioned using constraints
    private var buttonsLeft = [SwipeableCellButton]()
    private var buttonsRight = [SwipeableCellButton]()
    private var viewDictionary: [String: UIView]!
    private let buttonColors = [UIColor.redColor(), UIColor.orangeColor(), UIColor.purpleColor()]


    //MARK: Initialisers
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        viewDictionary = ["cv": contentView, "sv" : scrollView, "scv" : scrollContentView, "lv" : labelView]
    }


    //MARK: Lifecycle methods
    override func awakeFromNib() {
        super.awakeFromNib()

        // setup scrollView display characteristics
        scrollView.delegate = self
        scrollView.translatesAutoresizingMaskIntoConstraints = false
        scrollView.showsHorizontalScrollIndicator = false
        scrollView.showsVerticalScrollIndicator = false

        // build cell's view hierachy
        contentView.addSubview(scrollView)
        contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|[sv]|", options: [], metrics: nil, views: viewDictionary))
        contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|[sv]|", options: [], metrics: nil, views: viewDictionary))

        // setup scrollContentView
        scrollContentView.translatesAutoresizingMaskIntoConstraints = false
        scrollContentView.backgroundColor = UIColor.greenColor()
        scrollView.addSubview(scrollContentView)
        contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:[scv(==cv)]", options: [], metrics: nil, views: viewDictionary)) // match width of cell contentView
        contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:[scv(==cv)]", options: [], metrics: nil, views: viewDictionary)) // match height of cell contentView
        scrollView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|[scv]|", options: [], metrics: nil, views: viewDictionary)) // pin L+R edges to scrollview
        scrollView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|[scv]|", options: [], metrics: nil, views: viewDictionary)) // pin top+bottom edges to scrollview

        labelView.backgroundColor = UIColor.blueColor()
        labelView.textColor = UIColor.whiteColor()
        labelView.text = "TEST LABEL VIEW"
        labelView.translatesAutoresizingMaskIntoConstraints = false
        scrollContentView.addSubview(labelView)
        scrollContentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|[lv]|", options: [], metrics: nil, views: viewDictionary)) /// pin to L&R edges of superview
        scrollContentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|[lv]|", options: [], metrics: nil, views: viewDictionary)) /// pin to top and bottom of superview

        // setup button containers
        buttonContainers.left = ButtonContainer(properties: ContainerProperties.Left(width: buttonContainerWidth, height: contentView.frame.height, buttonWidth: buttonWidth))
        buttonContainers.right = ButtonContainer(properties: ContainerProperties.Right(width: buttonContainerWidth, height: contentView.frame.height, buttonWidth: buttonWidth))
        buttonContainers.right.frame.origin.x = contentView.frame.width
        viewDictionary.updateValue(buttonContainers.left, forKey: "bcl")
        viewDictionary.updateValue(buttonContainers.right, forKey: "bcr")

        for i in 1...buttonCount {
            var endX = buttonContainerWidth - (CGFloat(i) * buttonWidth) // calc for left button container
            let buttonL = SwipeableCellButton(container: buttonContainers.left, width: buttonWidth, endX: endX)
            buttonL.setAppearance("BUT\(i)", titleColor: UIColor.whiteColor(), backgroundColor: buttonColors[i - 1])

            endX = CGFloat(i - 1) * buttonWidth // re-calc for right button container
            let buttonR = SwipeableCellButton(container: buttonContainers.right, width: buttonWidth, endX: endX)
            buttonR.setAppearance("BUT\(i)", titleColor: UIColor.whiteColor(), backgroundColor: buttonColors[i - 1])

            buttonsLeft.append(buttonL)
            buttonsRight.append(buttonR)
            buttonContainers.left.addSubview(buttonL)
            buttonContainers.right.addSubview(buttonR)
        }
        scrollContentView.addSubview(buttonContainers.left)
        scrollContentView.addSubview(buttonContainers.right)

        // enable scrolling by adding an inset
        scrollView.contentInset = UIEdgeInsetsMake(0, buttonContainers.left.frame.width, 0, buttonContainers.right.frame.width)

    }

    override func layoutSubviews() {
        super.layoutSubviews()
        /// ensure x origin of R button container frmme is set correcntly
        buttonContainers.right.frame.origin.x = contentView.frame.width
        /// reset offset
        scrollView.contentOffset = CGPointZero
        NSLog("x offset after layoutSubviews: \(scrollView.contentOffset.x)")
    }


    //MARK: Private methods
    private func updateButtonPositions(buttonContainerVisibleWidth: CGFloat) {
        for s in buttonContainers.left.subviews {
            if let b = s as? SwipeableCellButton {
                b.updatePosition(buttonContainerVisibleWidth)
            }
        }
        for s in buttonContainers.right.subviews {
            if let b = s as? SwipeableCellButton {
                b.updatePosition(buttonContainerVisibleWidth)
            }
        }
    }
}


//MARK: Extension  - UIScrollViewDelegate
extension SwipeableTableViewCell : UIScrollViewDelegate {

    func scrollViewWillBeginDragging(scrollView: UIScrollView) {
        NSLog("Will begin dragging")
    }


    func scrollViewWillEndDragging(scrollView: UIScrollView, withVelocity velocity: CGPoint,
        targetContentOffset: UnsafeMutablePointer<CGPoint>) {
            NSLog("viewWillEndDragging")
            let x: CGFloat = scrollView.contentOffset.x
            NSLog("X offset \(x)")
            let left = buttonContainers.left.frame.width, right = buttonContainers.right.frame.width
            if (left > 0 && (x < -left || (x < 0 && velocity.x < -thresholdVelocity))) {
                targetContentOffset.memory.x = -left
            } else if (right > 0 && (x > right || (x > 0 && velocity.x > thresholdVelocity))) {
                targetContentOffset.memory.x = right
            } else {
                targetContentOffset.memory = CGPointZero

                // if the scroll isn't on a fast path to zero, animate it closed
                let ms: CGFloat = x / velocity.x
                if (velocity.x == 0 || ms < 0 || ms > maxClosureDuration) {
                    dispatch_async(dispatch_get_main_queue()) {
                        scrollView.setContentOffset(CGPointZero, animated: true)
                    }
                }
            }
    }


    func scrollViewDidScroll(scrollView: UIScrollView) {
        NSLog("viewDidScroll x offset \(scrollView.contentOffset.x)")
        let buttonContainerVisibleWidth = min(buttonContainerWidth, abs(scrollView.contentOffset.x))
        updateButtonPositions(buttonContainerVisibleWidth)
        /// stop the left button container moving away from the left margin of the cell
        if (scrollView.contentOffset.x < -buttonContainers.left.frame.width) {
            // Make the left buttonsLeft stay in place.
            buttonContainers.left.frame = CGRectMake(scrollView.contentOffset.x, 0, buttonContainers.left.frame.width, buttonContainers.left.frame.size.height)
            /// prevent right button container moving away from the right margin of the cell
        } else if (scrollView.contentOffset.x > buttonContainers.right.frame.width) {
            buttonContainers.right.frame = CGRectMake(scrollView.frame.size.width - buttonContainerWidth + scrollView.contentOffset.x, 0, buttonContainers.right.frame.width, buttonContainers.right.frame.height)
        }
    }
}
4

0 回答 0