Как стать автором
Обновить

Комментарии 25

Часто люди любят сразу называть багами то, что они не понимают и с чем неправильно работают. Я не говорю конкретно о Вас и всех тех вещах, которые Вы описали, но часть из них все же надуманные.

— reloadData действительно работает асинхронно. Я не нашел подтверждения этому в документации, но проверить это не сложно, достаточно посмотреть в какой последовательности выполняются методы DataSource для этой UICollectionView.

— «Попытка выполнить две операции анимации одновременно без performBatchUpdates блока приведет к падению приложения. „
на мой взгляд, ве логично и обоснованно. Вы начинаете изменение данных, оно анимируется, все это происходит асинхронно, не дождавшись завершения, Вы начинаете вторую операцию, которая тоже изменяет данные, что же должно произойти?!

и так далее.
Статья и не претендует на обучалку, как правильно работать с UICollectionView. Документация же, к сожалению, не содержит ответов на те проблемы, с которыми я неоднократно сталкивался. Я тоже считаю, что костыль — это просто признак того, что разработчик в каком-то смысле сдался, и не нашел корректного и стабильного решения проблемы. Но UICollectionView не оставляет другого выбора. Над некоторыми из проблем, перечисленных в статье, я убил по неделе времени, и не нашел решения лучше, чем описал сейчас.

А самый печальный факт — то, что стабильно работает с UITableView, при абсолютно идентичном использовании — не работает с UICollectionView. Моя библиотека для работы с UITableView — не содержит ни одного костыля в принципе, там все работает стабильно и безошибочно. Хотя архитектура абсолютно идентична тому, что я использую для UICollectionView.
Не понимаю вашего комментария. То, что reloadData работает асинхронно, я тоже узнал из личного опыта, и это, поверьте, было совсем не хорошей новостью — легко это проверить или сложно. А вдруг вам надо сначала перезагрузить данные, а потом сразу же вызвать какую-либо анимацию layout-а?
Учите матчасть.

Для справки: данные «перезагружает» collectionView.datasource, а никак не [collectionView reloadData]. Последний отвечает за обновление интерфейса в ответ на измененные данные.
Речь идет именно об обновлении интерфейса. UITableView обновляет его синхронно, UICollectionView, несмотря на отсутствие каких-либо анимаций — асинхронно.
Это я понимаю, я отвечал не на Ваш комментарий, а на «сумбурный крик» другого комментирующего.
Что касается асинхронной работы reloadData в UICollectionView, да, это и у меня порой вызывает сложные для решения проблемы, но приходится с этим жить.
Опечатался ночью, очевидно я писал о перерисовке интерфейса. В самой collection view вообще нет данных, т.к. это юайный класс.

Смените, пожалуйста, тон. Я подозреваю, что у вас не зря карма в минусе и вы со всеми так разговариваете.
Извините, если обидел, я не хотел этого.
Что касается кармы, карма на хабре отдельная тема, можно запостить веселую картинку и получить 1000 плюсов, а можно критику по делу высказать и получить 1000 минусов.
Ох, и больную же вы тему подняли! Давно хотел написать подобную статью, все руки не доходили. Сколько ж я с ней намучался за последние полтора года, а как красиво все начиналось… поначалу я был в восторге и от API, и от возможностей. Глубоко уверен, что Apple стоит больше покрывать код юнит-тестами. Из того, что первым вспомнилось без мака под рукой:

1) UICollectionView плохо обрабатывает очень высокие ячейки. Если высота ячейки — около 5-10 экранов (в зависимости от устройства), она при скроле начинает сначала «снежить», а потом просто пропадает. При этом в Open Source-реализации PSTCollectionView такого бага не было обнаружено.
2) Знает тот, кто пробовал имитировать стандартное для UITableView «залипание» хэдеров секции. После очередного, довольно рандомного Y в contentOffset UICollectionView она перестает махом спрашивать NSLayoutAttributes для, в моем случае, первых пяти секций, и хоть ты убейся. Пиксель назад спрашивает, пиксель вперед — начинает с шестой.

Ну и все или почти все ваши случаи, конечно, встречал. Особенно асинхронность, которой не должно быть, или которая должна быть хотя бы управляемой, и проблемы с синхронизацией анимаций — сущий ад. Добавлю один пункт:

3) Попробуйте запустить какую-либо анимацию в ячейке, и одновременно после этого вызвать reloadData. Крэш обеспечен.

Эпплу по рукам за нефикс багов функционала, который вышел полтора года назад, и юнит-тесты и еще раз юнит-тесты. И функциональные тесты. Библиотеку пишете, а не RSS reader! Зато живые иконки часов сделать — это пожалуйста, это всем очень нужно…
Я плюнул на залипание с помощью хэдеров и делал нормальный лэйаут поверх всего этого безобразия который следит за положением скролла таблицы. Работает безотказно. Там еще все это завязано на положение нав бара, что доставляет отдельного невероятного геморроя.
Т.е. вы подписываетесь на изменение contentInset-a и рисуете хэдер сами? Был бы очень признателен, если бы вы рассказали про свой способ поподробнее, т.к. очень уж много времени убил на эти хэдэры…
Да. Мне будет тяжело с примерами кода, они на Xamarin. Но смысл с нативным кодом тот же самый, разве что событие скролла может будет где-то в другом месте.

