Pertanyaan UIImageView awalnya tidak menampilkan gambar di UITableViewCell


Saya punya UITableView yang memuat gambar dari layanan. Ini menjemput URL dan mengunduh gambar dengan NSURLSession.sharedSession().dataTaskWithURL:

NSURLSession.sharedSession().dataTaskWithURL(url, completionHandler: { (data, _, error) -> Void in
                            guard
                                let data = data where error == nil,
                                let image = UIImage(data: data)
                                else {
                                    print(error?.localizedDescription)
                                    return
                            }
                            dispatch_async(dispatch_get_main_queue()) {
                                [unowned self] in
                                self.pictureView.image = image
                                self.pictureView.layoutIfNeeded()
                            }
                        }).resume()

Itu UIImageView adalah jalan keluar ke adegan storyboard. Itu UIImageView mode konten adalah Aspect Fit.

Itu UIImageView untuk sel yang terlihat pada awalnya menampilkan kosong (tidak ada gambar) meskipun saya telah mengonfirmasi bahwa gambar telah ditetapkan. Itu tidak sampai sel digulung / layar di luar bahwa sel akan redraw (?) Dan menampilkan gambar.

Jadi ini sepertinya masalah waktu, tapi aku tidak bisa mengetahuinya. Saya sudah mencoba memanggil semua hal berikut di dalam dispatch_async memblokir setelah gambar diatur:

self.pictureView.setNeedsDisplay()
self.pictureView.setNeedsUpdateConstraints()
self.pictureView.setNeedsLayout()
self.pictureView.reloadInputViews()

Tidak ada yang memperbaiki masalah ini. Apa yang menyebabkan gambar ini hanya ditampilkan setelah sel digulir keluar / terlihat?

EDIT: Mungkin penting untuk dicatat bahwa ini terjadi di dalam nib dan kelas terkait. Nib dimuat, lalu saya memulai unduhan. Saya bertanya-tanya apakah nib keluar sendiri, dan pada saat gambar saya diunduh dan diatur pada UIImageView Pass layout telah selesai. Kemudian dengan memindahkan sel (mengandung nib) layar off / on sel melakukan tata letak lagi dan semuanya ditampilkan dengan benar. Tebakan saja, dan aku masih terjebak dalam hal ini.

EDIT 2: Klarifikasi lebih lanjut dari proses:

1) My UIViewController mengandung a UITableView.

2) Ini UITableView memuat sel dari suatu kebiasaan UITableViewCell kelas yang disebut PostCell.

3) Seluruh konten PostCell adalah satu tampilan yang dimuat dari nib yang disebut PostView (dengan kelas PostView). Beban itu terjadi selama init:style:reuseIdentifier di PostCell:

postView = NSBundle.mainBundle().loadNibNamed("PostView", owner: self, options: nil).first as? PostView

4) Saya kemudian mengatur objek Post postView:

postView.post = Post

Perhatikan bahwa ini terjadi SETELAH awakeFromNib() dipanggil di PostView.

5) The post properti di PostView memiliki didSet pengamat yang memulai unduhan gambar berdasarkan URL di post objek (kode di atas). Gambar diunduh dan disetel pada UIImageView bernama pictureView.

Sunting 3:

Ini kode untuk tableView(_:cellForRowAtIndexPath:):

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {

    var cell = tableView.dequeueReusableCellWithIdentifier(postCellIdentifier) as? PostCell

        if cell == nil {
           cell = PostCell()
        }

        if let postCell = cell,
            let postArray = postArray,
            let post = postArray[indexPath.row] {
            postCell.post = post
            return postCell
        }

        // This section is hit during a refresh.
        let emptyCell = UITableViewCell()
        emptyCell.contentView.backgroundColor = .whiteColor()
        return emptyCell

    }

Edit 4:

Saya telah menemukan bahwa jika saya "prima" UIImageView dengan pra-pengaturan gambar placeholder di ujung pena, kemudian memuat gambar saya ke dalam itu UIImageView, gambar akan ditampilkan pada ketinggian placeholder. Ketika saya menarik sel yang offscreen kemudian hidupkan lagi, itu UIImageView akan melukis gambar pada ukuran yang benar menggunakan pengaturan kendala di ujung pena.

Jadi sepertinya kendala dan ukuran UIImageView telah ditetapkan pada saat beban gambar, dan menariknya keluar / di layar menyebabkan redraw dari beberapa jenis yang mengubah ukuran gambar dengan benar. Jika saya bisa memicu pemindaian itu secara terprogram, itu akan membantu, tetapi lihat di atas untuk apa yang sudah saya coba.

Edit 6:

Ini sedang beraksi. Perhatikan bahwa saya telah menetapkan gambar placeholder di ujung pena. Ini tampaknya menyebabkan gambar ditampilkan ketika awalnya dimuat, tetapi pada dimensi placeholder. Menggulirnya ke layar secara singkat akan menyebabkan perubahan ukuran, ketika itu kemudian ditampilkan dengan benar. enter image description here

Edit 7:

Saya menggunakan pena PostView di tempat lain di aplikasi saya tanpa masalah. Ketika memuat gambar berukuran benar. Masalahnya sepertinya hanya menampilkan dirinya dengan nib tertanam dalam UITableViewCell.

Edit 8:

Saya menemukan akar penyebab masalah ini, yang disebut di viewDidLoad dari saya UIViewController:

tableView.estimatedRowHeight = 500
tableView.rowHeight = UITableViewAutomaticDimension

