Виявлення зміни сторінки UIScrollView


Відповіді:


17

Реалізуйте делегат UIScrollView. Цей метод - це те, що ви шукаєте.

- (void)scrollViewDidScroll:(UIScrollView *)scrollView

29
виявлення обміну сторінками у цьому методі є неефективним, оскільки код запускатиметься кожного разу, коли користувач проводить пальцем. Тому краще це робити в методі scrollViewDidEndDecelerating.
jianpx

2
@jianpx - У цього рішення є проблема: якщо користувач припинить прокручувати в кінці сторінки (це цілком можливо), ця функція не буде викликана!
Maciej Kozieł

@ MaciejKozieł Ви знайшли рішення цього питання?
Zorayr

@Zorayr Так, я перестав про це турбуватися;) Я можу придумати кілька альтернатив (і оригінальна відповідь, хоча і менш ефективна, є однією з них). Він мені знадобився для індикатора сторінки, і цей крайній випадок не є проблемою розбиття програми; він оновиться, як тільки відбудеться уповільнення.
Maciej Kozieł

@jianpx ти вирішив для мене дуже конкретну проблему цим коментарем. Дякую.
StoriKnow

193

Використовуйте це, щоб визначити, яка сторінка зараз відображається, і виконати певні дії щодо зміни сторінки:

- (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:методу.


4
Я знаю, що це старе, але, можливо, це допоможе, використовуючи цей метод, можна повернути від’ємні числа, а також сторінки, що виходять за межі прокрутки. Якщо ви використовуєте сторінку для доступу до масиву, це, очевидно, може спричинити проблеми, і тому на сторінці можуть знадобитися перевірки.
ScottPetit

2
писати вищезгаданий код всередині цього було б набагато краще ... - (void) scrollViewDidEndDecelerating: (UIScrollView *) scrollView
Nishant

1
Я не розумію, чому голосують за цю відповідь. Використання вищезазначеного буде викликано при кожному русі подання прокрутки. У цьому насправді немає потреби. Використання scrollViewDidEndDeceleratingє кращим рішенням, але не повним. Я додав власне рішення, яке вирішило його повністю для мене.
Сегев

1
Це залежить від того, коли вони хочуть реагувати на зміну сторінки. Якщо їм потрібно лише знати, коли прокрутка перестала прокручуватися, найкраще це робити після уповільнення. В іншому випадку найкраще використовувати метод прокрутки did.
Водоспад Майкл

@segev - scrollViewDidEndDecelerating не працює.
Fattie

65

У режимі прокрутки з увімкненим підкачуванням ви можете використовувати scrollViewDidEndDecelerating, щоб знати, коли подання осідає на сторінці (може бути на тій самій сторінці).

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView

scrollViewDidScrollвикликається при кожному русі. А в контексті перегляду сторінок з увімкненим сторінком можна використовувати, щоб знайти, коли воно прокручене досить, щоб перейти на наступну сторінку (якщо перетягування зупинено в цьому місці).


2
Це рішення має проблему: якщо користувач зупинить прокрутку точно в кінці сторінки (це цілком можливо), ця функція не буде викликана!
Maciej Kozieł

@ MaciejKozieł Кращим рішенням є інша моя відповідь
Сегев,

@Sagev Як це вирішити той випадок , коли - (void)scrollViewWillBeginDecelerating:(UIScrollView *)scrollViewі - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollViewне називається взагалі?
Maciej Kozieł

42

Як щодо поєднання двох методів 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
   }
}

Це не спрацює, якщо у вас активовані як вертикальні, так і горизонтальні жести.
Sumit Gera

24

Перший результат на Google для виявлення сторінок, тому мені довелося відповісти на це кращим рішенням, на мій погляд. (навіть якщо це питання було задано 2 з половиною роки тому.)

Я волів би не телефонувати, 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;
}

Таким чином, користувач може прокручувати лише одну сторінку за раз без ризику, описаного вище.


Ви абсолютно праві. Це хороше рішення для нескінченного прокручування.
fabrizotus

2
Якщо користувач намагається швидко гортати кілька сторінок, він не буде поводитися належним чином. Однак, хоча це не ідеально, для мене це швидке вирішення, поки у мене не буде більше часу, щоб присвятити кращому рішенню.
Pier-Luc Gendreau

@ Pier-LucGendreau Настільки швидко, наскільки проходять мої тести, такого сценарію не існує. Користувач не може двічі провести пальцем. Помістіть точку розриву всередину, scrollViewWillBeginDeceleratingі ви дізнаєтесь, що за секунду користувач піднімає палець від першого проведення, коли ви досягнете цієї точки розриву.
Сегев

2
Я знаю, але якщо ви знову проведете пальцем до того, як закінчиться перша анімація, цей другий рух буде втрачено.
Pier-Luc Gendreau

1
Дякую, добросердий пане, що ви взяли на себе відповідальність за вдосконалення старої відповіді.
TigerCoding

18

Стрімкий 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)


}

2
Це правильна відповідь, інші методи викликаються, навіть якщо scrollView недостатньо прокручується, щоб викликати зміну підкачки.
Ромуло Б.М.

2

Ось швидке рішення для цього.

Зробіть дві властивості currentPage і previousPage у класі, де ви реалізуєте свій код, та ініціалізуйте їх до 0.

Тепер оновіть currentPage з scrollViewDidEndDragging ( : willDecelerate :) і scrollViewDidEndDecelerating ( : scrollView :).

А потім оновіть previousPage у scrollViewDidEndScrollingAnimation (_: scrollView :)

    //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
           }
       }
   }

1

Виріжте та вставте на 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" під час виведення.

У такому прикладі вам просто потрібно пам’ятати, що ви повинні спочатку встановити наше можливе значенняQuantumPage у будь-яких таких функціях виведення; немає справді узагальненого способу впоратися з початковою ситуацією.

Врешті-решт, можливо, ви, наприклад, захочете "швидко прокрутити" сторінку виведення, і хто знає, що це "означає" у ситуації quantumPage. Тож обов’язково обережно ініціалізуйте свою систему квантових сторінок під час підведення, виходячи з вашої ситуації.

У будь-якому випадку просто скопіюйте та вставте п’ять функцій зверху, щоб отримати ідеальну квантову сторінку.


0

Для Свіфта

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
    }
}

0
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
    }
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.