Як я можу програмно призупинити зупинку прокрутки в UIScrollView?


81

Примітка: Наведена тут відповідь для мене не працює.

У мене є UIScrollView (не перегляд таблиці, а лише спеціальна річ), і коли користувач робить певні дії, я хочу вбити будь-яку прокрутку (перетягування або уповільнення) всередині подання. Я спробував зробити, наприклад, це:

[scrollView scrollRectToVisible:CGRectInset([scrollView bounds], 10, 10) animated:NO];

на теорію, згідно з якою, якщо взяти прямокутник, який уже відомий, прокрутка просто зупиниться там, де є, але виявляється, що це не має ніякого ефекту - очевидно, вид прокрутки бачить, що даний прямокутник знаходиться в межах і приймає ніяких дій. Я можу змусити прокрутити зупинку, якщо я даю прямокутник, який, безумовно, знаходиться поза межами видимих ​​на даний момент, але всередині розміру вмісту подання. Здається, це зупиняє вигляд, як очікувалося ... але також змушує його перейти в інше місце. Я, мабуть, міг би трохи пограти на полях, щоб це працювало досить добре, але хтось знає чистий спосіб зупинити режим прокрутки, який робить свою справу?

Дякую.

Відповіді:


108

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

Узагальнене рішення для будь-якої дії прокрутки таке:

- (void)killScroll 
{
    CGPoint offset = scrollView.contentOffset;
    offset.x -= 1.0;
    offset.y -= 1.0;
    [scrollView setContentOffset:offset animated:NO];
    offset.x += 1.0;
    offset.y += 1.0;
    [scrollView setContentOffset:offset animated:NO];
}

[Редагувати] Станом на iOS 4.3 (і, можливо, і раніше) це також працює

- (void)killScroll 
{
    CGPoint offset = scrollView.contentOffset;
    [scrollView setContentOffset:offset animated:NO];
}

1
Девід: чудово, дякую. Я закрутився в цьому напрямку, але в підсумку отримав менш тривіальні розрахунки щодо вибору прямокутника 1х1 безпосередньо за межами прокрутки. Беручи вашу пропозицію щодо компенсації, а потім негайного відновлення (що відверто здається, щоб скористатися неопублікованою поведінкою, коли послідовні виклики в одному запуску події насправді працюють, хоча «результат» повинен бути неприйнятним), це працює нормально. Я збираюся відредагувати вашу відповідь вище, щоб включити узагальнене рішення, яке має працювати для будь-якого напрямку прокрутки. Дякую!
Бен Зотто,

(Сподіваюся, ви не заперечуєте редагування, хотіли роз’яснити для пізніших мандрівників. Дякую за вашу відповідь!)
Бен Зотто

виглядає чудово ... до речі, як ти повертаєшся туди, де був? Ви зберігаєте офсет перед тим, як його вбити, а потім повертаєтесь туди?
Ліор Френкель,

2
У iOS 7, коли scrollView isDecelerating, працює лише верхнє рішення (рішення + = 1, - = 1).
Sahil

1
Це рішення можна вдосконалити. Використовуйте + = 0,1 замість + = 1, тому setContentOffsetбуде достатньо одного дзвінка . Вид прокрутки автоматично зміщує зміщення вмісту.
kelin

91

Загальна відповідь - [scrollView setContentOffset:offset animated:NO]це не те саме , що [scrollView setContentOffset:offset]!

  • [scrollView setContentOffset:offset animated:NO] фактично зупиняє будь-яку запущену анімацію.
  • [scrollView setContentOffset:offset] не зупиняє жодної запущеної анімації.
  • Те саме для scrollView.contentOffset = offset: не зупиняє жодної запущеної анімації.

Це ніде не задокументовано, але така поведінка була перевірена на iOS 6.1 та iOS 7.1 - ймовірно, і раніше.

Отже, рішення для зупинки запущеної анімації / уповільнення таке просте:

[scrollView setContentOffset:scrollView.contentOffset animated:NO];

В основному те, що сказав Девід Лю у своїй відредагованій відповіді. Але я хотів пояснити, що ці два API НЕ однакові.

Свіфт 3:

scrollView.setContentOffset(scrollView.contentOffset, animated:false)

11
Молодці щодо пояснення "шукачам причин" серед нас ... (:
Авіель Гросс

3
Це, начебто, мене засмучує.
DCMaxxx

2
Я не розумію, чому ці речі не задокументовані ... urggggghh
user1105951

Ще цікавіше (принаймні) iOS 11.3 та 11.4: якщо contentOffset анімується, наприклад, (300, 0), тоді призначається "contentOffset = (300, 0)", поки анімація все ще працює, contentOffset може скинутись до (0, 0)! Це, мабуть, помилка!
січня

68

Для мене прийнята відповідь Девіда Луя для мене не спрацювала. Це те, що я закінчив робити:

- (void)killScroll {
    self.scrollView.scrollEnabled = NO;
    self.scrollView.scrollEnabled = YES;
}

Для чого я вартую, я використовую iOS 6.0 iPhone Simulator.


2
+1, як ви сказали, інший мені теж не підійшов, але це рішення спрацювало чудово!
brenjt

1
Чудово, це чудово працює, supportedInterfaceOrientationsщоб запобігти продовженню подій прокрутки, якщо користувач обертає пристрій під час прокрутки , що часом буває безлад у залежності від того, що ви робите.
cprcrack

21

Це те, що я роблю для своїх переглядів прокрутки та всіх інших пов’язаних підкласів:

- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset
 {
    *targetContentOffset = scrollView.contentOffset;
 }

Це встановлює targetContentOffsetдо scrollView«и струму зміщення, що робить прокрутку до зупинки , тому що він досяг мети. Насправді має сенс використовувати метод, метою якого є те, щоб користувачі могли встановити цільовий показник contentOffset.

Стрімкий

func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
    targetContentOffset.pointee = scrollView.contentOffset
}