Menggunakan dimensi otomatis menyebabkan tinggi baris yang salah awal. Untuk beberapa alasan, menggulir sel keluar / ke tampilan akan menghitung ulang tinggi baris dengan benar dan gambar ditampilkan pada ketinggian yang tepat. Menghapus kode ini akan menampilkan gambar, tetapi tinggi baris tidak mendapat manfaat dari perhitungan ketinggian otomatis. Apa metode terbaik untuk menghitung tinggi baris secara otomatis dan masih menghindari masalah ini?


14
2018-06-27 16:22


asal


Jawaban:


Anda dapat mencoba ekstensi ini:

extension UIImageView {
    func downloadedFrom(link link:String, contentMode mode: UIViewContentMode) {
        guard
            let url = NSURL(string: link)
            else {return}
        contentMode = mode
        NSURLSession.sharedSession().dataTaskWithURL(url, completionHandler: { (data, response, error) -> Void in
            guard
                let httpURLResponse = response as? NSHTTPURLResponse where httpURLResponse.statusCode == 200,
                let mimeType = response?.MIMEType where mimeType.hasPrefix("image"),
                let data = data where error == nil,
                let image = UIImage(data: data)
                else { return }
            dispatch_async(dispatch_get_main_queue()) { () -> Void in
                self.image = image
            }
        }).resume()
    }
}

Dan kemudian Anda dapat menempatkan baris ini di mana pun Anda mau

cell.cellPhoto.downloadedFrom(link: "customImageUrl", contentMode: .ScaleAspectFit) //Try changing contentMode

4
2017-07-04 15:53



Setelah dequeuing sel untuk tampilan tabel mungkin ada lebar berbeda sel dan tampilan tabel. Pada saat itu sel Anda belum diubah ukurannya secara otomatis. Jadi ubah kode ini setelah dequeuing dari tampilan tabel:

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    ...
    if let postCell = cell,
        let postArray = postArray,
        let post = postArray[indexPath.row] {

           // set the post with image after the cell is sized correctly 
           dispatch_async(dispatch_get_main_queue()) {
              postCell.post = post
           }
        return postCell
    }

Pastikan Anda telah menyiapkan batasan dengan benar di file sel nib.

Saya sarankan juga untuk menggunakan metode registerNib dalam metode controller viewDidLoad:

tableView?.registerNib(UINib(nibName: "PostCellName", bundle: NSBundle.mainBundle()), forCellReuseIdentifier: postCellIdentifier)

2
2017-07-04 14:58



Sudahkah kau melihat Sesi WWDC 2014 226 ? Video ini mungkin memberi lebih banyak cahaya pada masalah Anda.

Dalam sesi WWDC yang mereka gunakan self.tableView.estimatedRowHeight = 100.0;  yang telah Anda lakukan dan kemudian mereka mengatakan Anda tidak memerlukan jenis sel prototip boneka dari pengontrol tampilan tabel.

Masalahnya kemudian adalah bahwa ketika storyboard memuat tampilan tabel itu set rowHeight properti ke nilai dari pembangun xib / antarmuka. Untuk menggunakan sel-sel auto-sizing Anda perlu mengatur estimatedRowHeight dan pergi rowHeight untuk apa yang seharusnya menjadi default - UITableViewAutomaticDimension. (yang telah Anda lakukan)

self.tableView.rowHeight = UITableViewAutomaticDimension

️ Masalah baru adalah bahwa sel pertama memuat sebelum Anda memiliki tinggi baris yang bonafide. Triknya mungkin untuk memaksa me-reload meja ketika tampilan ditampilkan?

[super viewDidAppear:animated]
[self.tableView reloadData]

Jika ini tidak berhasil maka mungkin Anda tidak sengaja mengatur eksplisit contentWidth / Height pada sesuatu yang menyebabkannya tidak diukur dengan benar


2
2017-07-07 19:19



Anda perlu memuat ulang data tampilan tabel Anda setelah unduhan selesai. coba telepon self.tableview.reloadData() dalam dispatch_async blok

Dan juga jika Anda ingin mengunduh gambar dari URL secara asinkron, saya sarankan untuk menggunakannya Burung pekakak


1
2017-07-05 03:06



Setelah melihat pengeditan (8 saat ini) dan pembaruan dari responden Anda, sepertinya Anda mengalami kesalahan yang sama dalam metode init yang menyebabkan kesalahan dalam init disebutkan di sini.

Kredit harus diberikan kepada Prcela berada di jalur yang benar dengan mendaftarkan nib, tetapi solusi yang lebih elegan disediakan di bawah ini:

Cara termudah (tersedia sejak iOS 5.0) untuk membuat sel tampilan tabel khusus dalam file nib adalah menggunakan registerNib: forCellReuseIdentifier: di pengontrol tampilan tabel. Keuntungan besar adalah dequeueReusableCellWithIdentifier: maka secara otomatis instantiates sel dari file nib jika diperlukan. Anda tidak perlu bagian if (cell == nil) ... lagi.

Pada Pengontrol Tampilan Tabel di bawah viewDidLoad

[self.tableView registerNib:[UINib nibWithNibName:@"CueTableCell" bundle:nil] forCellReuseIdentifier:@"CueTableCell"];

Di cellForRowAtIndexPath menambahkan

CueTableCell *cell = [tableView dequeueReusableCellWithIdentifier:@"CueTableCell"];
// setup cell
return cell;

Sel yang diambil dari file nib diinstansiasi menggunakan initWithCoder, Anda   dapat mengesampingkan itu di subclass Anda jika perlu. Untuk modifikasi   elemen UI, Anda harus mengganti awakeFromNib (jangan lupa   panggil "super").

Saya berharap saya dapat mengambil kredit untuk solusi elegan seperti itu, tetapi kredit itu milik Martin R dari pos yang sama.


0
2017-07-07 23:31