【问题标题】:Fetching images and setting them makes the app freeze using Combine SwiftUI使用 Combine SwiftUI 获取图像并设置它们会使应用程序冻结
【发布时间】:2019-12-23 16:32:25
【问题描述】:

您好,我目前在从 url 获取图像时显示图像时遇到问题。因此,我目前从 Genius API 获取数据,并在获取图像 url 的同时获取所需的信息,但是当我将数据设置为视图时,应用程序会冻结几秒钟,然后它就可以正常工作了。

我通过以下方式获取数据:

class NetworkManager: ObservableObject {
    var objectWillChange = PassthroughSubject<NetworkManager, Never>()
    var imageLoader = ImageLoader()

    var fetchedSongsResults = [SongCardViewModel]() {
        willSet {
            objectWillChange.send(self)
        }
    }

    func fetchSongs(userSearched callback: String) {
        guard let url = URL(string: "https://api.genius.com/search?q=\(callback.replacingOccurrences(of: " ", with: "%20"))") else { return }
        var urlRequest = URLRequest(url: url)
        urlRequest.setValue("Bearer", forHTTPHeaderField: "Authorization")

      URLSession.shared.dataTask(with: urlRequest) {data, response, error in
            guard let data = data else { return }
            let songs = try! JSONDecoder().decode(feed.self, from: data)
            self.imageLoader.fetchSongImages(urlStrings: songs.response.hits.map({ $0.result.imageUrl }))
            DispatchQueue.main.async {
                if self.imageLoader.dataIsValid == true {
                    for song in songs.response.hits {

                        self.fetchedSongsResults.append(SongCardViewModel(Id: song.result.id, ImageURL: self.imageLoader.fetchedImage[0], LyricsUrl: song.result.lyricsUrl, Title: song.result.title, Lyrics_State: song.result.lyrics_state, Primary_Artist: song.result.primary_artist))
                    }
                }
            }
        }.resume()
    }
}

所以,网络管理器就像我的主类,正如您在函数 fetchSongs 中看到的那样,我调用了另一个类 imageLoader,它也是一个获取图像并附加到获取图像的数组,我已经硬编码了 0 索引别介意。这是imageLoader的以下代码

class ImageLoader: ObservableObject {
    @Published var dataIsValid = false
    var fetchedImage = [Data]()

    func fetchSongImages(urlStrings: [String]) {
        for urlString in urlStrings {
            guard let url = URL(string: urlString) else { return }
            let task = URLSession.shared.dataTask(with: url) { data, response, error in
                guard let data = data else { return }
                DispatchQueue.main.async {
                    self.dataIsValid = true
                    self.fetchedImage.append(data)
                }
            }
            task.resume()
        }
    }
}

只是为了清楚显示数据,但是当我设置视图并使用回调触发网络管理器时,我可以看到数据已收到,但是当我尝试向下滚动时,我等了几下才能看到秒我的理论是图像仍在从背景中获取,这就是应用程序冻结的原因,我尝试使用 isDataValid 布尔值来确定何时获取图像,但遗憾的是它没有工作。

这是我如何显示数据的代码

@ObservedObject var networkManager = NetworkManager()

    var body: some View {
           ScrollView {
                    ForEach(self.networkManager.fetchedSongsResults, id: \.title) { song in
                        VStack {
                            NavigationLink(destination: LyricView(url: song.lyricsUrl)) {
                                SongCardView(isSearching: self.isSearching, imageData: song.imageUrl, title: song.title, artist: song.primary_artist.name)

                            }
                        }
                    }
                }

这是我的自定义 SongCardView

truct SongCardView: View {
    var isSearching: Bool
    var imageData: Data
    var title: String
    var artist: String

    var body: some View {
        ZStack(alignment: .topLeading) {
            CustomImageView(ImageData: imageData)
            HStack {
                Text(title)
                    .modifier(SongCardViewTextModifier())
                Text("-")
                    .modifier(SongCardViewTextModifier())
                Text(artist)
                    .modifier(SongCardViewTextModifier())

            }
            .frame(minWidth: 0, maxWidth: 240, minHeight: 0, maxHeight: 30)
            .background(Color.gray.opacity(0.8))
            .padding()
        }
    }
}

最让我好奇的是,当我尝试从 dispatchQueue 中删除 self.isDataValid = true 并且已经在 for 循环之外它不起作用时,我的数组获取的图像变成了空的,我没有知道为什么?。主要目标是停止让应用程序冻结,我的结论是图像是那些让事情变得困难的图像,因为它们在图像之前被获取。感谢您的帮助

这里是 SondCardViewModel:

class SongCardViewModel: Identifiable {
    var id: Int
    var imageUrl: Data
    var lyricsUrl: String
    var title: String
    var lyrics_state: String
    var primary_artist: artist

    init(Id:Int, ImageURL:Data, LyricsUrl:String, Title:String, Lyrics_State:String, Primary_Artist: artist) {
        id = Id
        imageUrl = ImageURL
        lyricsUrl = LyricsUrl
        title = Title
        lyrics_state = Lyrics_State
        primary_artist = Primary_Artist

    }

}

这里还有CustomImageView

import SwiftUI

func imageFromData(_ data:Data) -> UIImage {
    UIImage(data: data) ?? UIImage()
}

struct CustomImageView: View {
    var imageData: Data
    init(ImageData data:Data) {
        imageData = data
    }

    var body: some View {
        // checking for our fetched is not nil if nil then we just use an empty UIImage
        // if we have fetched our image then we set as our image
        Image(uiImage: imageFromData(imageData))
           .renderingMode(.original) 
           .resizable()
           .cornerRadius(30)
           .frame(width: UIScreen.main.bounds.width/1.5, height: UIScreen.main.bounds.width/1.5)
    }
}

【问题讨论】:

  • 和你的 CustomImageView ....?和 SongCardViewModel?
  • @Chris 所以现在包含了 CusomImageView 和 SongCardViewModel
  • 现在艺术家和 lyricview 不见了......请给我们可重现的可运行编码示例......
  • @Chris 抱歉,我认为代码太多了,所以我没有把所有代码都放在这里,因为这会很乱,但是该项目实际上是在 GitHub 上发布的,你可以使用以下链接访问此处github.com/Atheer2104/MusicX
  • @Chris 发现了问题,并且如果您从导航链接中取出歌曲卡视图并注释掉应用程序根本不会冻结的导航链接,则应用程序会因为导航链接而冻结我想如果我不能使用导航链接,那么我可以让图像可点击或添加覆盖整个区域的按钮,也许是一个更好的解决方案

标签: swift swiftui combine


【解决方案1】:

这个问题的解决方案是 NavigationLink 为什么实际上在删除它时让应用程序冻结的那个是例外

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-08-21
    • 2017-01-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-10-02
    • 2021-06-16
    相关资源
    最近更新 更多