12. Медленный GalleryView со списком картинКод

Экран выставки картин работает медленно и прерывисто. Нужно указать проблемы в этом коде и исправить его так, чтобы этих проблем не осталось.

enum PaintingType: Identifiable { case masterpiece case contemporary case sketch var id: UUID { UUID() } } struct Exhibition { var title: String var description: String var paintings: [PaintingType] = [] } struct TitleView: View { let exhibition: Exhibition var body: some View { Text(exhibition.title) } } struct PaintingView: View { var painting: PaintingType var body: some View { // Интерфейс картины } } struct GalleryView: View { @State var exhibition: Exhibition var body: some View { ScrollView { VStack { TitleView(exhibition: exhibition) ForEach(exhibition.paintings) { painting in if painting != .sketch { PaintingView(painting: painting) } } TextField("Название", text: $exhibition.title) Text(exhibition.description) } } } }

Дополнительные вопросы

1. Если view нужны несколько полей, например четыре, как передать их?

Темы

Компании

Ответ

  • У enum геттер id каждый раз возвращает новый UUID(). ForEach сверяет элементы по id, не находит совпадений и пересоздаёт PaintingView.
  • В TitleView передают весь Exhibition, хотя view рисует только название.
  • ScrollView с VStack сразу строит все картины, даже вне экрана.
enum PaintingType: Identifiable { case masterpiece case contemporary case sketch // stored properties у enum нет, let id = UUID() не поставить var id: Self { self } // стабильный, не новый UUID() } struct Exhibition { var title: String var description: String var paintings: [PaintingType] = [] } struct TitleView: View { let title: String // только то, что рисуем var body: some View { Text(title) } } struct PaintingView: View { let painting: PaintingType var body: some View { // Интерфейс картины } } struct GalleryView: View { @State var exhibition: Exhibition var visiblePaintings: [PaintingType] { exhibition.paintings.filter { $0 != .sketch } // вынесли для удобства, чтобы не писать if внутри ForEach } var body: some View { ScrollView { LazyVStack { // view по мере прокрутки TitleView(title: exhibition.title) ForEach(visiblePaintings) { painting in PaintingView(painting: painting) } TextField("Название", text: $exhibition.title) Text(exhibition.description) } } } }

Дополнительные вопросы

1. Для view лучше собрать модель из нужных полей и передать её целиком, а не список отдельных параметров.

struct HeaderModel { let title: String let subtitle: String let year: Int let isHighlighted: Bool } struct HeaderView: View { let model: HeaderModel var body: some View { Text(model.title) } }

Список дёргается из-за идентичности и из-за того, что все картины строятся сразу. id у PaintingType каждый раз возвращает новый UUID(). ForEach считает, что элементы сменились, и пересоздаёт вью на каждом обновлении. Два одинаковых case ещё и неразличимы. Картине нужен свой стабильный id, записанный один раз при создании, а не вычисляемый при чтении. VStack измеряет все картины разом. Для длинного списка нужен LazyVStack. .sketch лучше отфильтровать до ForEach, а не прятать условие внутри.

struct Painting: Identifiable { let id = UUID() var type: PaintingType } var paintings = exhibition.paintings.filter { $0.type != .sketch } LazyVStack { ForEach(paintings) { painting in PaintingView(painting: painting) } }

Дополнительные вопросы

1. Передавать то, что вью читает, а не всю модель. Четыре поля — четыре параметра или маленький тип только с этими полями. Тогда правка списка картин не пересобирает заголовок, которому список не нужен.