滚动视图动画时获取偏移量

问题描述

我有一个带动画的 scorll 视图

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

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

解决方法

您可以通过检查滚动视图的 presentation layerbounds.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,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,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,testButton.centerXAnchor.constraint(equalTo: g.centerXAnchor),testButton.widthAnchor.constraint(equalTo: g.widthAnchor,multiplier: 0.6),statusLabel.topAnchor.constraint(equalTo: testButton.bottomAnchor,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)"
    }
    
}