1. Инициализирую новый View поверх таблицы и связываю его с ней логически. Это наш кастом хэдер (наследован от UIView).
2. Ловлю событие Scrolled в datasource коллекции. Проверяю наличие кастом хэдера и если он есть, то вызываю его метод определения нового положения.
3. Обновляю положение вот таким вот методом внутри кастом хэдера:

//Метод заставляет хэдер плавно прятаться когда таблица скроллится вниз дальше определенного положения и показываться когда таблица скроллится вверх.
public void Scrolled (MonoTouch.UIKit.UIScrollView scrollView)
		{

			var topInset = scrollView.ContentOffset.Y + scrollView.ContentInset.Top;
			float downInset = 200;
			//fix when scroll down and scroll up
			if (this.Frame.Y > topInset || this.Frame.Y< downInset ){//<scrollView.ContentInset.Top)
				this.Frame = new RectangleF (
					x: this.Frame.X,
					y: topInset,
					width: this.Frame.Width,
					height: this.Frame.Height
					);

				this.SetNeedsDisplay ();
			}

		}

2) Знает тот, кто пробовал имитировать стандартное для UITableView «залипание» хэдеров секции.

А в вашем случае можно было использовать такой подход: обернуть коллекцию в таблицу, то есть каждая секция в вашей коллекции — ячейка в таблице, у которой на contentView лежит коллекция? В таблице же количество секций = количеству ячеек.
Также спасибо за ссылку на DTCollectionViewManager, не сомневаюсь что очень полезная вещь. Но есть один существенный недостаток — любой ваш контроллер должен наследоваться от DTCollectionViewController. А т.к. в objective-c нет множественного наследования, наследоваться от чего-то другого невозможно.

А так конечно да, если данное требование не критично — модель шикарна. Так уже надоело менять Data Source, потом искать index path нужной ячейки и т.п!
Я тоже думал изначально датасорс делать отдельным обьектом, но в итоге отказался от данной затеи, все-таки слишком сложная получается архитектура. Для себя я решил данную проблему таким способом — везде, где используется UICollectionView или UITableView, и контроллер достаточно сложный, я просто добавляю DTCollectionViewController как childViewController. А события при необходимости прокидываю через протокол. Таким образом родительский контроллер можно унаследовать от чего угодно.
Я натыкался ещё на один баг UICollectionView, когда Flow Layout считал начальную координату по оси X ячейки неправильно если у коллекции был установлен contentInset, воспроизводилось стабильно, но только на некоторых инстансах, то есть в одном приложении из двух казалось бы одинаковых UICollectionView на одном стабильно воспроизводится, на другом стабильно всё прекрасно. Ни причины ни тем более решения так и не нашёл.

Если говорить о UITableView в нём тоже далеко не всё так радужно, прямо сейчас уже больше дня бьюсь головой об стенку. Есть ячейка в xib с включенным auto layout и приложение без него на storyboard (понадобилась динамическая высота ячейки, а приложение полностью переделывать долго), так вот лезет постоянная ошибка с конфликтующими ограничениями среди которых подлый NSAutoresizingMaskLayoutConstraint на contentView, который мало того, что абсолютно непонятно откуда берётся, так ещё и оказывается меньше чем минимальная ширина ячейки, что и влечёт конфликт.

Вообще для меня лично основной источник проблем это невозможность залезть в базовые библиотеки отладчиком (тут я может и просто не туда смотрю, но не вижу как это сделать), и даже там, куда я могу дотянуться, отладчик частенько не может показать значения полей (Даже frame у контрола какого-нибудь). Да я избалован явой, в которой почти всегда можно докопаться что и почему происходит, тут же я прыгаю вокруг чёрного ящика.
frame можно вывести в консоль через po [NSValue valueWithCGRect:view.frame].
С констрэйнтами есть такая беда — они очень плохо работают в иерархии вьюх, в которых то используется, то не используется автолэйаут. Мой совет — все же переделать ячейку без autolayout.
есть еще NSStringFromCGRect()
еще есть p view.frame, или p (CGRect)[something frame]
это УЖЕ есть, на момент написания коммента еще не работало :)
наверное :) я до недавних времен, не зная о NSStringFromCGRect юзал CGRectCreateDictionaryRepresentation, что длинновато
о да, как я вас понимаю — еще и intellisense в последнем икскоде тормозит знатно, а на С-шные функции вроде вообще не работало в какой-то версии из последних…
вы проверяли какие-то из этих «багов» на бете 7.1?
может быть что-то из этого уже пофиксено?
К сожалению нет, на тестирование беты у меня никогда не хватало времени. Как только выйдет финальный релиз, я обязательно проверю основные баги, и обновлю этот пост.
Как мне кажется, 90 % причин всех багов в UICollectionView можно разделить на две категории:
1) множественные анимированные действия без performBatchUpdates блока;
2) наличие нескольких секций, хедеров (supplementary view) и футеров (decoration view).
Первое вроде как не тревожит, если все множественные вставки/удаления делать корректно и по науке (читай в performBatchUpdates) блоке.
По поводу второго, мне кажется, есть неплохой вариант, который даже вроде бы и не костыль вовсе. Суть в том, чтобы использовать только односекционные коллекции без футеров и хедеров. А уже эти коллекции использовать как ячейки UITableView, который и будет ответственным за возню со всеми этими хедерами и футерами (а он с этим неплохо справляется).
При секциях, предполагающих большое количество ячеек, конечно, таким грешить не стоит.
Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.