Можно ли динамически расширять иерархию NSProgress после начала прогресса?
Скажем, у меня есть некоторая иерархия NSProgress
объекты. Для простоты скажем основной прогресс с 2 детьми. Если прогресс уже начался, могу ли я добавить еще один дочерний прогресс в иерархию и ожидать, что это произойдет правильно? Например, скажем, я начинаю с 2 детьми, поэтому totalUnitCount
корень 2
и pendingUnitCount
является 2
, Как только первый ребенок завершит корень прогресса " fractionCompleted
будет 0.5
, Если я потом добавлю еще одного ребенка fractionCompleted
обновляет до ~0.33
как хотелось бы но рут totalUnitCount
остается в 2
как и pendingUnitCount
, Я могу обновить totalUnitCount
вручную, но это бросает корень fractionCompleted
выкл. Кроме того, кажется, нет возможности обновить pendingUnitCount
без звонка becomeCurrentWithPendingUnitCount:
, Было NSProgress
не предназначен для такого использования или есть "правильный" способ сделать это?
Мой пример использования - у меня есть приложение, которое может загружать файлы на удаленный сервер. Пользователь может выбрать некоторые файлы и нажать кнопку, чтобы начать их загрузку. Во время загрузки пользователь видит общий ход выполнения задания загрузки. Теперь, если пользователь хочет выбрать еще несколько фотографий для загрузки, когда загрузки уже выполняются, я хочу обновить прогресс, чтобы отразить новый итог. Например, в настоящее время он загружает 2 файла, 1 из которых уже завершен, и пока они загружают, он добавляет еще 2 для загрузки. Прогресс должен наладиться сам. Общее количество единиц теперь должно быть 4, и для загрузки осталось 3 элемента. Похоже, это было бы обычным делом для меня.
Вот модульный тест, который я написал для динамического расширения NSProgress
иерархия. Все утверждения в тесте ниже проходят, но если вы проверите рут totalUnitCount
оно всегда остается равным 2, и, похоже, нет никакого способа определить, сколько элементов осталось выполнить из основного процесса. Корневой прогресс - это тот, который будет наблюдать пользовательский интерфейс, поэтому важно, чтобы изменения в иерархии распространялись глубже, включая totalUnitCount
, так далее.
- (void)testDynamicNSProgress
{
NSProgress *root = [NSProgress progressWithTotalUnitCount:2];
[root becomeCurrentWithPendingUnitCount:2];
NSProgress *child1 = [NSProgress progressWithTotalUnitCount:1];
NSProgress *child2 = [NSProgress progressWithTotalUnitCount:1];
XCTAssertEqual(root.totalUnitCount, 2);
XCTAssertEqual(root.completedUnitCount, 0);
child1.completedUnitCount++;
XCTAssertEqual(ceil(root.fractionCompleted * 100), 50);
NSProgress *child3 = [NSProgress progressWithTotalUnitCount:1];
NSProgress *child4 = [NSProgress progressWithTotalUnitCount:1];
XCTAssertEqual(ceil(root.fractionCompleted * 100), 25);
child2.completedUnitCount++;
XCTAssertEqual(ceil(root.fractionCompleted * 100), 50);
child3.completedUnitCount++;
XCTAssertEqual(ceil(root.fractionCompleted * 100), 75);
child4.completedUnitCount++;
XCTAssertEqual(ceil(root.fractionCompleted * 100), 100);
__unused NSProgress *child5 = [NSProgress progressWithTotalUnitCount:1];
XCTAssertEqual(ceil(root.fractionCompleted * 100), 80);
}
2 ответа
Я подал ошибку в Apple, и это был их ответ:
Инжиниринг установил, что эта проблема ведет себя как задумано, основываясь на следующей информации:
Причина в том, что мы не можем обновить счетчик завершенных единиц родителя, пока какой-либо член "группы" потомков (тех, кто присоединен к нему по мере продвижения ребенка, пока идет ход) не завершен. Как только все члены группы будут готовы, мы отсоединяем эту группу от родителя и увеличиваем количество завершенных единиц родителя. Это сохраняет правильный учет выполненных работ. Это объясняется методом локализованного описания, поэтому он дает результат, отличный от свойства completeUnitCount.
Если вы хотите более внимательно отслеживать завершение этих потомков, то вы можете сделать так, чтобы родитель стал более актуальным с меньшим количеством единиц. Затем, когда дочерние элементы будут завершены, родитель будет чаще обновлять счетчик завершенных единиц.
Ладно, думаю, я знаю, что я делаю не так. Перед добавлением нового дочернего элемента корень должен завершить текущую работу и вызвать resignCurrent. Затем корень может обновить totalUnitCount, снова стать Current с новым количеством ожидающих единиц, и дети могут выполнять работу. Кажется, что completeUnitCount currentProgress не обновляется до тех пор, пока не уйдет в отставку current. Вот почему localizedAdditionalDescription неверно. Не уверен, что это по замыслу или это ошибка, но это означает, что я не могу полагаться на completeUnitCount корневого узла, если я собираюсь динамически добавлять дочерние элементы в иерархию прогресса.
- (void)testDynamicNSProgress2
{
NSProgress *root = [NSProgress progressWithTotalUnitCount:2];
[root becomeCurrentWithPendingUnitCount:2];
XCTAssertEqual(root.completedUnitCount, 0);
NSLog(@"%@", root.localizedDescription); // 0% completed
NSLog(@"%@", root.localizedAdditionalDescription); // 0 of 2
// Add two children
NSProgress *child1 = [NSProgress progressWithTotalUnitCount:1];
NSProgress *child2 = [NSProgress progressWithTotalUnitCount:1];
// First child completes work
child1.completedUnitCount++;
XCTAssertEqual(root.completedUnitCount, 0);
NSLog(@"%@", root.localizedDescription); // 50% complete
NSLog(@"%@", root.localizedAdditionalDescription); // 1 of 2
// Second child completes work
child2.completedUnitCount++;
[root resignCurrent];
XCTAssertEqual(root.completedUnitCount, 2);
NSLog(@"%@", root.localizedDescription); // 100% complete
NSLog(@"%@", root.localizedAdditionalDescription); // 2 of 2
// Update totalUnitCount and become current again
root.totalUnitCount = 3;
[root becomeCurrentWithPendingUnitCount:1];
XCTAssertEqual(root.completedUnitCount, 2);
XCTAssertEqual(root.totalUnitCount, 3);
NSLog(@"%@", root.localizedDescription); // 66% completed
NSLog(@"%@", root.localizedAdditionalDescription); // 1 of 3 (wrong! should be 2 of 3)
// Last child completes
NSProgress *child3 = [NSProgress progressWithTotalUnitCount:1];
child3.completedUnitCount++;
[root resignCurrent];
XCTAssertEqual(root.completedUnitCount, 3);
NSLog(@"%@", root.localizedDescription); // 100% completed
NSLog(@"%@", root.localizedAdditionalDescription); // 3 of 3
}