Просмотр без необходимости обновлений @Binding, что приводит к проблемам с производительностью.IOS

Программируем под IOS
Ответить Пред. темаСлед. тема
Anonymous
 Просмотр без необходимости обновлений @Binding, что приводит к проблемам с производительностью.

Сообщение Anonymous »


У меня есть следующее корневое представление

@StateObject Private var vm = NCMediaViewModel() @State Private var title = "Медиа" ///... var body: some View { ZStack(выравнивание: .top) { MediaScrollView (columnCountStages: $columnCountStages, columnsCountStagesIndex: $columnCountStagesIndex, title: $title) .environmentObject(ВМ) .environmentObject(родительский) .onPreferenceChange(TitlePreferenceKey.self) {значение в заголовок = значение } .onPreferenceChange(ScrollOffsetPreferenceKey.self) {значение в пусть isScrolledToTop = value.y >= -10 withAnimation(.default) { titleColor = isScrolledToTop? Цвет.основной: .белый toolbarItemsColor = isScrolledToTop? .синий: .белый ToolbarColors = isScrolledToTop? [.clear] : [.black.opacity(0.8), .black.opacity(0.0)] } } HStack(содержимое: { HStack { Текст (название) .font(.system(размер: 20, вес: .bold)) .foregroundStyle(titleColor) Разделитель() Кнопка (действие: { title = String(Int.random(in: 1...50)) // vm.isInSelectMode.toggle() }, этикетка: { Text (NSLocalizedString (vm.isInSelectMode? "_cancel_" : "_select_", комментарий: "")).font(.system(size: 14)) .foregroundStyle(toolbarItemsColor) }) .padding(.горизонтальный, 6) .padding(.вертикальный, 3) .background(.ultraThinMaterial) .cornerRadius(.бесконечность) .onPreferenceChange(ScrollOffsetPreferenceKey.self) {значение в пусть isScrolledToTop = value.y >= -10 withAnimation(.default) { titleColor = isScrolledToTop? Цвет.основной: .белый toolbarItemsColor = isScrolledToTop? .синий: .белый ToolbarColors = isScrolledToTop? [.clear] : [.black.opacity(0.8), .black.opacity(0.0)] } } если vm.isInSelectMode, !vm.selectedMetadatas.isEmpty { ToolbarCircularButton (imageSystemName: «trash.fill», цвет: .red) .onTapGesture { шоуDeleteConfirmation = истина } .confirmationDialog("", isPresented: $showDeleteConfirmation) { Button(NSLocalizedString("_delete_selected_media_", комментарий: ""), role: .destructive) { vm.deleteSelectedMetadata() } } } Меню { если vm.isInSelectMode, !vm.selectedMetadatas.isEmpty { Раздел { Кнопка { vm.copyOrMoveSelectedMetadataInApp() } этикетка: { Label(NSLocalizedString("_move_selected_files_", комментарий: ""), systemImage: "arrow.up.right.square") } Кнопка { vm.copySelectedMetadata() } этикетка: { Label(NSLocalizedString("_copy_file_", комментарий: ""), systemImage: "doc.on.doc") } } } еще { Раздел { Picker(NSLocalizedString("_media_view_options_", комментарий: ""), выбор: $vm.filter) { Label(NSLocalizedString("_media_viewimage_show_", комментарий: ""), systemImage: "photo.fill").tag(Filter.onlyPhotos) Label(NSLocalizedString("_media_viewvideo_show_", комментарий: ""), systemImage: "video.fill").tag(Filter.onlyVideos) Text(NSLocalizedString("_media_show_all_", комментарий: "")).tag(Filter.all) }.pickerStyle(.меню) Кнопка { выберитеМедиафолдер() } этикетка: { Label(NSLocalizedString("_select_media_folder_", комментарий: ""), systemImage: "папка") } } Раздел { Кнопка (действие: { если позволить tabBarController = vm.appDelegate?.window?.rootViewController как? UITabBarController { NCDocumentPickerViewController (tabBarController: tabBarController, isViewerMedia: true,allowMultipleSelection: false, viewController: родительский) } }, этикетка: { Label(NSLocalizedString("_play_from_files_", комментарий: ""), systemImage: "play.circle") }) Кнопка (действие: { showPlayFromURLAlert = правда }, этикетка: { Label(NSLocalizedString("_play_from_url_", комментарий: ""), systemImage: "ссылка") }) } } } этикетка: { ToolbarCircularButton (imageSystemName: «многоточие», цвет: $toolbarItemsColor) } } }) .frame(maxWidth: .infinity) .padding([.horizontal, .top], 10) .padding(.bottom, 20) .background(LinearGradient(градиент: Градиент(цвета: ToolbarColors), startPoint: .top, endPoint: .bottom).edgesIgnoringSafeArea(.top)) } .onRotate { ориентация в если ориентация.isLandscapeHardCheck { столбецCountStages = [4, 6, 8] } еще { столбецCountStages = [4, 4, 4] } } .onAppear { vm.loadMediaFromDB() } .onChange(of: vm.isInSelectMode) { newValue в если newValue == false { vm.selectedMetadatas.removeAll() } } .onChange(of: columnsCountStagesIndex) { _ в ColumnCountChanged = правда } .alert("", isPresented: $showPlayFromURLAlert) { TextField("https://...", текст: $playFromUrlString) .keyboardType(.URL) .textContentType(.URL) Button(NSLocalizedString("_cancel_", комментарий: ""), role: .cancel) {} Button(NSLocalizedString("_ok_", комментарий: "")) { playVideoFromUrl() } } сообщение: { Text(NSLocalizedString("_valid_video_url_", комментарий: "")) } .жест( MagnificationGesture (минимумScaleDelta: 0) .onChanged { масштабировать печать (масштаб) если !columnCountChanged { let newZoom = Double(columnCountStages[columnCountStagesIndex]) * 1 / масштаб let newZoomIndex = findClosestZoomIndex (значение: newZoom) columnsCountStagesIndex = новыйZoomIndex } } .onEnded({ _ в столбецCountChanged = ложь }) ) } Я инициализирую MediaScrollView и передаю определенные @State как @Binding, один из них — title.

