2017-05-11 6 views
1

各セルに画像があるカスタムセルでtableViewを作成しました。モデルクラスの画像をテーブルビューで表示することはできません

は、私は、URLからデータを取得し、完了ハンドラブロックにUIImageにデータを変換し、その後、UIImageの配列の変数に画像を追加するURLSession dataTaskメソッドを使用するfunc mainPulatesData()を作成しました。

データを取り出してUIImageアレイに追加するプロセスは、DispatchQueue.global(qos: .userInitiated).asyncブロックで実行されました。印刷メッセージに基づいて、画像がアレイに追加された。

しかし、テーブルビューコントローラでモデルクラスのインスタンスを作成してmainPulatesData()viewDidlLoadに呼び出しても、その画像はテーブルに表示されませんでした。

テーブルビューコントローラクラスの他の印刷メッセージに基づいて、モデルクラスの配列に追加することもできますが、tableViewコントローラのモデルクラスのインスタンスでは動作しないようです。

func mainPulatesData() { 
    let session = URLSession.shared 
    if myURLs.count > 0{ 
     print("\(myURLs.count) urls") 
     for url in myURLs{ 
      let task = session.dataTask(with: url, completionHandler: { (data,response, error) in 
       let imageData = data 
       DispatchQueue.global(qos: .userInitiated).async { 
        if imageData != nil{ 
         if let image = UIImage(data: imageData!){ 
          self.imageList.append(image) 
          print("\(self.imageList.count) images added.") 
         } 
        } 
        else{ 
         print("nil") 
        } 
       } 
      }) 
      task.resume() 
     } 
    } 
} 

モデルのインスタンスを作成するためのビューコントローラのコードです:

override func viewDidLoad() { 
    super.viewDidLoad() 
    myModel.mainPulatesURLs() 
    myModel.mainPulatesData() 
    loadImages() 
} 

private func loadImages(){ 
    if myModel.imageList.count > 0{ 
     tableView.reloadData() 
    } 
    else{ 
     print("data nil") 
    } 
} 

override func numberOfSections(in tableView: UITableView) -> Int { 
    return 1 
} 

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 
    return myModel.imageList.count 
} 

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 
    let cell = tableView.dequeueReusableCell(withIdentifier: "imageCell", for: indexPath) as! ImageTableViewCell 
    if myModel.imageList.count > 0{ 
     let image = myModel.imageList[indexPath.row] 
     cell.tableImage = image 
     return cell 

    } 
    return cell 
} 
+0

これは遅延読み込みの概念です。これについては、SDWebImage(サードパーティのライブラリ) –

答えて

0

reloadData()の後にcellForRowAtが呼び出されたときに画像またはimageListが準備完了になっていないのが原因です。

最初はプレースホルダ画像を使用し、一度にすべてではなく表ビューのセルが表示されている場合のみ画像をロードすることをお勧めします。何かのように:

// VC class 
private var modelList = [MyModel(url: url1), MyModel(url: url2), ...] 

override func numberOfSections(in tableView: UITableView) -> Int { 
    return 1 
} 

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 
    return modelList.count 
} 

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 
    let cell = tableView.dequeueReusableCell(withIdentifier: "imageCell", for: indexPath) as! ImageTableViewCell 
    cell.update(model: modelList[indexPath.row]) 
    return cell 
} 

// Cell class 
@IBOutlet weak var cellImageView: UIImageView! 

func update(model: MyModel) { 
    model.fetchImage(callback: { image in 
     self.cellImageView.image = image 
    }) 
} 

// Model class 
final class MyModel: NSObject { 
    let url: URL 
    private var _imageCache: UIImage? 

    init(url: URL) { 
    self.url = url 
    } 

    func fetchImage(callback: @escaping (UIImage?) -> Void) { 
    if let img = self._imageCache { 
     callback(img) 
     return 
    } 

    let task = URLSession.shared.dataTask(with: self.url, completionHandler: { data, _, _ in 
     if let imageData = data, let img = UIImage(data: imageData) { 
     self._imageCache = img 
     callback(img) 
     } else { 
     callback(nil) 
     } 
    }) 
    task.resume() 
    } 
} 
+0

