异步获取数据并将其放入 UICollectionView 失败 - 集合在数据之前加载并且不更新
fetching data asynchronously and putting it in UICollectionView fails - collection loads before the data and does not update
我的问题与我在此处的其他问题部分相关
在我的应用程序中,我使用 UICollectionView
。每个单元格都包含一张照片和一个用户名。
但首先要做的是 - 我正在从我的网络服务中获取一个 json 文件,其中包含该用户名和 link 一张照片。我将这两个值存储在一个名为 SingleUser
的对象中。看起来像这样:
class SingleUser: NSObject {
let username: String
let photo: String
var image: UIImage? = UIImage()
init(username: String, photo: String) {
self.username = username
self.photo = photo
}
class func fromJSON(json: JSON) -> SingleUser? {
let username = json["username"].string
let photo = json["photo"].string
return SingleUser(username: username!, photo: photo!)
}
}
根据我上一个问题的建议,我修改了我的代码,我的主要 class 和 UICollectionView
看起来如下:
class UserList: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate {
@IBOutlet weak var tview: UICollectionView!
let reuseIdentifier = "cell"
var items = NSMutableArray()
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
fetchAllUsers()
tview.delegate = self
tview.dataSource = self
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = tview.dequeueReusableCellWithReuseIdentifier(reuseIdentifier, forIndexPath: indexPath) as! MyCollectionViewCell
let user:SingleUser = self.items[indexPath.item] as! SingleUser
cell.username.text = user.name
if user.image == nil{
if let checkedUrl = NSURL(string: user.photo) {
cell.userImg.contentMode = .ScaleAspectFit
getDataFromUrl(checkedUrl) { (data, response, error) in
dispatch_async(dispatch_get_main_queue()) { () -> Void in
guard let data = data where error == nil else { return }
let image = UIImage(data: data)
user.image = image
if collectionView.cellForItemAtIndexPath(indexPath) == cell
{
cell.userImg.image = image
}
}
}
}else{
cell.userImg.image = user.image
}
return cell
}
所以现在 - 希望 - 每次用户滚动视图时数据都不会改变。但这里有一个主要问题——当用户进入面板时,collectionView 变得可见,但所有照片都无法快速加载。所以用户只能看到用户名和照片的地方是空的。
这是因为我使用 viewWillAppear
中调用的方法 fetchAllUsers
异步加载数据造成的:
func fetchAllUsers(){
Alamofire.request(.GET, "http://mywebservice")
.responseJSON { response in
switch response.result {
case .Success:
dispatch_async(dispatch_get_main_queue(),{
self.items.removeAllObjects()
if let jsonData = response.result.value as? [[String: AnyObject]] {
for requestJSON in jsonData {
if let request = SingleUser.fromJSON(JSON(requestJSON)){
self.items.addObject(request)
self.tview.reloadData()
}
}
}
self.tview.reloadData()
})
case .Failure(let error):
print("SWITCH ERROR")
print(error)
}
}
}
此外,我在上面使用的另一种异步方法是:
func getDataFromUrl(url:NSURL, completion: ((data: NSData?, response: NSURLResponse?, error: NSError? ) -> Void)) {
NSURLSession.sharedSession().dataTaskWithURL(url) { (data, response, error) in
completion(data: data, response: response, error: error)
}.resume()
}
所以这里有很多异步调用导致在视图出现之前显示缺少数据的问题。
我想避免这种情况,而是 - 例如,当每张照片尚未加载时 - 显示加载指示器,该指示器会在获取照片时消失。我知道可能有必要对我的代码进行大量修改,但我可以在这里向您寻求任何建议吗?谢谢!
实际上,每当下载新数据时,您都需要重新加载收集数据。
执行此操作的最佳位置可能是在 "getDataFromURL" 的闭包中,在此闭包中执行以下操作:
dispatch_async(dispatch_get_main_queue()) {
tview.reloadData()
}
要获得加载图像,只需将您需要的图像放在用户图像后面的原型单元格中。在显示用户图像之前,这将是可见的。
我的问题与我在此处的其他问题部分相关
在我的应用程序中,我使用 UICollectionView
。每个单元格都包含一张照片和一个用户名。
但首先要做的是 - 我正在从我的网络服务中获取一个 json 文件,其中包含该用户名和 link 一张照片。我将这两个值存储在一个名为 SingleUser
的对象中。看起来像这样:
class SingleUser: NSObject {
let username: String
let photo: String
var image: UIImage? = UIImage()
init(username: String, photo: String) {
self.username = username
self.photo = photo
}
class func fromJSON(json: JSON) -> SingleUser? {
let username = json["username"].string
let photo = json["photo"].string
return SingleUser(username: username!, photo: photo!)
}
}
根据我上一个问题的建议,我修改了我的代码,我的主要 class 和 UICollectionView
看起来如下:
class UserList: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate {
@IBOutlet weak var tview: UICollectionView!
let reuseIdentifier = "cell"
var items = NSMutableArray()
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
fetchAllUsers()
tview.delegate = self
tview.dataSource = self
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = tview.dequeueReusableCellWithReuseIdentifier(reuseIdentifier, forIndexPath: indexPath) as! MyCollectionViewCell
let user:SingleUser = self.items[indexPath.item] as! SingleUser
cell.username.text = user.name
if user.image == nil{
if let checkedUrl = NSURL(string: user.photo) {
cell.userImg.contentMode = .ScaleAspectFit
getDataFromUrl(checkedUrl) { (data, response, error) in
dispatch_async(dispatch_get_main_queue()) { () -> Void in
guard let data = data where error == nil else { return }
let image = UIImage(data: data)
user.image = image
if collectionView.cellForItemAtIndexPath(indexPath) == cell
{
cell.userImg.image = image
}
}
}
}else{
cell.userImg.image = user.image
}
return cell
}
所以现在 - 希望 - 每次用户滚动视图时数据都不会改变。但这里有一个主要问题——当用户进入面板时,collectionView 变得可见,但所有照片都无法快速加载。所以用户只能看到用户名和照片的地方是空的。
这是因为我使用 viewWillAppear
中调用的方法 fetchAllUsers
异步加载数据造成的:
func fetchAllUsers(){
Alamofire.request(.GET, "http://mywebservice")
.responseJSON { response in
switch response.result {
case .Success:
dispatch_async(dispatch_get_main_queue(),{
self.items.removeAllObjects()
if let jsonData = response.result.value as? [[String: AnyObject]] {
for requestJSON in jsonData {
if let request = SingleUser.fromJSON(JSON(requestJSON)){
self.items.addObject(request)
self.tview.reloadData()
}
}
}
self.tview.reloadData()
})
case .Failure(let error):
print("SWITCH ERROR")
print(error)
}
}
}
此外,我在上面使用的另一种异步方法是:
func getDataFromUrl(url:NSURL, completion: ((data: NSData?, response: NSURLResponse?, error: NSError? ) -> Void)) {
NSURLSession.sharedSession().dataTaskWithURL(url) { (data, response, error) in
completion(data: data, response: response, error: error)
}.resume()
}
所以这里有很多异步调用导致在视图出现之前显示缺少数据的问题。
我想避免这种情况,而是 - 例如,当每张照片尚未加载时 - 显示加载指示器,该指示器会在获取照片时消失。我知道可能有必要对我的代码进行大量修改,但我可以在这里向您寻求任何建议吗?谢谢!
实际上,每当下载新数据时,您都需要重新加载收集数据。
执行此操作的最佳位置可能是在 "getDataFromURL" 的闭包中,在此闭包中执行以下操作:
dispatch_async(dispatch_get_main_queue()) {
tview.reloadData()
}
要获得加载图像,只需将您需要的图像放在用户图像后面的原型单元格中。在显示用户图像之前,这将是可见的。