Как исправить замораживание активного индикатора при извлечении для обновления в табличном представлении

#ios #swift

#iOS #swift

Вопрос:

Каждый раз, когда я использую pull для обновления, активный индикатор и само приложение на мгновение зависают. Я нашел, какой метод вызывает эту проблему — getTracks (), если я удалю его, то все будет работать хорошо (за исключением того, что таблица не обновляется), но я не могу понять, в чем дело, я перепробовал много вариантов, но без результата. Кто-нибудь может мне помочь? Мой код:

 class LibraryViewController: UIViewController {
   private let fetchManager = FetchManager()
   private var tracks: [TrackModelProtocol] = []
   ...
   private let refreshControl: UIRefreshControl = {
        let refreshControl = UIRefreshControl()
        refreshControl.addTarget(self, action: #selector(refreshTableView), for: .valueChanged)
        return refreshControl
    }()
   ...
   override func viewDidLoad() {
        super.viewDidLoad()
        ...
        getTracks()
        setupTableView()
        tableView.refreshControl = refreshControl
    }
   ...
   private func getTracks() {
        guard let tracks = fetchManager.setupTrackModels() else { return }
        self.tracks = tracks
    }
    
   //pull to refresh
   @objc private func refreshTableView(sender: UIRefreshControl) {
        getTracks()
        tableView.reloadData()
        sender.endRefreshing()
   }
   ...
}
...
class FetchManager {
   var content: [URL] = []
    
    private var currentDirectory : URL?
    private var playerQueue: [AVPlayerItem] = []
    
    private let audioPlayer = AudioPlayer()
    
    private func fetchDataFromDeviceMemory() -> [URL]? {
        var content: [URL] = []
        
        if currentDirectory == nil {
            currentDirectory = try! FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
        }
        do {
            let allURLs = try FileManager.default.contentsOfDirectory(at: currentDirectory!, includingPropertiesForKeys: nil, options: [.skipsHiddenFiles, .skipsPackageDescendants])
            content = allURLs.filter{ $0.pathExtension == "mp3" || $0.pathExtension == "aac" || $0.pathExtension == "wav" }
        } catch {
            content = []
        }
    
        return content
    }
    
    func setupTrackModels() -> [TrackModelProtocol]? {
        guard let content = fetchDataFromDeviceMemory() else { return [] }
        self.content = content
        playerQueue = audioPlayer.createPlayerQueue(from: content)
        var tracks: [TrackModelProtocol] = []
        
        for index in 0..<playerQueue.count {
            
            var artwork: UIImage?
            
            let playerItem = playerQueue[index]
            let metadataList = playerItem.asset.metadata
            
            for item in metadataList {
                guard let key = item.commonKey?.rawValue, let value = item.value else{
                    continue
                }
                
                switch key {
                case "artwork" where value is Data:
                    artwork = UIImage(data: value as! Data)
                default:
                    continue
                }
            }
            
            let track = TrackFromDeviceMemory(
                fileName: content[index].lastPathComponent,
                duration: playerQueue[index].asset.duration.seconds,
                artwork: artwork,
                url: content[index].absoluteURL)
            
            tracks.append(track)
        }
        
        return tracks
    }
}



 func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
        
        if editingStyle == .delete {
            
            if isSearchActive {
                let deletedTrackURL = filteredTracks[indexPath.row].url
                print(deletedTrackURL)
                do {
                    try FileManager.default.removeItem(at: deletedTrackURL)
                } catch {
                    print(error.localizedDescription)
                }
                
                getTracks()
                searchController.isActive = false
//                tableView.reloadData()
                tableView.delegate?.tableView!(tableView, didSelectRowAt: IndexPath(row: 0, section: indexPath.section))
                
                
            } else {
                let deletedTrackURL = tracks[indexPath.row].url
                print(deletedTrackURL)
                do {
                    try FileManager.default.removeItem(at: deletedTrackURL)
                } catch {
                    print(error.localizedDescription)
                }
                
                getTracks()
                tableView.deleteRows(at: [indexPath], with: .automatic)
//                tableView.reloadData()
  

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

1. IMO было бы лучше переместить метод tableView.reloadData() and sender.endRefreshing() в getTracks() после получения ответа от API. попробуйте это и посмотрите, работает ли это.

2. Я сделал, как вы сказали, переместил TableView.reloadData () и TableView.refreshControl.endRefreshing() в функцию getTracks (), но это не помогает

3. Мне кажется, что проблема заключается в получении дорожек из памяти устройства

4. Вы пробовали использовать DispatchQueue.main ?

5. Что именно я должен поместить в основную очередь?

Ответ №1:

Вместо возврата массива с помощью setupTrackModels вы пробовали использовать обработчик завершения и выполнить выборку из глобальной очереди? Само выполнение выборки в основной очереди может быть причиной зависания.

 func setupTrackModels(completion: (_ models: [TrackModelProtocol])->Void) {
    DispatchQueue.global().async {
        guard let content = fetchDataFromDeviceMemory() else {
            DispatchQueue.main.async {
                completion([])
                return
            }
        }
        self.content = content
        playerQueue = audioPlayer.createPlayerQueue(from: content)
        var tracks: [TrackModelProtocol] = []
        
        for index in 0..<playerQueue.count {
            
            var artwork: UIImage?
            
            let playerItem = playerQueue[index]
            let metadataList = playerItem.asset.metadata
            
            for item in metadataList {
                guard let key = item.commonKey?.rawValue, let value = item.value else{
                    continue
                }
                
                switch key {
                case "artwork" where value is Data:
                    artwork = UIImage(data: value as! Data)
                default:
                    continue
                }
            }
            
            let track = TrackFromDeviceMemory(
                fileName: content[index].lastPathComponent,
                duration: playerQueue[index].asset.duration.seconds,
                artwork: artwork,
                url: content[index].absoluteURL)
            
            tracks.append(track)
        }
        DispatchQueue.main.async {
            completion(tracks)
        }
    }
}
  

На сайте вызова:

         fetchManager.setupTrackModels { tracks in
            refreshControl.endRefreshing()
            self.tracks = tracks
            tableView.reloadData()
        }
  

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

1. Это работает, но теперь, когда я удаляю строку, приложение вылетает. В функции … зафиксируйте стиль редактирования… Я использую … Просмотр таблицы.делегировать?.Просмотр таблицы! (TableView, didSelectRowAt… И asyncAfter не помогает

2. Я добавил к основному вопросу

3. Происходит ли сбой при getTracks() или tableView.delegate?.tableView!(tableView, didSelectRowAt ? что за ошибка выводится в консоли?

4. Ошибка: Завершение работы приложения из-за неперехваченного исключения ‘NSInternalInconsistencyException’, причина: ‘Недопустимое обновление: недопустимое количество строк в разделе 0. Количество строк, содержащихся в существующем разделе после обновления (29), должно быть равно количеству строк, содержащихся в этом разделе до обновления (29), плюс или минус количество строк, вставленных или удаленных из этого раздела (0 вставлено, 1 удалено), и плюс или минус количество строк, перемещенных в этот раздел или из него (0 перемещено, 0 удалено)

5. При перезагрузке данных происходит сбой