以编程方式将 UIScrollView 滚动到 Swift 中的子 UIView(子视图)的顶部
Posted
技术标签:
【中文标题】以编程方式将 UIScrollView 滚动到 Swift 中的子 UIView(子视图)的顶部【英文标题】:Programmatically scroll a UIScrollView to the top of a child UIView (subview) in Swift 【发布时间】:2016-08-18 12:16:55 【问题描述】:我的 UIScrollView 中有一些屏幕内容,它们只能垂直滚动。
我想以编程方式滚动到包含在其层次结构中某处的视图。
UIScrollView 移动,以便子视图位于 UIScrollView 的顶部(动画与否)
【问题讨论】:
【参考方案1】:这是我最后写的一个扩展。
用法:
从我的 viewController 调用,self.scrollView 是 UIScrollView 的出口,self.cmetsHeader 是其中的一个视图,靠近底部:
self.scrollView.scrollToView(self.commentsHeader, animated: true)
代码:
您只需要 scrollToView 方法,但也保留 scrollToBottom / scrollToTop 方法,因为您可能也需要这些方法,但感觉可以随意删除。
extension UIScrollView
// Scroll to a specific view so that it's top is at the top our scrollview
func scrollToView(view:UIView, animated: Bool)
if let origin = view.superview
// Get the Y position of your child view
let childStartPoint = origin.convertPoint(view.frame.origin, toView: self)
// Scroll to a rectangle starting at the Y of your subview, with a height of the scrollview
self.scrollRectToVisible(CGRect(x:0, y:childStartPoint.y,width: 1,height: self.frame.height), animated: animated)
// Bonus: Scroll to top
func scrollToTop(animated: Bool)
let topOffset = CGPoint(x: 0, y: -contentInset.top)
setContentOffset(topOffset, animated: animated)
// Bonus: Scroll to bottom
func scrollToBottom()
let bottomOffset = CGPoint(x: 0, y: contentSize.height - bounds.size.height + contentInset.bottom)
if(bottomOffset.y > 0)
setContentOffset(bottomOffset, animated: true)
【讨论】:
如果键盘在屏幕上,这对我来说效果不佳。而不是使用 scrollRectToVisible 我正在使用: setContentOffset(CGPoint(x: 0, y: childStartPoint.y), animated: animated) self.scrollRectToVisible(CGRect(x: 0, y: childStartPoint.y, width: 1, height: view.frame.height), animated: animated) 为我工作。使用的视图高度 在使用内容插入时考虑将height: self.frame.height
替换为height: frame.height - (contentInset.top + contentInset.bottom)
。
@Nati 当键盘出现在屏幕上时,您需要确保 UIScrollView 的 contentInset 属性设置正确以考虑键盘下方的区域。
将let childStartPoint = origin.convertPoint(view.frame.origin, toView: self)
替换为let childStartPoint = origin.convert(view.frame.origin, to: self)
解决了我的问题。【参考方案2】:
scrollView.scrollRectToVisible(CGRect(x: x, y: y, width: 1, height:
1), animated: true)
或
scrollView.setContentOffset(CGPoint(x: x, y: y), animated: true)
另一种方法是
scrollView.contentOffset = CGPointMake(x,y);
我用这样的动画来做
[UIView animateWithDuration:2.0f delay:0 options:UIViewAnimationOptionCurveLinear animations:^
scrollView.contentOffset = CGPointMake(x, y);
completion:NULL];
【讨论】:
【参考方案3】:scrollView.setContentOffset(CGPoint, animated: Bool)
该点的 y 坐标是您要显示的视图框架的 y 坐标,相对于滚动视图的内容视图。
【讨论】:
【参考方案4】:这是我的答案,这很快。这将无限滚动滚动视图中的页面。
private func startBannerSlideShow()
UIView.animate(withDuration: 6, delay: 0.1, options: .allowUserInteraction, animations:
scrollviewOutlt.contentOffset.x = (scrollviewOutlt.contentOffset.x == scrollviewOutlt.bounds.width*2) ? 0 : scrollviewOutlt.contentOffset.x+scrollviewOutlt.bounds.width
, completion: (status) in
self.startBannerSlideShow()
)
【讨论】:
【参考方案5】:更新了dyson's answer,使其行为类似于UITableView
的scrollToRowAtIndexPath:atScrollPosition:animated:
,因为那是我的用例:
extension UIScrollView
/// Scrolls to a subview of the current `UIScrollView `.
/// - Parameters:
/// - view: The subview to which it should scroll to.
/// - position: A constant that identifies a relative position in the `UIScrollView ` (top, middle, bottom) for the subview when scrolling concludes. See UITableViewScrollPosition for descriptions of valid constants.
/// - animated: `true` if you want to animate the change in position; `false` if it should be immediate.
func scrollToView(view: UIView,
position: UITableView.ScrollPosition = .top,
animated: Bool)
// Position 'None' should not scroll view to top if visible like in UITableView
if position == .none &&
bounds.intersects(view.frame)
return
if let origin = view.superview
// Get the subview's start point relative to the current UIScrollView
let childStartPoint = origin.convert(view.frame.origin,
to: self)
var scrollPointY: CGFloat
switch position
case .bottom:
let childEndY = childStartPoint.y + view.frame.height
scrollPointY = CGFloat.maximum(childEndY - frame.size.height, 0)
case .middle:
let childCenterY = childStartPoint.y + view.frame.height / 2.0
let scrollViewCenterY = frame.size.height / 2.0
scrollPointY = CGFloat.maximum(childCenterY - scrollViewCenterY, 0)
default:
// Scroll to top
scrollPointY = childStartPoint.y
// Scroll to the calculated Y point
scrollRectToVisible(CGRect(x: 0,
y: scrollPointY,
width: 1,
height: frame.height),
animated: animated)
/// Scrolls to the top of the current `UIScrollView`.
/// - Parameter animated: `true` if you want to animate the change in position; `false` if it should be immediate.
func scrollToTop(animated: Bool)
let topOffset = CGPoint(x: 0, y: -contentInset.top)
setContentOffset(topOffset, animated: animated)
/// Scrolls to the bottom of the current `UIScrollView`.
/// - Parameter animated: `true` if you want to animate the change in position; `false` if it should be immediate.
func scrollToBottom(animated: Bool)
let bottomOffset = CGPoint(x: 0,
y: contentSize.height - bounds.size.height + contentInset.bottom)
if (bottomOffset.y > 0)
setContentOffset(bottomOffset, animated: animated)
【讨论】:
【参考方案6】:对我来说,导航栏与滚动视图内容的一小部分重叠。所以我做了两件事:
尺寸检查器 - 滚动视图 - 内容插入 --> 从自动更改为从不。 Size Inspector - Constraints- "Align Top to" (Top Alignment Constraints)- 第二项 --> 从 Superview.Top 更改为 Safe Area.Top 和 value(constant field ) 设置为 0
【讨论】:
这不能回答问题【参考方案7】:swift 5.0 代码
extension UIScrollView
// Scroll to a specific view so that it's top is at the top our scrollview
func scrollToView(view:UIView, animated: Bool)
if let origin = view.superview
// Get the Y position of your child view
let childStartPoint = origin.convert(view.frame.origin, to: self)
// Scroll to a rectangle starting at the Y of your subview, with a height of the scrollview
self.scrollRectToVisible(CGRect(x:0, y:childStartPoint.y,width: 1,height: self.frame.height), animated: animated)
// Bonus: Scroll to top
func scrollToTop(animated: Bool)
let topOffset = CGPoint(x: 0, y: -contentInset.top)
setContentOffset(topOffset, animated: animated)
// Bonus: Scroll to bottom
func scrollToBottom()
let bottomOffset = CGPoint(x: 0, y: contentSize.height - bounds.size.height + contentInset.bottom)
if(bottomOffset.y > 0)
setContentOffset(bottomOffset, animated: true)
【讨论】:
【参考方案8】:用于在动画完成时滚动到顶部或底部
// MARK: - UIScrollView extensions
extension UIScrollView
/// Animate scroll to bottom with completion
///
/// - Parameters:
/// - duration: TimeInterval
/// - completion: Completion block
func animateScrollToBottom(withDuration duration: TimeInterval,
completion: (()->())? = nil)
UIView.animate(withDuration: duration, animations: [weak self] in
self?.setContentOffset(CGPoint.zero, animated: false)
, completion: finish in
if finish completion?()
)
/// Animate scroll to top with completion
///
/// - Parameters:
/// - duration: TimeInterval
/// - completion: Completion block
func animateScrollToBottomTop(withDuration duration: TimeInterval,
completion: (()->())? = nil)
UIView.animate(withDuration: duration, animations: [weak self] in
guard let `self` = self else
return
let desiredOffset = CGPoint(x: 0, y: -self.contentInset.top)
self.setContentOffset(desiredOffset, animated: false)
, completion: finish in
if finish completion?()
)
【讨论】:
【参考方案9】:对我来说scrollRectToVisible()
不起作用(请参阅here),所以我使用setContentOffset()
并根据 AMAN77 的回答自己计算:
extension UIScrollView
func scrollToView(view:UIView, animated: Bool)
if let superview = view.superview
let child = superview.convert(view.frame, to: self)
let visible = CGRect(origin: contentOffset, size: visibleSize)
let newOffsetY = child.minY < visible.minY ? child.minY : child.maxY > visible.maxY ? child.maxY - visible.height : nil
if let y = newOffsetY
setContentOffset(CGPoint(x:0, y: y), animated: animated)
这是一个水平滚动视图,但同样的想法也可以垂直应用。
【讨论】:
【参考方案10】:重要的是要向你们中的任何初学者指出,您需要将故事板中的 UIScrollView 链接到您的代码中,然后使用扩展名“.nameoffunction”
例如:
您将 UIScrollView 导入您的代码并将其命名为 bob。
你有一个像上面那样由“dyson returns”编写的扩展脚本
你现在写你的代码:
“bob.scrollToTop”
这将扩展功能“scrollToTop”附加到情节提要中的 UIScrollView。
祝你好运,振作起来!
【讨论】:
【参考方案11】:您可以使用以下方法,它对我很有效
func scrollViewToTop( _ someView:UIView)
let targetViewTop = someView.frame.origin.y
//If you have a complicated hierarchy it is better to
// use someView superview (someView.superview?.frame.origin.y) and figure out your view origin
let viewToTop = targetViewTop - scrollView.contentInset.top
scrollView.setContentOffset(CGPoint(x: 0, y: viewToTop), animated: true)
或者你可以有一个扩展
extension UIScrollView
func scrollViewToTop( _ someView:UIView)
let targetViewTop = someView.frame.origin.y
let viewToTop = targetViewTop - self.contentInset.top
self.setContentOffset(CGPoint(x: 0, y: viewToTop), animated: true)
这里是滚动视图的约束
一些屏幕截图
截图2
【讨论】:
以上是关于以编程方式将 UIScrollView 滚动到 Swift 中的子 UIView(子视图)的顶部的主要内容,如果未能解决你的问题,请参考以下文章
以编程方式将触摸传递给 UIScrollView 以进行滚动
如何以编程方式设置 UIScrollView 以使用 autoLayout 水平滚动