当用户在启用分页的UIScrollView中更改页面时,是否有方法可以检测到或获取通知?
Answers:
实现UIScrollView的委托。此方法是您要寻找的。
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
使用它来检测当前正在显示哪个页面,并对页面更改执行一些操作:
- (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
是更好的解决方案,但不是一个完整的解决方案。我添加了自己的解决方案,完全为我解决了。
在启用分页的滚动视图中,您可以使用scrollViewDidEndDecelerating来了解何时将视图放置在页面上(可能是同一页面)。
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
scrollViewDidScroll
每次动作都会被调用。在启用分页的视图的上下文中,可以使用它来查看何时滚动足够的时间以移动到下一页(如果此时停止拖动)。
- (void)scrollViewWillBeginDecelerating:(UIScrollView *)scrollView
和的情况- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
?
如何结合两种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
}
}
Google在页面检测方面的第一个结果,因此我认为必须以更好的解决方案来回答。(即使这个问题是在两年半之前提出的。)
我不想打电话scrollViewDidScroll
只是为了跟踪页码。多数民众赞成在这样简单的事情的矫kill过正。使用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;
}
这样,用户一次只能滑动一页,而没有上述风险。
scrollViewWillBeginDecelerating
,您将了解到用户从第一次滑动就将手指抬起的毫秒级,您将到达该断点。
斯威夫特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)
}
这是快速的解决方案。
在要实现代码的类中,将两个属性设为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
}
}
}
要做到这一点并不容易:
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”。
在这样的示例中,您只需要记住,必须首先在任何此类调用函数中设置我们可能的QuantumPage值。没有真正通用的方法来处理开始情况。
毕竟,举例来说,您可能想要“快速滚动”到弹出页面,并且谁知道在QuantumPage情况下“意味着”什么。因此,请确保在启动时根据情况仔细初始化量子页面系统。
无论如何,只需将五个函数复制并粘贴到顶部即可获得完美的量子分页。
对于斯威夫特
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
}
}
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
}
}