如何获取 uiscrollView 位于 scrollIndicator 的元素

How to get the element of uiscrollView which is at scrollIndicator

我想在用户滚动时获取它在 scrollIndicator 中的元素。 因此,更清楚地说,当用户滚动时,我想获取正在拖动的元素(在 scoll 指示器处)。

我的滚动视图是水平的。

提前致谢

如果您想在用户开始拖动某物时获取位置,您可以通过以下方式实现 willBeginDragging

extension ViewController : UIScrollViewDelegate {
    func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
        let location = scrollView.panGestureRecognizer.location(in: scrollView)
        print(location)
    }
}

根据您的描述,您所有的 scrollView 子视图都将具有相同的大小,并且每个都是 scrollView 框架的大小。

使用该假设,基本思路是获取 scrollView 的 contentOffset,将其除以 scrollView 的宽度,然后将其转换为 Int 得到 "index" 显示其中的子视图。

因此,如果您的 scrollView 是 100 磅宽,则您添加的每个图像视图都将是 100 磅宽。如果用户已经滚动,所以 contentOffset20(向左滚动一点),索引将是:

    Int(20.0 / 100.0)  // equals 0

如果用户刚刚滚动到第三张图片的末尾,contentOffset 将为(例如)315,索引将为:

    Int(315.0 / 100.0)  // equals 3

(当然使用标准的基于零的索引)

这是一个 基本 示例 - 所有元素都添加到代码中,因此只需在 Storyboard 中使用空 ViewController 并将其分配给此 class:

class ScrollIndexViewController: UIViewController, UIScrollViewDelegate {

    var theScrollView: UIScrollView = {
        let v = UIScrollView()
        v.translatesAutoresizingMaskIntoConstraints = false
        return v
    }()

    // array of colors for imageView backgrounds
    // could be array of images (or image names), for example
    var colors: [UIColor] = [
        .red,
        .green,
        .blue,
        .orange,
        .magenta,
        .yellow,
        .brown,
        .cyan,
        .purple,
    ]

    override func viewDidLoad() {
        super.viewDidLoad()

        // add the scroll view
        view.addSubview(theScrollView)

        // constrain it to the full view
        NSLayoutConstraint.activate([
            theScrollView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 0.0),
            theScrollView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: 0.0),
            theScrollView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 0.0),
            theScrollView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: 0.0),
            ])

        // set scroll view delegate to self
        theScrollView.delegate = self

        // var to use when constraining the scrollable subviews
        var prevImageView: UIImageView?

        // for each element in the array
        for cl in colors {

            // create a new image view
            let v = UIImageView()
            v.translatesAutoresizingMaskIntoConstraints = false
            v.backgroundColor = cl

            // add it to the scroll view
            theScrollView.addSubview(v)

            // all added image views get
            //      width = scroll view width
            //      height = scroll view height
            //      top = scroll view top
            NSLayoutConstraint.activate([
                v.widthAnchor.constraint(equalTo: theScrollView.widthAnchor, multiplier: 1.0),
                v.heightAnchor.constraint(equalTo: theScrollView.heightAnchor, multiplier: 1.0),
                v.topAnchor.constraint(equalTo: theScrollView.topAnchor, constant: 0.0),
                ])

            // if this is the first added image view,
            // constrain leading to scroll view leading
            if cl == colors.first {
                NSLayoutConstraint.activate([
                    v.leadingAnchor.constraint(equalTo: theScrollView.leadingAnchor, constant: 0.0),
                    ])
            }

            // if this is the last added image view,
            // constrain trailing to scroll view trailing
            if cl == colors.last {
                NSLayoutConstraint.activate([
                    v.trailingAnchor.constraint(equalTo: theScrollView.trailingAnchor, constant: 0.0),
                    ])
            }

            // if prevImageView is not nil, that means we've added
            // an image view before this one, so,
            // constrain leading to trailing of previous image view
            if let pv = prevImageView {
                NSLayoutConstraint.activate([
                    v.leadingAnchor.constraint(equalTo: pv.trailingAnchor, constant: 0.0),
                    ])
            }

            // set prevImageView to this image view
            prevImageView = v

        }

    }

    func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {

        // if user scrolled by swiping

        // calculate the "index" of the visible image view
        let idx = getImageIndex(scrollView)
        // do what you want with the index
        print(#function, "Index: \(idx)")

    }

    func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {

        // if user scrolls and stops (i.e. doesn't swipe and lift)
        if !decelerate {
            // calculate the "index" of the visible image view
            let idx = getImageIndex(scrollView)
            // do what you want with the index
            print(#function, "Index: \(idx)")
        }

    }

    func getImageIndex(_ scrollView: UIScrollView) -> Int {

        // get the contentOffset
        let offX = scrollView.contentOffset.x

        // get the width of the scroll view
        // (which is also the width of the image view subviews)
        let svW = scrollView.frame.width

        // return offset.x / image view width as an Int
        return Int(offX / svW)

    }

}

请记住 --- 如果用户向左滚动直到在左边缘只能看到图像的单个像素宽度,该图像 仍将被视为 "visible image view"。

您可能希望将索引计算更改为 return "most visible" 子视图,您可以这样做:

    return Int((offX / svW).rounded())

或者可能仅当下一个图像视图至少有 75% 可见时...满足您的需要。