Як я можу програмно призупинити зупинку прокрутки в 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.