Поки UIScrollView
(або його похідний клас) прокручується, здається, що всі NSTimers
запущені призупиняються, поки прокрутка не закінчиться.
Чи є спосіб обійти це? Нитки? Встановлення пріоритету? Що-небудь?
Поки UIScrollView
(або його похідний клас) прокручується, здається, що всі NSTimers
запущені призупиняються, поки прокрутка не закінчиться.
Чи є спосіб обійти це? Нитки? Встановлення пріоритету? Що-небудь?
Відповіді:
Просте і просте у реалізації рішення - це зробити:
NSTimer *timer = [NSTimer timerWithTimeInterval:...
target:...
selector:....
userInfo:...
repeats:...];
[[NSRunLoop mainRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
Для тих, хто використовує Swift 3
timer = Timer.scheduledTimer(timeInterval: 0.1,
target: self,
selector: aSelector,
userInfo: nil,
repeats: true)
RunLoop.main.add(timer, forMode: RunLoopMode.commonModes)
timer = Timer(timeInterval: 0.1, target: self, selector: aSelector, userInfo: nil, repeats: true)
як першу команду замість Timer.scheduleTimer()
, оскільки scheduleTimer()
додає таймер до циклу запуску, а наступний виклик - це ще одне додавання до того самого циклу запуску, але з іншим режимом. Не виконуйте двічі одну і ту ж роботу.
Вам потрібно запустити інший потік та інший цикл запуску, якщо ви хочете, щоб таймери спрацьовували під час прокрутки; оскільки таймери обробляються як частина циклу подій, якщо ви зайняті обробкою прокрутки вашого перегляду, ви ніколи не потрапите до таймерів. Хоча покарання за ефективність / заряд батареї, що працює на інших потоках, може не коштувати розгляду цієї справи.
для тих, хто використовує Swift 4:
timer = Timer(timeInterval: 1, target: self, selector: #selector(timerUpdated), userInfo: nil, repeats: true)
RunLoop.main.add(timer, forMode: .common)
tl; dr, runloop робить прокрутку, щоб не міг обробляти більше подій - якщо ви не встановите таймер вручну, щоб це також могло статися, коли runloop обробляє події дотику. Або спробуйте альтернативне рішення та скористайтеся GCD
Обов’язкове прочитання для будь-якого розробника iOS. Багато речей в кінцевому підсумку виконуються через RunLoop.
Походить від документів Apple .
Цикл запуску дуже схожий на його назву. Це цикл, який ваш потік вводить і використовує для запуску обробників подій у відповідь на вхідні події
Оскільки таймери та інші періодичні події доставляються під час запуску циклу запуску, обхід цього циклу порушує доставку цих подій. Типовий приклад такої поведінки трапляється кожного разу, коли ви впроваджуєте програму відстеження миші, вводячи цикл і неодноразово вимагаючи від програми подій. Оскільки ваш код захоплює події безпосередньо, а не дозволяє додатку нормально відправляти ці події, активні таймери не зможуть спрацьовувати, поки ваша програма відстеження миші не вийде і не поверне керування додатку.
Це трапляється БАГАТО ЧАСІВ, а ми ніколи цього не помічаємо. Я маю на увазі, що ми вмикаємо таймер на 10: 10: 10: 00, але пробіг виконує подію, яка триває до 10: 10: 10: 05, отже, таймер спрацьовує 10: 10: 10: 06
Подібним чином, якщо таймер спрацьовує, коли цикл запуску знаходиться посередині виконання процедури обробника, таймер чекає наступного разу через цикл запуску, щоб викликати свою процедуру обробника. Якщо цикл запуску взагалі не працює, таймер ніколи не спрацьовує.
Ви можете налаштувати таймери для генерації подій лише один раз або кілька разів. Таймер, що повторюється, перепрограмує себе автоматично на основі запланованого часу стрільби, а не фактичного часу стрільби. Наприклад, якщо таймер планується спрацьовувати в певний час і кожні 5 секунд після цього, запланований час стрільби завжди припадає на початкові інтервали часу в 5 секунд, навіть якщо фактичний час стрільби затримується. Якщо час стрільби затримується настільки, що він пропускає один або кілька запланованих часів стрільби, таймер спрацьовує лише один раз за пропущений період часу. Після стрільби на пропущений період таймер переноситься на наступний запланований час стрільби.
Ви не можете. ОС просто змінюється сама для вас. наприклад, коли користувач натискає, тоді режим перемикається на eventTracking
. Після закінчення натискань користувача режим повертається до default
. Якщо ви хочете, щоб щось було запущено в певному режимі, то це залежить від вас, щоб переконатися, що це станеться.
Коли користувач прокручує, режим Run Loop Mode стає tracking
. RunLoop призначений для перемикання передач. Після встановлення режиму значення eventTracking
надає пріоритет (пам’ятайте, у нас обмежене число ядер процесора) торкатися подій. Це архітектурний дизайн дизайнерів ОС .
За замовчуванням таймери НЕ плануються у tracking
режимі. Вони заплановані на:
Створює таймер і планує його на поточному циклі запуску в режимі за замовчуванням .
scheduledTimer
Під це робить:
RunLoop.main.add(timer, forMode: .default)
Якщо ви хочете, щоб ваш таймер працював під час прокрутки, ви повинні зробити одне:
let timer = Timer.scheduledTimer(timeInterval: 1.0, target: self,
selector: #selector(fireTimer), userInfo: nil, repeats: true) // sets it on `.default` mode
RunLoop.main.add(timer, forMode: .tracking) // AND Do this
Або просто зробіть:
RunLoop.main.add(timer, forMode: .common)
Зрештою, виконавши одне з вищезазначеного, ваш потік не буде заблокований подіями дотику. що еквівалентно:
RunLoop.main.add(timer, forMode: .default)
RunLoop.main.add(timer, forMode: .eventTracking)
RunLoop.main.add(timer, forMode: .modal) // This is more of a macOS thing for when you have a modal panel showing.
Ви можете розглянути можливість використання GCD для свого таймера, який допоможе вам "захистити" свій код від проблем із керуванням циклом запуску.
Для повторення просто використовуйте:
DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
// your code here
}
Для повторюваних таймерів використовуйте:
Подивіться, як використовувати DispatchSourceTimer
Копаючись глибше в дискусії, яку я мав із Даніелем Ялкутом:
Питання: як виконується GCD (фонові потоки), наприклад asyncAfter на фоновому потоці поза RunLoop? З цього я розумію, що все має виконуватися в межах RunLoop
Не обов'язково - кожен потік має щонайбільше один цикл запуску, але може мати нуль, якщо немає причин узгоджувати виконання "власності" потоку.
Потоки - це доступність рівня ОС, яка надає вашому процесу можливість розділити свої функціональні можливості на різні паралельні контексти виконання. Цикли запуску - це можливість на рівні фреймворку, яка дозволяє додатково розділити один потік, щоб він міг ефективно ділитися кількома шляхами коду.
Як правило, якщо ви відправляєте щось, що запускається в потоці, воно, ймовірно, не матиме циклу запуску, якщо не буде викликано щось, [NSRunLoop currentRunLoop]
що неявно створить його.
У двох словах, режими в основному є механізмом фільтрації входів та таймерів