Кто-нибудь еще заметил, что проблема с исправлениями не исправляется должным образом. Я только что добавил некоторую начальную поддержку для своего приложения, но заметил, что они не отображали то, что я ожидал от них. Ради примера, и для удобства тестирования этого вопроса быстро я создать графикApple Clock ClockKit Осложнения не обновляют свои записи в хронологическом порядке, если циферблат не скрыт во время выполнения.
A -> B -> C
0s 10s 20s
Но все, что я хотел видеть, был осложнение записи в пребывании около мимо времени, которые должны отображаться B и C.
Мое обычное приложение не настроено на создание регулярных интервалов, подобных этому, у него есть много аспектов таймеров, которые он предоставляет, которые могут быть установлены пользователем, но один из таких аспектов позволяет пользователю запускать несколько таймеров при один раз, который будет завершен после того, как пользователь определит длительность, которую они выберут. В отличие от таймера приложения часов iOS, вы можете указать длительность таймера в секундах, и поэтому вполне возможно, что 2 таймера будут заканчиваться в течение нескольких секунд друг от друга, в целом, хотя более вероятно, что они будут на несколько минут друг от друга. Также не должно быть добавлено слишком много записей осложнений, хотя другие более сложные аспекты моего приложения могут легко добавить 10 или даже 100 записей сложности в зависимости от того, насколько сложна задача, которую пользователь установил в ней. Пока же этот простой пример легче обсуждать и тестировать.
Я обновил Xcode до последней версии (7.3.2) без каких-либо улучшений и отправил сборку на мой реальный телефон и посмотрел и снова не улучшился. До тех пор он действительно работал. При дальнейшей отладке я обнаружил, что могу сделать шкалу времени самой, просто опустив часы (чтобы отключить экран), а затем снова разбудить ее, пока она была в середине, выполняя мою временную шкалу. Сделав это, временная шкала затем будет работать должным образом с тех пор.
Я создал тестовое приложение, чтобы продемонстрировать проблему, которая полностью воспроизводит проблему, поэтому я отправлю ее на яблоко в отчете об ошибке. Просто подумал, что посмотрю, заметил ли кто-нибудь еще этот вопрос.
Кроме того, когда мой тест приложение выполняет я получаю следующий вывод протоколирования с ошибкой, что не имеет смысл
-[ExtensionDelegate session:didReceiveUserInfo:]:67 - complication.family=1 in activeComplications - calling reloadTimelineForComplication
-[ComplicationController getTimelineStartDateForComplication:withHandler:]:43 - calling handler for startDate=2016-06-15 22:08:26 +0000
-[ComplicationController getTimelineEndDateForComplication:withHandler:]:73 - calling handler for endDate=2016-06-15 22:08:46 +0000
-[ComplicationController getCurrentTimelineEntryForComplication:withHandler:]:148 - calling handler for entry at date=2016-06-15 22:08:26 +0000
-[ComplicationController getTimelineEntriesForComplication:afterDate:limit:withHandler:]:202 - adding entry at date=2016-06-15 22:08:36 +0000; with timerEndDate=2016-06-15 22:08:46 +0000 i=1
getTimelineEntriesForComplication:afterDate:limit:withHandler: -- invalid entries returned. (1 entries before start date 2016-06-15 22:08:46 +0000). Excess entries will be discarded.
Соответствующую информацию из этого журнала следующего
getTimelineStartDateForComplication - calling handler for startDate=22:08:26
getTimelineEndDateForComplication - calling handler for endDate=22:08:46
getCurrentTimelineEntryForComplication - calling handler for entry at date=22:08:26
getTimelineEntriesForComplication:afterDate - adding entry at date=22:08:36
getTimelineEntriesForComplication:afterDate:limit:withHandler: -- invalid entries returned. (1 entries before start date 22:08:46). Excess entries will be discarded.
Что вас может видеть в ошибке из системы в конце, что она использует дату начала 22:08:46, что было на самом деле тем, что я сказал Clockkit, был моей конечной датой окончания, NOT the startDate. Я не уверен, связано ли это с поведением, которое я вижу, когда вижу ту же ошибку, когда она работает после того, как я скрываю/показываю экран.
Я разместил видео этого поведения в своем тестовом приложении онлайн here. Подробности этого тестового приложения следующие:
Полный код, который должен запускаться только в соответствующих тренажерах, доступен here, соответствующие ссылочные модули также перечислены здесь для справки.
В моем удлинительном делегате, я получаю USERINFO из приложения IOS и график перезагрузки моих осложнения сроков
ExtensionDelegate.м
- (void)session:(WCSession *)session didReceiveUserInfo:(NSDictionary<NSString *, id> *)userInfo
{
DbgLog(@"");
WKExtension *extension = [WKExtension sharedExtension];
DbgLog(@"self=%p; wkExtension=%p; userInfo=%@", self, extension, userInfo);
self.lastReceivedUserInfo = userInfo;
CLKComplicationServer *complicationServer = [CLKComplicationServer sharedInstance];
for (CLKComplication *complication in complicationServer.activeComplications)
{
DbgLog(@"complication.family=%d in activeComplications - calling reloadTimelineForComplication", complication.family);
[complicationServer reloadTimelineForComplication:complication];
}
}
Тогда в моем ComplicationController следующие методы для обработки Усложнение сторону вещей
ComplicationController.m
#define DbgLog(fmt, ...) NSLog((@"%s:%d - " fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__)
@interface ComplicationController()
@end
@implementation ComplicationController
#pragma mark - Timeline Configuration
- (void)getSupportedTimeTravelDirectionsForComplication:(CLKComplication *)complication withHandler:(void(^)(CLKComplicationTimeTravelDirections directions))handler
{
handler(CLKComplicationTimeTravelDirectionForward|CLKComplicationTimeTravelDirectionBackward);
}
- (void)getTimelineStartDateForComplication:(CLKComplication *)complication withHandler:(void(^)(NSDate * __nullable date))handler
{
NSDate *startDate;
WKExtension *extension = [WKExtension sharedExtension];
assert(extension.delegate);
assert([extension.delegate isKindOfClass:[ExtensionDelegate class]]);
ExtensionDelegate *extensionDelegate = (ExtensionDelegate *)extension.delegate;
if (extensionDelegate.lastReceivedUserInfo)
{
NSDictionary *userInfo = extensionDelegate.lastReceivedUserInfo;
startDate = [userInfo objectForKey:@"date"];
}
DbgLog(@"calling handler for startDate=%@", startDate);
handler(startDate);
}
- (NSDate*)getTimelineEndDate
{
NSDate *endDate;
WKExtension *extension = [WKExtension sharedExtension];
assert(extension.delegate);
assert([extension.delegate isKindOfClass:[ExtensionDelegate class]]);
ExtensionDelegate *extensionDelegate = (ExtensionDelegate *)extension.delegate;
if (extensionDelegate.lastReceivedUserInfo)
{
NSDictionary *userInfo = extensionDelegate.lastReceivedUserInfo;
NSDate *startDate = [userInfo objectForKey:@"date"];
NSNumber *duration = [userInfo objectForKey:@"duration"];
NSNumber *count = [userInfo objectForKey:@"count"];
NSTimeInterval totalDuration = duration.floatValue * count.floatValue;
endDate = [startDate dateByAddingTimeInterval:totalDuration];
}
return endDate;
}
- (void)getTimelineEndDateForComplication:(CLKComplication *)complication withHandler:(void(^)(NSDate * __nullable date))handler
{
NSDate *endDate=[self getTimelineEndDate];
DbgLog(@"calling handler for endDate=%@", endDate);
handler(endDate);
}
- (void)getPrivacyBehaviorForComplication:(CLKComplication *)complication withHandler:(void(^)(CLKComplicationPrivacyBehavior privacyBehavior))handler {
handler(CLKComplicationPrivacyBehaviorShowOnLockScreen);
}
#pragma mark - Timeline Population
- (CLKComplicationTemplate *)getComplicationTemplateForComplication:(CLKComplication *)complication
forEndDate:(NSDate *)endDate
orBodyText:(NSString *)bodyText
withHeaderText:(NSString *)headerText
{
assert(complication.family == CLKComplicationFamilyModularLarge);
CLKComplicationTemplateModularLargeStandardBody *template = [[CLKComplicationTemplateModularLargeStandardBody alloc] init];
template.headerTextProvider = [CLKSimpleTextProvider textProviderWithText:headerText];
if (endDate)
{
template.body1TextProvider = [CLKRelativeDateTextProvider textProviderWithDate:endDate style:CLKRelativeDateStyleTimer units:NSCalendarUnitHour|NSCalendarUnitMinute|NSCalendarUnitSecond];
}
else
{
assert(bodyText);
template.body1TextProvider = [CLKSimpleTextProvider textProviderWithText:bodyText];
}
return template;
}
- (CLKComplicationTimelineEntry *)getComplicationTimelineEntryForComplication:(CLKComplication *)complication
forStartDate:(NSDate *)startDate
endDate:(NSDate *)endDate
orBodyText:(NSString *)bodyText
withHeaderText:(NSString *)headerText
{
CLKComplicationTimelineEntry *entry = [[CLKComplicationTimelineEntry alloc] init];
entry.date = startDate;
entry.complicationTemplate = [self getComplicationTemplateForComplication:complication forEndDate:endDate orBodyText:bodyText withHeaderText:headerText];
return entry;
}
- (void)getCurrentTimelineEntryForComplication:(CLKComplication *)complication withHandler:(void(^)(CLKComplicationTimelineEntry * __nullable))handler
{
// Call the handler with the current timeline entry
CLKComplicationTimelineEntry *entry;
assert(complication.family == CLKComplicationFamilyModularLarge);
WKExtension *extension = [WKExtension sharedExtension];
assert(extension.delegate);
assert([extension.delegate isKindOfClass:[ExtensionDelegate class]]);
ExtensionDelegate *extensionDelegate = (ExtensionDelegate *)extension.delegate;
if (extensionDelegate.lastReceivedUserInfo)
{
NSDictionary *userInfo = extensionDelegate.lastReceivedUserInfo;
NSDate *startDate = [userInfo objectForKey:@"date"];
NSNumber *duration = [userInfo objectForKey:@"duration"];
//NSNumber *count = [userInfo objectForKey:@"count"];
NSTimeInterval totalDuration = duration.floatValue;
NSDate *endDate = [startDate dateByAddingTimeInterval:totalDuration];
entry = [self getComplicationTimelineEntryForComplication:complication forStartDate:startDate endDate:endDate orBodyText:nil withHeaderText:@"current"];
}
if (!entry)
{
NSDate *currentDate = [NSDate date];
entry = [self getComplicationTimelineEntryForComplication:complication forStartDate:currentDate endDate:nil orBodyText:@"no user info" withHeaderText:@"current"];
}
DbgLog(@"calling handler for entry at date=%@", entry.date);
handler(entry);
}
- (void)getTimelineEntriesForComplication:(CLKComplication *)complication beforeDate:(NSDate *)date limit:(NSUInteger)limit withHandler:(void(^)(NSArray<CLKComplicationTimelineEntry *> * __nullable entries))handler
{
NSArray *retArray;
assert(complication.family == CLKComplicationFamilyModularLarge);
WKExtension *extension = [WKExtension sharedExtension];
assert(extension.delegate);
assert([extension.delegate isKindOfClass:[ExtensionDelegate class]]);
ExtensionDelegate *extensionDelegate = (ExtensionDelegate *)extension.delegate;
if (extensionDelegate.lastReceivedUserInfo)
{
NSDictionary *userInfo = extensionDelegate.lastReceivedUserInfo;
NSDate *startDate = [userInfo objectForKey:@"date"];
if ([startDate timeIntervalSinceDate:date] < 0.f)
{
assert(0);
// not expected to be asked about any date earlier than our startDate
}
}
// Call the handler with the timeline entries prior to the given date
handler(retArray);
}
- (void)getTimelineEntriesForComplication:(CLKComplication *)complication afterDate:(NSDate *)date limit:(NSUInteger)limit withHandler:(void(^)(NSArray<CLKComplicationTimelineEntry *> * __nullable entries))handler
{
NSMutableArray *timelineEntries = [[NSMutableArray alloc] init];
assert(complication.family == CLKComplicationFamilyModularLarge);
WKExtension *extension = [WKExtension sharedExtension];
assert(extension.delegate);
assert([extension.delegate isKindOfClass:[ExtensionDelegate class]]);
ExtensionDelegate *extensionDelegate = (ExtensionDelegate *)extension.delegate;
if (extensionDelegate.lastReceivedUserInfo)
{
NSDictionary *userInfo = extensionDelegate.lastReceivedUserInfo;
NSDate *startDate = [userInfo objectForKey:@"date"];
NSNumber *duration = [userInfo objectForKey:@"duration"];
NSNumber *count = [userInfo objectForKey:@"count"];
NSInteger i;
for (i=0; i<count.integerValue && timelineEntries.count < limit; ++i)
{
NSTimeInterval entryDateOffset = duration.floatValue * i;
NSDate *entryDate = [startDate dateByAddingTimeInterval:entryDateOffset];
if ([entryDate timeIntervalSinceDate:date] > 0)
{
NSDate *timerEndDate = [entryDate dateByAddingTimeInterval:duration.floatValue];
DbgLog(@"adding entry at date=%@; with timerEndDate=%@ i=%d", entryDate, timerEndDate, i);
CLKComplicationTimelineEntry *entry = [self getComplicationTimelineEntryForComplication:complication forStartDate:entryDate endDate:timerEndDate orBodyText:nil withHeaderText:[NSString stringWithFormat:@"After %d", i]];
[timelineEntries addObject:entry];
}
}
if (i==count.integerValue && timelineEntries.count < limit)
{
NSDate *timelineEndDate = [self getTimelineEndDate];
CLKComplicationTimelineEntry *entry = [self getComplicationTimelineEntryForComplication:complication forStartDate:timelineEndDate endDate:nil orBodyText:@"Finished" withHeaderText:@"Test"];
[timelineEntries addObject:entry];
}
}
NSArray *retArray;
if (timelineEntries.count > 0)
{
retArray = timelineEntries;
}
// Call the handler with the timeline entries after to the given date
handler(retArray);
}
#pragma mark Update Scheduling
/*
// don't want any updates other than the ones we request directly
- (void)getNextRequestedUpdateDateWithHandler:(void(^)(NSDate * __nullable updateDate))handler
{
// Call the handler with the date when you would next like to be given the opportunity to update your complication content
handler(nil);
}
*/
- (void)requestedUpdateBudgetExhausted
{
DbgLog(@"");
}
#pragma mark - Placeholder Templates
- (void)getPlaceholderTemplateForComplication:(CLKComplication *)complication withHandler:(void(^)(CLKComplicationTemplate * __nullable complicationTemplate))handler
{
CLKComplicationTemplate *template = [self getComplicationTemplateForComplication:complication forEndDate:nil orBodyText:@"doing nothing" withHeaderText:@"placeholder"];
// This method will be called once per supported complication, and the results will be cached
handler(template);
}
@end
Может быть, вы можете увидеть, если у вас есть те же проблемы с вашими собственными осложнениями в ваших собственных приложениях.
Я не думаю, что я делаю что-то неправильно, ничего, что должно было бы вызвать это странное поведение, просто чувствует себя как ошибка для меня. К сожалению, это тот, который подрывает мое приложение, которое может работать с довольно небольшими записями временной шкалы в некоторых случаях, и я бы предпочел, чтобы они просто не работали, если пользователь обращает внимание и держит экран просмотра во время тестирования.
Спасибо за ваше время,
Приветствия
Спасибо за такой подробный ответ. Я отредактирую свое сообщение, чтобы показать код, а затем снова ответьте на ваши вопросы. Ура! – jimbobuk
Опять же, большое спасибо за написание такого подробного ответа. Осложнения, похоже, не имеют большого освещения здесь из моих собственных поисков. Теперь я обновил исходное сообщение с большим количеством обсуждений на фоне моего обычного приложения вместе с полными фрагментами кода и ссылкой на полный проект, который должен работать на симуляторе. Надеюсь, теперь вы можете видеть, что мой метод afterDate осложнен. Вы можете видеть из журналов вывода, что даты, которые я передаю, верны. Вы также можете увидеть, что startDate системные ошибки - это на самом деле мой endDate !! – jimbobuk
Важнейшим образом, усложнение может вести себя правильно, если пользователь выполняет технику блокировки/отображения часов, подразумевая ошибку в ОС, поскольку нет новой информации о осложнениях, отправленной в систему после начальной загрузки информации. Я также добавил небольшую заметку в начале оригинального сообщения, в котором обсуждалось основное приложение. Он НЕ создает эти 10-секундные записи усложнения интервала, я только что создал тестовое приложение, которое делает это так быстро и просто, чтобы воспроизвести проблему и показать, что она работает и не работает, как показано на моем видео, в зависимости от действия пользователя. – jimbobuk