当用户在启用分页的 UIScrollView 中更改页面时,有没有办法检测或获取通知?
10 回答
使用它来检测当前正在显示哪个页面并在页面更改时执行一些操作:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
static NSInteger previousPage = 0;
CGFloat pageWidth = scrollView.frame.size.width;
float fractionalPage = scrollView.contentOffset.x / pageWidth;
NSInteger page = lround(fractionalPage);
if (previousPage != page) {
// Page has changed, do your thing!
// ...
// Finally, update previous page
previousPage = page;
}
}
如果您可以在滚动完全停止后仅对页面更改做出反应,那么最好在scrollViewDidEndDecelerating:
委托方法而不是scrollViewDidScroll:
方法中执行上述操作。
在启用分页的滚动视图中,您可以使用scrollViewDidEndDecelerating来了解视图何时在页面上确定(可能是同一页面)。
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
scrollViewDidScroll
每个动作都会被调用。在启用分页的上下文中,可以使用视图来查找它何时滚动到足以移动到下一页(如果在该点停止拖动)。
结合 UIScrollViewDelegate 的两种方法怎么样?
在scrollViewDidEndDragging(_:willDecelerate:)
中,如果它立即停止,我们进行页面计算;如果它正在减速,我们让它走,它会被scrollViewDidEndDecelerating(_:)
.
代码使用 XCode 7.1.1 版、Swift 2.1 版测试
class ViewController: UIViewController, UIScrollViewDelegate {
// MARK: UIScrollViewDelegate
func scrollViewDidEndDragging(scrollView: UIScrollView, willDecelerate decelerate: Bool) {
if decelerate == false {
let currentPage = scrollView.currentPage
// Do something with your page update
print("scrollViewDidEndDragging: \(currentPage)")
}
}
func scrollViewDidEndDecelerating(scrollView: UIScrollView) {
let currentPage = scrollView.currentPage
// Do something with your page update
print("scrollViewDidEndDecelerating: \(currentPage)")
}
}
extension UIScrollView {
var currentPage: Int {
return Int((self.contentOffset.x+ (0.5*self.frame.size.width))/self.frame.width)+1
}
}
谷歌页面检测的第一个结果,所以我认为我必须用更好的解决方案来回答这个问题。(即使这个问题是在两年半前提出的。)
我不想打电话scrollViewDidScroll
只是为了跟踪页码。对于这么简单的事情来说,这太过分了。使用scrollViewDidEndDecelerating
确实有效并在页面更改时停止但是(这是一个很大的但是)如果用户在屏幕上滑动的速度比正常快两次,scrollViewDidEndDecelerating
则只会调用一次。您可以轻松地从第 1 页转到第 3 页,而无需处理第 2 页。
这完全为我解决了:
- (void)scrollViewWillBeginDecelerating:(UIScrollView *)scrollView
{
scrollView.userInteractionEnabled = NO;
}
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
//Run your code on the current page
scrollView.userInteractionEnabled = YES;
}
这样,用户一次只能滑动一页,而没有上述风险。
斯威夫特 4
我发现最好的方法是使用scrollViewWillEndDragging(_:withVelocity:targetContentOffset:)
. 它可以让您预测是否会在您将手指从屏幕上移开时立即发生分页。此示例用于水平分页。
请记住分配给scrollView.delegate
采用UIScrollViewDelegate
并实现此方法的对象。
var previousPageXOffset: CGFloat = 0.0
func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
let targetOffset = targetContentOffset.pointee
if targetOffset.x == previousPageXOffset {
// page will not change
} else if targetOffset.x < previousPageXOffset {
// scroll view will page left
} else if targetOffset.x > previousPageXOffset {
// scroll view will page right
}
previousPageXOffset = targetOffset.x
// If you want to track the index of the page you are on just just divide the previousPageXOffset by the scrollView width.
// let index = Int(previousPageXOffset / scrollView.frame.width)
}
实现 UIScrollView 的委托。这个方法就是你要找的。
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
这是解决此问题的快速解决方案。
在要实现代码的类中创建两个属性 currentPage 和 previousPage 并将它们初始化为 0。
现在从 scrollViewDidEndDragging( :willDecelerate:) 和 scrollViewDidEndDecelerating( :scrollView:) 更新 currentPage。
然后在 scrollViewDidEndScrollingAnimation(_:scrollView:) 中更新 previousPage
//Class Properties
var currentPage = 0
var previousPage = 0
func scrollViewDidEndDragging(scrollView: UIScrollView, willDecelerate decelerate: Bool) {
updatePage(scrollView)
return
}
func scrollViewDidEndDecelerating(scrollView: UIScrollView){
updatePage(scrollView)
return
}
func updatePage(scrollView: UIScrollView) {
let pageWidth:CGFloat = scrollView.frame.width
let current:CGFloat = floor((scrollView.contentOffset.x-pageWidth/2)/pageWidth)+1
currentPage = Int(current)
if currentPage == 0 {
// DO SOMETHING
}
else if currentPage == 1{
// DO SOMETHING
}
}
func scrollViewDidEndScrollingAnimation(scrollView: UIScrollView) {
if previousPage != currentPage {
previousPage = currentPage
if currentPage == 0 {
//DO SOMETHING
}else if currentPage == 1 {
// DO SOMETHING
}
}
}
2019 年剪切和粘贴
做到这一点并不容易:
var quantumPage: Int = -100 { // the UNIQUELY LANDED ON, NEVER REPEATING page
didSet {
print(">>>>>> QUANTUM PAGE IS \(quantumPage)")
pageHasActuallyChanged() // your function
}
}
private var possibleQuantumPage: Int = -100 {
didSet {
if oldValue != possibleQuantumPage {
quantumPage = possibleQuantumPage
}
}
}
public func scrollViewDidEndDragging(
_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
if decelerate == false {
possibleQuantumPage = currentPageEvenIfInBetween
}
}
public func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
possibleQuantumPage = currentPageEvenIfInBetween
}
var currentPageEvenIfInBetween: Int {
return Int((self.contentOffset.x + (0.5 * self.frame.width)) / self.frame.width)
}
完美运行。
pageHasActuallyChanged
仅当用户更改人类认为“更改页面”的页面时才会调用。
请注意:启动很棘手:
这在启动时很难初始化,并且取决于您如何使用分页系统。
在任何分页系统中,您很可能会有类似“scrollToViewAtIndex...”的内容
open func scrollToViewAtIndexForBringup(_ index: Int) {
if index > -1 && index < childViews.count {
let w = self.frame.size.width
let h = self.frame.size.height
let frame = CGRect(x: CGFloat(index)*w, y: 0, width: w, height: h)
scrollRectToVisible(frame, animated: false) // NOTE THE FALSE
// AND IMPORTANTLY:
possibleQuantumPage = currentPageEvenIfInBetween
}
}
因此,如果用户在第 17 页打开“书”,则在您的老板类中,您将调用该函数以在启动时将其设置为“17”。
在这样的示例中,您只需要记住,您必须在任何此类启动函数中初始设置我们的 possibleQuantumPage 值;没有真正通用的方法来处理起始情况。
毕竟,例如,您可能想要“快速滚动”到弹出页面,并且,谁知道在quantumPage 情况下这“意味着”什么。因此,请务必在启动期间根据您的情况仔细初始化您的量子页面系统。
无论如何,只需复制并粘贴顶部的五个函数即可获得完美的量子分页。
var scrollViewPage = 0
override func viewDidLoad() {
super.viewDidLoad()
scrollViewPage = scrollView.currentPage
}
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
if scrollViewPage != scrollView.currentPage {
scrollViewPage = scrollView.currentPage
// Do something with your page update
print("scrollViewDidEndDecelerating: \(scrollViewPage)")
}
}
并使用扩展
extension UIScrollView {
var currentPage: Int {
return Int((self.contentOffset.x + (0.5 * self.frame.size.width)) /
self.frame.width) + 1
}
}
对于斯威夫特
static var previousPage: Int = 0
func scrollViewDidScroll(_ scrollView: UIScrollView){
let pageWidth: CGFloat = scrollView.frame.width
let fractionalPage: CGFloat = scrollView.contentOffset.x / pageWidth
let page = lround(Double(fractionalPage))
if page != previousPage{
print(page)
// page changed
}
}