【问题标题】:Find view in array of views that is closest to the center of scrollview在最靠近滚动视图中心的视图数组中查找视图
【发布时间】:2021-05-10 08:39:44
【问题描述】:

如何在 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)
        }
    }
}

【问题讨论】:

  • 所以你想要一个“捕捉”效果,让最靠近中间的视图“捕捉”到中间?
  • 是的,没错

标签: ios swift uiscrollview


【解决方案1】:

您可以使用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
}

结果:

【讨论】:

    【解决方案2】:

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-02-24
      • 1970-01-01
      相关资源
      最近更新 更多