#arrays #cocoa #subview #superview
#массивы #какао #подвиды #супервизор
Вопрос:
У меня есть программа макета рекламы, которая создает страницы (страница класса NSView) и на каждой странице размещает поля и желоба (больше NSView), а затем объявления (объявление класса NSView). Приложение автоматически размещает объявления из источника базы данных, ища подходящее место для заполнения на каждой странице. Затем у пользователя есть возможность переместить объявления, скажем, со страницы 4 на страницу 2. У них также есть возможность вручную отображать объявления или запускать автозапуск приложения, предполагая, что только что перемещенное объявление окажется в местоположении 0/0 x / y, а затем остальные переместятся в любое доступное пространство и будут перенесены на следующую страницу, если на текущей странице нет свободного места.один (из-за объявления, которое вы только что переместили).
То, что я делал, это получал все страницы (сами подвиды NSView, называемые masterpage), а затем перебирал их, чтобы получить все их подвиды в массив. Перебирайте этот массив, фильтруя поля и желоба, и, если это объявление, удалите его из супервизора. Я думал начать с чистой страницы и заменить все объявления. Проблема в том, что я продолжаю получать эту ошибку:
Collection <NSCFArray: 0x1078900> was mutated while being enumerated.
Я использую этот код:
//loop through all the pages
for(NSView* thisPage in masterPageSubViews) {
//get all the ads on this page
NSArray* thisPageSubviews = [thisPage subviews];
for(NSView* thisPageSubview in thisPageSubviews) {
if ([[thisPageSubview className] isEqual:@"Ad"]) {
[thisPageSubview removeFromSuperview];
}
}
}
Наверное, я не понимаю, почему я получаю эту ошибку. Я думал, что объекты в массиве отделены от объектов, создаваемых в цикле.
Поскольку количество объявлений на каждой странице является динамическим, как я могу удалить их из супервизора без использования массива?
Ответ №1:
Как указано в ошибке, вы не можете изменить содержимое массива при просмотре его с помощью быстрого перечисления. removeFromSuperview
удаляет представление из subviews
массива супервизора (это сложное предложение для чтения!). Это означает, что вы изменяете массив во время его перечисления.
Вероятно, есть лучший способ, но у вас может быть NSMutableArray
вариант, который вы заполняете просмотрами объявлений, а затем, как только вы закончите, вы можете заставить их удалиться из супервизора (непроверенный код, введите в браузере!)
NSMutableArray *viewsToRemove = [[NSMutableArray alloc] init];
for(NSView* thisPage in masterPageSubViews)
{
//get all the ads on this page
NSArray* thisPageSubviews = [thisPage subviews];
for(NSView* thisPageSubview in thisPageSubviews)
if ([thisPageSubview isMemberOfClass:[Ad class]])
[viewsToRemove addObject:thisPageSubview];
}
[viewsToRemove makeObjectsPerformSelector:@selector(removeFromSuperview)];
[viewsToRemove release];
Комментарии:
1. Спасибо за быстрый ответ, я собираюсь попробовать. Но я все еще не понимаю, как я изменяю содержимое массива. Это потому, что, когда я перебираю массив, я удаляю представление из его супервизора, и, следовательно, объекта, на который указывает объект массива, больше нет?
2. Также спасибо за метод isMemberOfClass. Этого не знал.
3. Добавлена пояснительная строка в ответе относительно изменения массива во время его перечисления.
4. это сработало, просто нужно было исправить первый вызов NSMutableArray, не было *, кроме того, что все было в порядке.
5. ах, объяснение имеет смысл. Спасибо.
Ответ №2:
Приведенный выше ответ довольно дорогой, зачем создавать объекты, когда вы можете этого избежать? И вы можете сделать то же самое с меньшим количеством кода.
while([[superView subviews] count] > 0) {
[[[superView subviews] objectAtIndex:0] removeFromSuperview];
}
РЕДАКТИРОВАТЬ: я новичок в Objective C и Cocoa, поэтому я понятия не имею, будет ли это работать без ARC, поскольку я никогда не кодировал без него.
Ответ №3:
if([[yourView subviews] count] > 0) {
[[[yourView subviews] objectAtIndex:0] removeFromSuperview];
}
Good Luck !!!
Ответ №4:
По крайней мере, с 10.10 вы можете использовать быстрое перечисление для вложенных представлений, поскольку оно возвращает копию.
[view.subviews enumerateObjectsUsingBlock:^(NSView * subview, NSUInteger idx, BOOL *stop) {
[subview removeFromSuperview];
}];
Ответ №5:
Если проверка класса является проблемой (и в исходном вопросе это было), то, опять же, без перечислителя, проблема может быть решена из серверной части:
NSArray *subs = [myView subviews];
for(NSInteger i=[subs count]-1; i>0; i--){
if([[subs[i] className]isEqual:@"nameOfClassToDelete"]){
[subs[i] removeFromSuperviewWithoutNeedingDisplay];
}
}
У меня есть этот код на практике, он работает 🙂