це схоже на те, яке саме яблуко повинні використовувати розробники. тому що всі інші в якийсь момент призводять до помилок. я здивований, що це не прийнята відповідь.
OlDor

Для цього призначений цей метод делегатів. чудова відповідь.
Райан

На мій погляд, це найелегантніше рішення, як Apple говорить у своїй документації: "Ваша програма може змінити значення параметра targetContentOffset, щоб налаштувати, де прокручування закінчує свою анімацію прокрутки.". У мене
спрацювало

це правильна відповідь. Жодне з перерахованого не спрацювало для мене
2017 року,

20

Швидко зупиніть прокрутку :

scrollView.setContentOffset(scrollView.contentOffset, animated: false)

16

Власне ... Найбільш "сучасним" способом буде ->

scrollview.panGestureRecognizer.enabled = false;
scrollview.panGestureRecognizer.enabled = true;

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

Редагувати: Це насправді просто вбиває поточне перетягування користувача, але не відразу зупиняє уповільнення, якщо прокрутка перебуває в цьому стані. Для цього прийняті відповіді редагувати - це майже найкращий спосіб xD

[scrollview setContentOffset: scrollview.contentOffset animated:false];

Ну, я використовую це в моєму додатку, і він робить саме те, що вимагає OP. Чи можете ви дати трохи більше контексту? Що не працює чи чому?
Xatian

Ой, вибачте ... Здається, я знаю ... Я щойно відповів на ту частину запитання, яку шукав ... другу частину, яку я пропустив ... :-) -> відредаговано.
Xatian

Не зупиняє уповільнення.
Рудольф Адамкович

5

Найчистішим способом буде підклас UIScrollView та надання власного методу setContentOffset. Це повинно передати повідомлення, лише якщо ви не ввімкнули своє freezeлогічне властивість.

Подобається так:

BOOL freeze; // and the @property, @synthesize lines..

-(void)setContentOffset:(CGPoint)offset
{
    if ( !freeze ) [super setContentOffset:offset];
}

Потім, щоб заморозити:

scrollView.freeze = YES;

1
Дякую. Хоча це зупиняє видиме прокручування, насправді це не зупиняє внутрішнє уповільнення. Якщо ви ввімкнете це, а потім через деякий час знову вимкнете, ви побачите паузу прокрутки, а потім стрибніть вперед і продовжуйте гальмувати. Отже, внутрішній стан прокрутки все ще прокручується. Таким чином, вам потрібно завмерти, поки ви не знаєте, що уповільнення припинилося, що ви можете зробити у партнерстві з делегатом, але це мені здається трохи тупим. Я сподіваюся на щось, що фактично просто негайно припиняє уповільнення (наприклад, має ефект одного натискання на екран).
Ben Zotto

гаразд, подібні проблеми здаються знайомими з попереднього досвіду ... чому б не уповільнити уповільнення, встановивши режим decelerationRate = 1e10;заморожування == ТАК?
mvds

(не знаючи внутрішньої математики, 10 * UIScrollViewDecelerationRateFast може бути розумнішим вибором, ніж 1e10)
mvds

Цікава ідея! На жаль, це значення, схоже, зафіксоване. Випробував, і, здається, немає такого значення, яке призведе до негативного припинення уповільнення. Найкраще, що я можу зробити, це зробити це млявим. :)
Бен Зотто,

план Б полягає в тому, щоб відключити прокрутку на короткий період - можливо, ви зможете з цим уникнути, оскільки ви все одно блокуєте setContentOffset, сподіваючись, запобігаючи побічним ефектам від відключення прокрутки.
mvds


3

Вимкніть просто прокручування взаємодії з користувачем. (швидко)

scrollView.isScrollEnabled = false

Вимкнути під час анімації прокрутки після перетягування. (швидко)

var scrollingByVelocity = false

func scrollViewWillBeginDecelerating(_ scrollView: UIScrollView) {
    if !scrollingByVelocity {
        scrollView.setContentOffset(scrollView.contentOffset, animated: false)
    }
}

0

Я спробував ці методи в collectionview:

self.collectionView.collectionViewLayout.finalizeCollectionViewUpdates()


0

Це працює для мене в Swift 4.2 :

   func killScroll() {
    self.scrollView.isScrollEnabled = false;
    self.scrollView.isScrollEnabled = true;
}

... як продовження:

extension UIScrollView {
    func killScroll() {
        self.isScrollEnabled = false;
        self.isScrollEnabled = true;

    }
}

0

Я хотів відключити прокрутку лише тоді, коли певний UIView в режимі прокрутки є джерелом дотику під час проведення. Для переміщення UIView за межі UIScrollView знадобилося б досить багато рефакторингу, оскільки у нас була складна ієрархія подання.

Як обхідний шлях я додав до підвиду один UIPanGestureRecognizer, в якому я хотів запобігти прокрутці. Цей UIPanGestureRecognizer будеcancelsTouchesInView який заважає активізувати panGesture UIScrollView.

Це трохи «хак», але це надзвичайно легка зміна, і якщо ви використовуєте XIB або Storyboard, все, що вам потрібно зробити, - це перетягнути жест панорамування до відповідного підпрогляду.


0

SWift 5+

за допомогою Extension

extension UIScrollView  {
    
    func stopDecelerating() {
        let contentOffset = self.contentOffset
        self.setContentOffset(contentOffset, animated: false)
    }
}

використання

    myScrollView.stopDecelerating()
    // your stuff
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.