iOS WatchKit / Swift - 在WKInterfaceTable行中实现延迟加载图片

3
在我的Apple Watch应用中,我有一个表格,大约有25行,每一行都有一些文本和需要从互联网加载的图片。类似于Instagram风格的feed,但这些是每个图片大小约为8k的个人资料图片。
当我基于传入的JSON数据构建表格时,我正在正确地执行所有操作,利用WatchKit内置的图像缓存来减少不必要的网络流量。
应用程序“工作”,并且图像正确显示。但问题在于视图需要10-20秒才能准备好与用户交互。在这10-20秒内,滚动很卡顿,并且按钮在所有图像加载完成之前无法使用。
我希望有一种方法可以在用户向下滚动时延迟加载图片。
我已经尝试过实现分页解决方案,但这也有其缺点,而且并不能完全解决我的原始问题。
如果我只填充文本部分(没有图片),则表格会立即显示并准备好与用户交互。
***更新: Mike Swanson的答案非常详尽,并且解决了我的大部分问题。
我想为任何遇到类似问题的人发布最终代码。具体来说,我想展示如何实现后台派发。
现在我在主线程中暂时将图片设置为占位符以显示活动状态。一旦实际图片加载并传输到手表中,它就会被替换。我还改进了图像缓存密钥,以避免新图像已上传时缺失。
新代码:
let qualityOfServiceClass = QOS_CLASS_BACKGROUND
let backgroundQueue = dispatch_get_global_queue(qualityOfServiceClass, 0)

i = 0
for p in self.profiles {
    if let profileId = p["id"] as? NSNumber {
        var profileIdStr = "\(profileId)"
        let row = self.profilesTable.rowControllerAtIndex(i) as! WatchProfileRow

        if let hasImage = p["has_image"] as? NSNumber {
            let profileImageCacheKey = "\(profileIdStr)-\(hasImage)"

            // if the image is already in cache, show it
            if self.device.cachedImages[profileImageCacheKey] != nil {
                row.profileImageThumbnail.setImageNamed(profileImageCacheKey)

                // otherwise, get the image from the cdn and cache it
            } else {
                if let imageHost = self.imageHost as String! {
                    var imageURL = NSURL(string: NSString(format: "%@%@-thumbnail?version=%@", imageHost, profileId, hasImage) as String)

                    var imageRequest = NSURLRequest(URL: imageURL!)

                    NSURLConnection.sendAsynchronousRequest(imageRequest, queue: NSOperationQueue.mainQueue()) {
                        (response, data, error) -> Void in
                        if error == nil {
                            row.profileImageThumbnail.setImageNamed("profile-placeholder")

                            dispatch_async(backgroundQueue, {
                                self.device.addCachedImageWithData(data, name: profileImageCacheKey)

                                dispatch_async(dispatch_get_main_queue(), { () -> Void in
                                    row.profileImageThumbnail.setImageNamed(profileImageCacheKey)
                                })
                            })
                        }
                    }
                } else {
                    row.profileImageThumbnail.setImageNamed("profile-placeholder")
                }
            }
        } else {
            row.profileImageThumbnail.setImageNamed("profile-placeholder")
        }

    }

    i++
}

ORIGINAL CODE:

i = 0
for p in self.profiles {
    if let profileId = p["id"] as? NSNumber {
        var profileIdStr = "\(profileId)"
        let row = self.profilesTable.rowControllerAtIndex(i) as! WatchProfileRow

        if let hasImage = p["has_image"] as? NSNumber {

            // if the image is already in cache, show it
            if self.device.cachedImages[profileIdStr] != nil {
                row.profileImageThumbnail.setImageNamed(profileIdStr)

            // otherwise, get the image from the cdn and cache it
            } else {
                if let imageHost = self.imageHost as String! {
                    var imageURL = NSURL(string: NSString(format: "%@%@-thumbnail?version=%@", imageHost, profileId, hasImage) as String)

                    var imageRequest = NSURLRequest(URL: imageURL!)

                    NSURLConnection.sendAsynchronousRequest(imageRequest, queue: NSOperationQueue.mainQueue()) {
                        (response, data, error) -> Void in
                        if error == nil {
                            var image = UIImage(data: data)
                            row.profileImageThumbnail.setImage(image)

                            self.device.addCachedImage(image!, name: profileIdStr)
                        }
                    }
                } else {
                    row.profileImageThumbnail.setImageNamed("profile-placeholder")
                }
            }
        } else {
            row.profileImageThumbnail.setImageNamed("profile-placeholder")
        }   
    }        
    i++
}
1个回答

5

首先,在WatchKit中没有一种方法可以确定WKInterfaceTable中的当前位置。这意味着你使用的任何逻辑都必须围绕这个事实进行。

其次,看起来你正在发送异步网络请求,然后在有数据时使用setImage。在将图像传输到设备之前,setImage将图像编码为PNG格式,并且在你现有的代码中,这是在主线程上发生的。如果你不需要PNG图像,我建议考虑使用setImageData发送JPEG编码数据以进一步减少需要传输到手表的字节数。

最后,似乎你正在双倍传输你的图像。一次使用setImage,一次使用addCachedImage。在接收到图像后,我建议调度到后台线程,然后使用WKInterfaceDeviceaddCachedImageWithData(可以安全地在后台线程中使用)发送数据。然后,在使用setImageNamed设置现在已缓存的图像之前,返回到主线程。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接