Я реализую полосу здоровья, которая анимируется с помощью пользовательского ввода.
Эти анимации заставляют его подниматься или опускаться на определенную величину (скажем, на 50 единиц) и являются результатом нажатия кнопки. Есть две кнопки. Увеличение и уменьшение.
Я хочу выполнить блокировку на панели здоровья, чтобы только один поток мог изменить ее за раз. Проблема в том, что я получаю тупик.
Я предполагаю, что отдельный поток запускает блокировку, удерживаемую другим потоком. Но этот замок сломается, когда анимация завершится. Как реализовать блокировку, которая заканчивается после завершения [UIView AnimateWithDuration]?
Интересно, подходит ли NSConditionLock
, но я хочу использовать NSLocks, если это возможно, чтобы избежать ненужной сложности. Что вы порекомендуете?
(В конце концов я хочу, чтобы анимации «стояли в очереди», позволяя продолжить ввод данных пользователем, но сейчас я просто хочу, чтобы блокировка работала, даже если она сначала блокирует ввод.)
(Хм, если подумать, что одновременно работает только один [UIView AnimateWithDuration] для одного и того же UIView. Второй вызов прерывает первый, вызывая немедленный запуск обработчика завершения для первого. Может быть, вторая блокировка запускается до того, как сначала есть шанс разблокировать. Как лучше всего справиться с блокировкой в этом случае? Возможно, мне следует вернуться к Grand Central Dispatch, но я хотел посмотреть, есть ли более простой способ.)
В ViewController.h я объявляю:
NSLock *_lock;
В ViewController.m у меня есть:
В loadView:
_lock = [[NSLock alloc] init];
Остальная часть ViewController.m (соответствующие части):
-(void)tryTheLockWithStr:(NSString *)str
{
LLog(@"\n");
LLog(@" tryTheLock %@..", str);
if ([_lock tryLock] == NO)
{
NSLog(@"LOCKED.");
}
else
{
NSLog(@"free.");
[_lock unlock];
}
}
// TOUCH DECREASE BUTTON
-(void)touchThreadButton1
{
LLog(@" touchThreadButton1..");
[self tryTheLockWithStr:@"beforeLock"];
[_lock lock];
[self tryTheLockWithStr:@"afterLock"];
int changeAmtInt = ((-1) * FILLBAR_CHANGE_AMT);
[self updateFillBar1Value:changeAmtInt];
[UIView animateWithDuration:1.0
delay:0.0
options:(UIViewAnimationOptionTransitionNone|UIViewAnimationOptionBeginFromCurrentState|UIViewAnimationOptionAllowUserInteraction)
animations:
^{
LLog(@" BEGIN animationBlock - val: %d", self.fillBar1Value)
self.fillBar1.frame = CGRectMake(FILLBAR_1_X_ORIGIN,FILLBAR_1_Y_ORIGIN, self.fillBar1Value,30);
}
completion:^(BOOL finished)
{
LLog(@" END animationBlock - val: %d - finished: %@", self.fillBar1Value, (finished ? @"YES" : @"NO"));
[self tryTheLockWithStr:@"beforeUnlock"];
[_lock unlock];
[self tryTheLockWithStr:@"afterUnlock"];
}
];
}
-(void)updateFillBar1Value:(int)changeAmt
{
self.prevFillBar1Value = self.fillBar1Value;
self.fillBar1Value += changeAmt;
if (self.fillBar1Value < FILLBAR_MIN_VALUE)
{
self.fillBar1Value = FILLBAR_MIN_VALUE;
}
else if (self.fillBar1Value > FILLBAR_MAX_VALUE)
{
self.fillBar1Value = FILLBAR_MAX_VALUE;
}
}
Вывод:
Чтобы воспроизвести инструкции: один раз нажмите "Уменьшить"
touchThreadButton1..
попробуйте TheLock перед блокировкой.. бесплатно.
tryTheLock afterLock.. ЗАБЛОКИРОВАНО. BEGIN animationBlock - значение: 250 END animationBlock - значение: 250 - завершено: YES
попробуйте TheLock перед разблокировкой.. ЗАБЛОКИРОВАНО.
попробуйте TheLock после разблокировки.. бесплатно.
Вывод. Это работает так, как ожидалось.
--
Вывод:
Инструкции по воспроизведению: дважды быстро нажмите "Уменьшить" (прервав начальную анимацию).
touchThreadButton1..
попробуйте TheLock перед блокировкой.. бесплатно.
tryTheLock afterLock.. ЗАБЛОКИРОВАНО. BEGIN animationBlock - val: 250 touchThreadButton1..
tryTheLock перед блокировкой.. ЗАБЛОКИРОВАНО. * -[Блокировка NSLock]: взаимоблокировка ('(null)') * Прервать _NSLockError() для отладки.
Заключение. Ошибка тупика. Пользовательский ввод заморожен.