首页 > 解决方案 > 在最靠近滚动视图中心的视图数组中查找视图

问题描述

如何在 x 坐标最接近屏幕中心的子视图数组中找到视图?

这就是我所拥有的:

extension UIScrollView {
    func scrollToView(view:UIView, animated: Bool) {
        if let origin = view.superview {
            let childStartPoint = origin.convert(view.frame.origin, to: self)
            self.scrollRectToVisible(CGRect(x: childStartPoint.x, y: 0, width: 1, height: self.frame.height), animated: animated)
        }
    }
}

标签: iosswiftuiscrollview

解决方案


您可以通过获取滚动视图的“虚拟中心”来找到“最接近中心”的子视图——即.contentOffset.x框架宽度的加 1/2:

    // get the "virtual center" of the scroll view
    let scrollCenterX = scrollView.contentOffset.x + scrollView.frame.width * 0.5
    

然后,通过获取子视图中心和“虚拟中心”之间的最小差异来找到“中心”视图:

    // find the subview with center.x closest to scrollCenterX
    let closestToCenterView = viewsToTrack.min { a, b in abs(a.center.x - scrollCenterX) < abs(b.center.x - scrollCenterX) }
    

这是一个工作示例:

class CenterInScrollViewController: UIViewController {

    let scrollView: UIScrollView = {
        let v = UIScrollView()
        v.translatesAutoresizingMaskIntoConstraints = false
        v.backgroundColor = .systemYellow
        return v
    }()
    
    var viewsToTrack: [UIView] = []
    
    override func viewDidLoad() {
        super.viewDidLoad()

        view.addSubview(scrollView)
        
        let g = view.safeAreaLayoutGuide

        NSLayoutConstraint.activate([
            
            scrollView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
            scrollView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
            scrollView.heightAnchor.constraint(equalToConstant: 200.0),
            scrollView.centerYAnchor.constraint(equalTo: g.centerYAnchor),
            
        ])
        
        let cg = scrollView.contentLayoutGuide
        
        var prevView: UIView!
        
        for i in 1...10 {
            let v = UILabel()
            v.text = "\(i)"
            v.textAlignment = .center
            v.backgroundColor = .cyan
            v.layer.borderWidth = 1
            v.layer.borderColor = UIColor.red.cgColor
            v.translatesAutoresizingMaskIntoConstraints = false
            scrollView.addSubview(v)
            v.widthAnchor.constraint(equalToConstant: 120.0).isActive = true
            v.heightAnchor.constraint(equalTo: scrollView.frameLayoutGuide.heightAnchor).isActive = true
            if i == 1 {
                v.leadingAnchor.constraint(equalTo: cg.leadingAnchor, constant: 10.0).isActive = true
            } else {
                v.leadingAnchor.constraint(equalTo: prevView.trailingAnchor, constant: 40.0).isActive = true
            }
            if i == 10 {
                v.trailingAnchor.constraint(equalTo: cg.trailingAnchor, constant: -10.0).isActive = true
            }
            viewsToTrack.append(v)
            prevView = v
        }

        scrollView.delegate = self
        
    }

    func centerMiddleView() -> Void {
        // make sure we have views to find the centers
        guard viewsToTrack.count > 0 else {
            return
        }

        // reset view backgrounds to cyan
        viewsToTrack.forEach { $0.backgroundColor = .cyan }
        
        // get the "virtual center" of the scroll view
        let scrollCenterX = scrollView.contentOffset.x + scrollView.frame.width * 0.5
        
        // find the subview with center.x closest to scrollCenterX
        let closestToCenterView = viewsToTrack.min { a, b in abs(a.center.x - scrollCenterX) < abs(b.center.x - scrollCenterX) }
        
        // make sure we found one
        if let v = closestToCenterView {
            // set its background to yellow
            v.backgroundColor = .yellow
            // animate its center to the center of the scroll view via contentOffset
            UIView.animate(withDuration: 0.3, animations: {
                self.scrollView.contentOffset.x = v.center.x - self.scrollView.frame.width * 0.5
            })
        } else {
            print("No center view? This shouldn't happen...")
        }
    }
    
}

extension CenterInScrollViewController: UIScrollViewDelegate {
    func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
        centerMiddleView()
    }
    func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
        // let DidEndDecelerating handle positioning,
        //  unless Dragging ended while "holding in-place"
        if !decelerate {
            centerMiddleView()
        }
    }
}

请注意,除非您从第一个和最后一个子视图中添加了足够的前导和尾随,否则您将无法将这两个视图居中。


推荐阅读