Scrollview 动画时获取偏移量

Get offset when Scrollview is animated

我有一个带动画的滚动视图

       UIView.animate(withDuration: Double(totalMidiTime) / 1000, delay: 0, options: .curveLinear) {
            self.scrollView.contentOffset.x = self.scrollView.contentSize.width - self.leftMargin
        } completion: { (_) in }

我想在动画时获取当前位置偏移量

您可以通过检查滚动视图的 presentation layer:

bounds.origin.x 值来获取偏移量
guard let pl = scrollView.layer.presentation() else {
    return
}
print("Content Offset X:", pl.bounds.origin.x)

这是一个完整的例子...

我们创建了一个包含 30 个标签的滚动视图。在 viewDidAppear 上,我们开始一个 20 秒的水平动画到最后一个标签。每次我们点击按钮时,“状态标签”将更新为滚动视图表示层的当前 bounds.origin.x(与 contentOffset.x 匹配):

class ViewController: UIViewController {

    let scrollView: UIScrollView = {
        let v = UIScrollView()
        v.translatesAutoresizingMaskIntoConstraints = false
        v.backgroundColor = .systemGreen
        return v
    }()
    
    let testButton: UIButton = {
        let v = UIButton()
        v.translatesAutoresizingMaskIntoConstraints = false
        v.backgroundColor = .blue
        v.setTitle("Get Offset", for: [])
        v.setTitleColor(.lightGray, for: .highlighted)
        return v
    }()
    
    let statusLabel: UILabel = {
        let v = UILabel()
        v.translatesAutoresizingMaskIntoConstraints = false
        v.numberOfLines = 0
        v.textAlignment = .center
        v.text = "Content Offset X\n0.00"
        return v
    }()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let stack = UIStackView()
        stack.distribution = .fillEqually
        stack.spacing = 8
        stack.translatesAutoresizingMaskIntoConstraints = false
        
        for i in 1...30 {
            let v = UILabel()
            v.backgroundColor = .yellow
            v.text = "Label \(i)"
            v.textAlignment = .center
            stack.addArrangedSubview(v)
        }
        
        scrollView.addSubview(stack)
        view.addSubview(scrollView)
        view.addSubview(testButton)
        view.addSubview(statusLabel)
        
        let g = view.safeAreaLayoutGuide
        let cg = scrollView.contentLayoutGuide
        let fg = scrollView.frameLayoutGuide
        
        NSLayoutConstraint.activate([
            
            scrollView.topAnchor.constraint(equalTo: g.topAnchor, constant: 20.0),
            scrollView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
            scrollView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
            scrollView.heightAnchor.constraint(equalToConstant: 60.0),
            
            stack.topAnchor.constraint(equalTo: cg.topAnchor, constant: 8.0),
            stack.leadingAnchor.constraint(equalTo: cg.leadingAnchor, constant: 8.0),
            stack.trailingAnchor.constraint(equalTo: cg.trailingAnchor, constant: -8.0),
            stack.bottomAnchor.constraint(equalTo: cg.bottomAnchor),

            stack.heightAnchor.constraint(equalTo: fg.heightAnchor, constant: -16.0),
            
            testButton.topAnchor.constraint(equalTo: scrollView.bottomAnchor, constant: 20.0),
            testButton.centerXAnchor.constraint(equalTo: g.centerXAnchor),
            testButton.widthAnchor.constraint(equalTo: g.widthAnchor, multiplier: 0.6),
            
            statusLabel.topAnchor.constraint(equalTo: testButton.bottomAnchor, constant: 20.0),
            statusLabel.centerXAnchor.constraint(equalTo: g.centerXAnchor),
            statusLabel.widthAnchor.constraint(equalTo: g.widthAnchor, multiplier: 0.9),
            
        ])
        
        testButton.addTarget(self, action: #selector(gotTap(_:)), for: .touchUpInside)
    }
    
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        
        UIView.animate(withDuration: 20.0, delay: 0, options: .curveLinear) {
            self.scrollView.contentOffset.x = self.scrollView.contentSize.width - self.scrollView.frame.width
        } completion: { (_) in }
        
    }
    
    @objc func gotTap(_ sender: UIButton) -> Void {
        guard let pl = scrollView.layer.presentation() else {
            return
        }
        let x = String(format: "%0.2f", pl.bounds.origin.x)
        statusLabel.text = "Content Offset X\n\(x)"
    }
    
}