NSView удаляет некоторые подвиды из супервизора

#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];
    }
}
  

У меня есть этот код на практике, он работает 🙂