Gizmodo
Gizmodo

Reputation: 3202

UIScrollView – Adjust User Scroll Amount Akin to Scrubbing?

I have UIScrollview that the user scrolls up and down.

Is there a way to adjust how much the user's drag of the finger results in the final scroll amount?

I was looking at UIScrollview delegate methods, but haven't found a place to alter that.

scrollViewDidScroll(_:) seems too late since this is AFTER the event.

Upvotes: 0

Views: 53

Answers (1)

DonMag
DonMag

Reputation: 77690

iOS users are very familiar with using scrolling views, so changing the "scroll speed" may be confusing. However, it's your app :)

Give this a try...

When the user Begins dragging, we'll grab the .contentOffset.y as a "starting point." In scrollViewDidScroll, we'll get the difference between the new .contentOffset.y and the startingY ... multiply that by the speed factor ... and then change the .contentOffset.y.

Note that manually setting .contentOffset.y triggers scrollViewDidScroll, so we'll also need to set a bool flag to prevent recursion:

class SlowScrollVC: UIViewController, UIScrollViewDelegate {
    
    // scrollSpeed --- example values
    //  1.0 == normal
    //  1.5 == fast
    //  0.5 == slow
    var scrollSpeed: CGFloat = 0.5
    
    var startingOffsetY: CGFloat = 0
    var bManualOffset: Bool = false

    let scrollView = UIScrollView()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // add a bunch of labels and buttons so we have something to scroll
        let stack = UIStackView()
        stack.axis = .vertical
        stack.spacing = 40
        
        for i in 1...20 {
            let v = UILabel()
            v.text = "Label \(i)"
            v.textAlignment = .center
            v.backgroundColor = .cyan
            stack.addArrangedSubview(v)
            let b = UIButton()
            b.setTitle("Button \(i)", for: [])
            b.setTitleColor(.white, for: .normal)
            b.setTitleColor(.lightGray, for: .highlighted)
            b.backgroundColor = .systemBlue
            b.addTarget(self, action: #selector(btnTap(_:)), for: .touchUpInside)
            stack.addArrangedSubview(b)
        }
        
        stack.translatesAutoresizingMaskIntoConstraints = false
        scrollView.translatesAutoresizingMaskIntoConstraints = false
        
        scrollView.addSubview(stack)
        view.addSubview(scrollView)
        
        // so we can see the scroll view frame
        scrollView.backgroundColor = .yellow
        
        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.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: -20.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, constant: -8.0),
            
            stack.widthAnchor.constraint(equalTo: fg.widthAnchor, constant: -16.0),
            
        ])
        
        scrollView.delegate = self

        // you may also want to adjust .decelerationRate
        //  try various values to see the result
        //scrollView.decelerationRate = UIScrollView.DecelerationRate(rawValue: 0.99)

    }
    
    func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
        startingOffsetY = scrollView.contentOffset.y
    }
    func scrollViewDidScroll(_ scrollView: UIScrollView) {
        if !bManualOffset {
            // get the difference between previous offset.y and new offset.y
            let diff = scrollView.contentOffset.y - startingOffsetY
            // adjust by scroll-speed factor
            let newY = startingOffsetY + diff * scrollSpeed
            // prevent recursion
            bManualOffset = true
            // set adjusted offset.y
            scrollView.contentOffset.y = newY
            // update start Y
            startingOffsetY = newY
        }
        bManualOffset = false
    }
    
    @objc func btnTap(_ sender: UIButton) {
        // just to confirm we tapped a button
        print("Tap:", sender.currentTitle)
    }

}

Upvotes: 1

Related Questions