在最接近滚动视图中心的视图数组中查找视图

Find view in array of views that is closest to the center of scrollview

如何在子视图数组中找到 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)
        }
    }
}

您可以使用 zip 或其他方式提高效率。但这是我想出的:

func centerMiddleView() {
    var closestDistance = CGFloat(9999)
    var closestView = UIView()
    
    let scrollViewCenterX = scrollView.bounds.width / 2
    
    for colorView in colorViews { /// loop over views
        if
            let colorView = colorView,
            let convertedCenter = colorView.superview?.convert(colorView.center, to: nil).x /// the view's center in terms of the screen
        {
            
            let distance = scrollViewCenterX - convertedCenter
            if abs(distance) < closestDistance { /// this is closer than the previous view
                closestDistance = distance
                closestView = colorView
            }
        }
    }
    
    let closestViewCenter = closestView.center.x
    let adjustedCenter = closestViewCenter - scrollViewCenterX
    
    let offsetPoint = CGPoint(x: adjustedCenter, y: 0) /// ignore the Y coordinate
    scrollView.setContentOffset(offsetPoint, animated: true)
}

你可以在用户手指抬起时调用它(当它们缓慢滚动时你需要两个函数来处理):

extension ViewController: UIScrollViewDelegate {
    func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
        centerMiddleView()
    }
    func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
        centerMiddleView()
    }
}

colorViews 是滚动视图中的视图数组。

@IBOutlet weak var orangeView: UIView!
@IBOutlet weak var cherryView: UIView!
@IBOutlet weak var purpleView: UIView!
@IBOutlet weak var blueView: UIView!
@IBOutlet weak var greenView: UIView!

lazy var colorViews = [orangeView, cherryView, purpleView, blueView, greenView]

@IBOutlet weak var scrollView: UIScrollView!

override func viewDidLoad() {
    super.viewDidLoad()
    
    scrollView.delegate = self /// don't forget to set delegate
    scrollView.contentInset = UIEdgeInsets(top: 0, left: 200, bottom: 0, right: 200) /// prevent glitch when centering an view at the edge
}

结果:

您可以通过获取滚动视图的“虚拟中心”找到“最靠近中心”的子视图——即.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 { [=12=].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()
        }
    }
}

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