あなたの提案に感謝しますが、モデルクラスを使用するように求められますコントローラーはデータについて何も知ることができません。この場合、私は何をすべきですか? – CEz

+0

@CEz私は自分の答えを更新しました。あなたが望むものが欲しいと願っています。 – xiangxin

+0

ありがとう!たくさん助けてくれる! – CEz

0

あなたの一連のノートを取る必要があり、画像データを得るためにモデルクラスのコードです

関数呼び出し:

myModel.mainPulatesURLs() --> populates myURLs 
myModel.mainPulatesData() --> populates image from myURLs in forloop asynchronously. 
loadImages() --> called asynchronously. 

から画像をロードしています。あなたはすでにloadImages()と呼ばれていますが、それでもmyModel.imageListはまだ空です。

myModel.mainPulatesData()からコールバックした後、または既に読み込まれている画像が確実である場合は、loadImages()に電話する必要があります。

dispatch_group_tを使用してコールバックを構成できます。ここ

要求されるように:あなたがバックグラウンドスレッド(非同期)でそれらをダウンロードするため

import UIKit 


var myURLs: [String] = ["urlA", "urlB", "urlC", "urlD"] 

// we define the group for our asynchronous fetch. also works in synchronous config 
var fetchGroup = DispatchGroup() 

for urlString in myURLs { 

    // for every url fetch we define, we will call an 'enter' to issue that there is a new block for us to wait or monitor 
    fetchGroup.enter() 

    // the fetch goes here 
    let url = URL(string: urlString)! 
    URLSession.shared.downloadTask(with: URLRequest(url: url), completionHandler: { (urlReq, urlRes, error) in 

     // do your download config here... 

     // now that the block has downloaded the image, we are to notify that it is done by calling 'leave' 
     fetchGroup.leave() 
    }).resume() 
} 

// now this is where our config will be used. 
fetchGroup.notify(queue: DispatchQueue.main) { 

    // reload your table here as all of the image were fetched regardless of download error. 
} 
+0

を使用して、 'dispatch_group_t'についてもっと教えてください。 – CEz

+0

@CEz私は、要求されたコメントを含むサンプルを追加しました。また、あなたの参照はここにあります:https://developer.apple.com/reference/dispatch/dispatch_group_t – nferocious76

0

画像が表示されていない、とloadImages()が同期的に呼び出されます。つまり、myModel.mainPulatesData()が実行される前にloadImage()が呼び出されたため、画像がダウンロードされるとtableviewは更新されません(reloadData()は呼び出されません)。 UIViewControllerにそのデータがダウンロードされたことを通知するにはProtocolを作成するか、Completion Handlerを使用する必要があります。

私が使用していますハンドラの単純な例は、私は私のviewDidLoadでこれを呼び出し、それがサーバーからデータを要求し、[Reservation]?

ReservationTableModule.requestAllReservations { [weak self] reservations in 
      guard let `self` = self else { 
       return 
      } 

      guard let `reservations` = `reservations` else { 
       return 
      } 
      self.reservations = reservations 
      .reservationsTableView.reloadData() 
     } 

の配列を返すこれは

class func requestAllReservations(handler: @escaping ([Reservation]?) -> Void) { 

    let url = "reservations/all" 

    APIModel.shared.requestWithLocation(.post, URL: url, parameters: nil) { data in 
     let reservations = data?["reservations"].to(type: Reservation.self) as? [Reservation] 
     handler(reservations) 
    } 
} 

handler: @escaping ([Reservation]?) -> Void実際の要求機能であります完了ハンドラと呼ばれていますが、私はそれをhandler: @escaping ([UIImage]?) -> Voidとし、データをダウンロードした後にしてください。handler(reservations)

+0

いくつかのサンプルコードを与えることができますか?私はプロトコルと完了ハンドラに精通していません。 btw、それはモデルクラスのデータを処理するように求められ、コントローラはデータについて何も知ってはいけません。 – CEz

+0

@CEzは – JuicyFruit

+0

という例を追加したので、 'class func requestAllReservations'メソッドで' ReservationTableModule'というクラスを作成する必要がありますか? 'viewDidLoad()'でそれを呼び出しますか? – CEz

関連する問題