다른 사람이 합병증 항목이 제대로 업데이트되지 않는 문제를 발견했습니다. 방금 내 앱에 초기 지원을 추가했지만 표시 할 것으로 기대되는 것을 표시하지 않는 것으로 나타났습니다. 예를 위해서, 빨리 내가 타임 라인시계 실행 도중 시계면이 숨겨져 있지 않으면 Apple Watch ClockKit 합병증이 타임 라인 항목을 업데이트하지 않습니다.
A -> B -> C
0s 10s 20s
을 만드는 것이 문제를 테스트 용이성 그러나 모든 나는 합병증 항목 A가 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에 내 타임 라인의 종료일이라고 말한 내용이었습니다
startDate입니다. 이것은 화면을 숨기거나 표시 한 후에도 작동 할 때 같은 오류가 나타나는 것을보고있는 것과 관련이 있는지 확실하지 않습니다.
나는이 동작 비디오를 내 테스트 응용 프로그램 here에 넣었습니다. 이 테스트 응용 프로그램의 세부 사항은 다음과 같습니다
해당 시뮬레이터에서 실행되어야하는 전체 코드는 here입니다. 해당되는 복잡한 모듈도 참조 용으로 여기에 나열되어 있습니다. 내 확장 위임에
, 난 iOS 앱에서 사용자 정보를 받아 내 합병증의 타임 라인ExtensionDelegate의 새로 고침을 예약합니다.일
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
의 합병증 측면을 처리하기 위해 다음과 같은 방법이 같은 문제가 발생하면 다음
- (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];
}
}
m 내 ComplicationController에 아마 당신은 볼 수 있습니다 자신의 응용 프로그램에서 자신의 합병증.
나는 이상한 행동을 일으키는 어떤 것도 잘못하고 있다고 생각하지 않습니다. 단지 버그라고 느낍니다. 불행히도 어떤 경우에는 아주 작은 타임 라인 항목으로 작업 할 수있는 내 앱을 훼손하는 앱이며, 사용자가주의를 기울이고 테스트 화면에서 시계 화면을 계속 유지하면 오히려 제대로 작동하지 않게됩니다. 시간에 대한
감사합니다,
건배
이와 같은 자세한 답장을 보내 주셔서 감사합니다. 내 게시물을 편집하여 코드를 표시 한 다음 다시 포인트에 응답합니다. 건배! – jimbobuk
다시 한 번 고맙습니다. 자세한 답장을 보내 주셔서 감사합니다. 합병증은 내 자신의 검색에서 여기에 대해 많은 관심을 보이지 않는 것 같습니다. 필자는 이제 원래의 게시물을 전체 코드 스 니펫과 함께 일반 앱의 배경과 시뮬레이터에서 실행할 수 있어야하는 전체 프로젝트에 대한 토론으로 업데이트했습니다. 제 합병증 afterDate 메서드가 건전하다는 것을 알았 으면 좋겠군요. 출력 로그에서 내가 전달한 날짜가 정확한지 확인할 수 있습니다. startDate 시스템 오류가 실제로 내 종료 날짜임을 알 수 있습니다 !! – jimbobuk
초기 정보 업로드 후 새로운 합병증 정보가 시스템에 전송되지 않으므로 사용자가 잠금/쇼 시계 앞면 기술을 수행하면 OS에서 버그를 암시하는 합병증이 적절하게 작용할 수 있습니다. 나는 또한 내 주 앱에 대해 토론하는 원본 게시물 상단에 약간의 메모를 추가했습니다. 이 10 초 간격의 복잡한 항목을 만들지는 않습니다. 테스트 애플리케이션을 만들어서 문제를 재현하고 사용자 작업에 따라 내 비디오에 표시된 것처럼 작동하지 않는 것을 빠르고 쉽게 보여줍니다. – jimbobuk