struct MediaScrollView: Просмотр { @EnvironmentObject var vm: NCMediaViewModel @EnvironmentObject var родительский: NCMediaUIKitWrapper @Binding var columnsCountStages: [Int] @Binding var columnsCountStagesIndex: Int @Binding var title: String var body: some View { ПрокруткаView { LazyVStack(выравнивание: .leading, интервал: 2) { ForEach(vm.metadatas.chunked(into: columnsCountStages[columnCountStagesIndex]), id: \.self) { rowMetadatas в NCMediaRow (метаданные: rowMetadatas, кэш: vm.cache, isInSelectMode: $vm.isInSelectMode) { TappedThumbnail, isSelected в //TODO: поместить в виртуальную машину если vm.isInSelectMode, isSelected { vm.selectedMetadatas.append(tappedThumbnail.metadata) } еще { vm.selectedMetadatas.removeAll(где: { $0.ocId == TappedThumbnail.metadata.ocId }) } если !vm.isInSelectMode { пусть selectedMetadata = TappedThumbnail.metadata vm.onCellTapped(метаданные: selectedMetadata) NCViewer.shared.view (viewController: родительский элемент, метаданные: selectedMetadata, метаданные: vm.metadatas, imageIcon: TappedThumbnail.image) } } onCellContextMenuItemSelected: { миниатюра, выделение в пусть выбранныеМетаданные = миниатюра.метаданные переключить выбор { случай .addToFavorites: vm.addToFavorites(метаданные: selectedMetadata) случай .подробности: NCActionCenter.shared.openShare (viewController: родительский, метаданные: selectedMetadata, страница: .activity) случай .openIn: vm.openIn(метаданные: selectedMetadata) случай .saveToPhotos: vm.saveToPhotos(метаданные: selectedMetadata) случай .viewInFolder: vm.viewInFolder(метаданные: selectedMetadata) случай .изменить: vm.modify(метаданные: выбранныеМетаданные) случай .удалить: vm.delete(метаданные: selectedMetadata) } } .onAppear { title = CCUtility.getTitleSectionDate(rowMetadatas.first?.date as? Date) ?? "" } если vm.needsLoadingMoreItems { ПрогрессВью() .frame(максШирина: .бесконечность) .onAppear { vm.loadMoreItems() } .padding(.top, 10) } } } .padding(.top, 70) .padding(.bottom, 40) .background(GeometryReader { геометрия в Цвет.прозрачный .preference(ключ: ScrollOffsetPreferenceKey.self, значение: geometry.frame(in: .named("scroll")).origin) }) } .preference(ключ: TitlePreferenceKey.self, значение: заголовок) .обновляемый { дождитесь vm.onPullToRefresh() } // Невозможно переместить представление управления обновлением через SwiftUI, поэтому нам нужно проанализировать внутренние представления UIKit, чтобы переместить его. // TODO: возможно, .contespontMargins() решит эту проблему, но это iOS 17+ .introspect(.scrollView, on: .iOS(.v15...)) {scrollView в ScrollView.refreshControl?.translatesAutoresizingMaskIntoConstraints = false ScrollView.refreshControl?.topAnchor.constraint(equalTo: ScrollView.superview!.topAnchor, константа: 120).isActive = true ScrollView.refreshControl?.centerXAnchor.constraint(equalTo: ScrollView.centerXAnchor).isActive = true } .coordinateSpace(имя: «прокрутка») } } onAppear обновляет привязку title во время прокрутки, что, в свою очередь, обновляет корневое представление. Проблема в том, что также обновляется дочернее представление, а также каждый NCMediaRow и во время прокрутки, что влияет на производительность. При обновлении title во время прокрутки наблюдается заметное зависание.

struct NCMediaRow: Просмотр { пусть метаданные: [tableMetadata] пусть кэшируется: UIImageLRUCache @Binding var isInSelectMode: Bool let onCellSelected: (ScaledThumbnail, Bool) -> Void let onCellContextMenuItemSelected: (ScaledThumbnail, ContextMenuSelection) -> Void @StateObject частный var vm = NCMediaRowViewModel() личное расстояние между квартирами: CGFloat = 2 Размер @State var: CGSize = .zero var body: some View { пусть _ = Self._printChanges() HStack (интервал: интервал) { если vm.rowData.scaledThumbnails.isEmpty { ForEach(metadatas, id: \.self) { метаданные в NCMediaLoadingCell (itemsInRow: метаданные.count, метаданные: метаданные, rowSize: size.width, интервал: интервал) } } еще { ForEach(vm.rowData.scaledThumbnails, id: \.self) {миниатюра в NCMediaCell (миниатюра: миниатюра, ShrinkRatio: vm.rowData.shrinkRatio, isInSelectMode: $isInSelectMode, onSelected: onCellSelected, onContextMenuItemSelected: onCellContextMenuItemSelected, isFavorite: миниатюра.metadata.favorite) } } } .фон( GeometryReader { прокси в Цвет.прозрачный .onAppear { размер = proxy.size } } ) .onAppear { vm.configure(метаданные: метаданные, кеш: кеш) vm.downloadThumbnails (rowWidth: size.width, интервал: интервал) } } } Я добавил Self._printChanges() в представление NCMediaRow, и вот что распечатывается:

NCMediaRow: @self, _isInSelectMode изменено. NCMediaRow: @self, _isInSelectMode изменен. NCMediaRow: @self, _isInSelectMode изменен. NCMediaRow: @self, _isInSelectMode изменен. NCMediaRow: @self, _isInSelectMode изменен. NCMediaRow: @self, _isInSelectMode изменен. NCMediaRow: @self, _isInSelectMode изменен. NCMediaRow: @self, _isInSelectMode изменен. NCMediaRow: @self, _isInSelectMode изменен. NCMediaRow: @self, _isInSelectMode изменен. NCMediaRow: @self, _isInSelectMode изменен. NCMediaRow: @self, _isInSelectMode изменен. NCMediaRow: @self, _isInSelectMode изменен. NCMediaRow: @self, _isInSelectMode изменен. NCMediaRow: @self, _isInSelectMode изменен. NCMediaRow: @self, _isInSelectMode изменен. NCMediaRow: @self, _isInSelectMode изменен. NCMediaRow: @self, _isInSelectMode изменен. NCMediaRow: @self, _isInSelectMode изменен. NCMediaRow: @self, _isInSelectMode изменен. NCMediaRow: @self, _isInSelectMode изменен. NCMediaRow: @self, _isInSelectMode изменен. Как видите, все строки на экране обновляются при обновлении title.

Удаление title @Binding и его обновление только в корневом представлении приведет к прекращению обновления NCMediaRow.

Я не уверен, как остановить обновление NCMediaRow даже с помощью этой @Binding. Мне это нужно, чтобы иметь возможность обновлять title.

Я пытался:
[*]Удалите ZStack из корневого представления, поскольку я его читаю, иногда обновляются все содержащиеся в нем представления. [*]Удаление isInSelectMode, поскольку оно появляется в печати, но @self все равно меняется. Удаление @Binding и использование вместо него PreferenceKey для обновления title. Я предположил, что, поскольку это ассоциация дочернего элемента с родительским, он не будет обновлять дочерний элемент, но я предполагаю, что это все еще в каком-то смысле @Binding, а NCMediaRow все еще обновляется.
Реклама
Ответить Пред. темаСлед. тема

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

  • Похожие темы
    Ответы
    Просмотры
    Последнее сообщение

Вернуться в «IOS»