在最接近滚动视图中心的视图数组中查找视图
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()
}
}
}
请注意,除非您从第一个和最后一个子视图中添加了足够多的前导和尾随,否则您将无法将这两个视图居中。
如何在子视图数组中找到 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()
}
}
}
请注意,除非您从第一个和最后一个子视图中添加了足够多的前导和尾随,否则您将无法将这两个视图居中。