เมื่อใดที่จะใช้ dequeueReusableCellWithIdentifier vs dequeueReusableCellWithIdentifier: forIndexPath


167

มีสองเกินพิกัดสำหรับ dequeueReusableCellWithIdentifier และฉันพยายามที่จะตรวจสอบว่าเมื่อใดที่ฉันควรใช้หนึ่ง vs อื่น ๆ ?

เอกสารแอปเปิ้ลเกี่ยวกับสถานะฟังก์ชั่น forIndexPath "วิธีนี้ใช้พา ธ ดัชนีเพื่อดำเนินการกำหนดค่าเพิ่มเติมตามตำแหน่งของเซลล์ในมุมมองตาราง"

ฉันไม่แน่ใจว่าจะตีความได้อย่างไร

คำตอบ:


216

ความแตกต่างที่สำคัญที่สุดคือforIndexPath:เวอร์ชันจะยืนยัน (ล่ม) หากคุณไม่ได้ลงทะเบียนคลาสหรือปลายปากกาสำหรับตัวระบุ รุ่นที่เก่ากว่า (ไม่ใช่ - forIndexPath:) จะส่งคืนnilในกรณีนั้น

คุณลงทะเบียนคลาสสำหรับตัวระบุโดยส่งregisterClass:forCellReuseIdentifier:ไปยังมุมมองตาราง คุณลงทะเบียนไส้ปากกาสำหรับตัวระบุโดยส่งregisterNib:forCellReuseIdentifier:ไปที่มุมมองตาราง

หากคุณสร้างมุมมองตารางและต้นแบบเซลล์ของคุณในสตอรีบอร์ดตัวโหลดสตอรีบอร์ดจะดูแลการลงทะเบียนต้นแบบของเซลล์ที่คุณกำหนดไว้ในสตอรีบอร์ด

เซสชั่น 200 - มีอะไรใหม่ใน Cocoa Touch จาก WWDC 2012กล่าวถึงรุ่น (ใหม่แล้ว) forIndexPath:เริ่มต้นประมาณ 8m30s มันบอกว่า“ คุณจะได้รับเซลล์ที่เริ่มต้นเสมอ” (โดยไม่ต้องพูดถึงว่ามันจะพังถ้าคุณไม่ได้ลงทะเบียนชั้นเรียนหรือปลายปากกา)

วิดีโอบอกว่า“ มันจะมีขนาดที่เหมาะสมสำหรับเส้นทางดัชนีนั้น” สันนิษฐานว่านี่หมายความว่ามันจะกำหนดขนาดของเซลล์ก่อนส่งคืนโดยดูที่ความกว้างของมุมมองตารางและเรียกtableView:heightForRowAtIndexPath:วิธีการของผู้รับมอบสิทธิ์ของคุณ(ถ้ากำหนดไว้) นี่คือสาเหตุที่มันต้องการพา ธ ดัชนี


นั่นเป็นประโยชน์จริงๆขอบคุณ การมีเซลล์ที่มีขนาดเท่าเวลาที่กำหนดนั้นดูเหมือนจะมีข้อได้เปรียบน้อยลงเมื่อมีการปรับขนาดอัตโนมัติ
Benjohn

38

dequeueReusableCellWithIdentifier:forIndexPath:จะเสมอกลับมือถือ มันอาจใช้เซลล์ที่มีอยู่หรือสร้างขึ้นใหม่และส่งคืนหากไม่มีเซลล์

ในขณะที่ดั้งเดิมdequeueReusableCellWithIdentifier:จะคืนค่าเซลล์ถ้ามันมีอยู่นั่นคือถ้ามีเซลล์ที่สามารถนำกลับมาใช้ใหม่ได้ก็จะส่งคืนที่อื่นมันจะส่งกลับศูนย์ ดังนั้นคุณจะต้องเขียนเงื่อนไขเพื่อตรวจสอบnilค่าเช่นกัน

เพื่อตอบคำถามของคุณใช้dequeueReusableCellWithIdentifier:เมื่อคุณต้องการสนับสนุน iOS 5 และรุ่นที่ต่ำกว่าเนื่องจากdequeueReusableCellWithIdentifier:forIndexPathมีเฉพาะใน iOS 6+ เท่านั้น

การอ้างอิง: https://developer.apple.com/library/ios/documentation/uikit/reference/UITableView_Class/Reference/Reference.html#//apple_ref/occ/instm/UITableView/dequeueReuseCellWithIdentifier:forIndexPath :


ไม่มีก็ไม่ได้เสมอกลับเซลล์ 2014/12/26 07: 56: 39.947 testProg [4024: 42920390] *** ความล้มเหลวในการยืนยัน - [UITableView dequeueReusableCellWithIdentifier: forIndexPath:], /SourceCache/UIKit_Sim/UIKit-3318.65/ UITableView.m: 6116 2014-12-26 07: 56: 39.954 เฟสระหว่าง [4024: 42920390] *** แอปสิ้นสุดเนื่องจากข้อยกเว้นที่ไม่ถูกตรวจจับ 'NSInternalInconsistencyException' เหตุผล: 'ไม่สามารถทำการ dequeue เซลล์ด้วยตัวระบุ MyCustomCellIdentifier - ต้องลงทะเบียน nib หรือคลาสสำหรับตัวระบุหรือเชื่อมต่อเซลล์ต้นแบบในสตอรีบอร์ด '
clearlight

@binarystar คุณต้องลงทะเบียนไส้ปากกาหรือคลาสของเซลล์ที่คุณกำหนดเองที่โหลด [self.tableView registerNib:[UINib nibWithNibName:@"cell" bundle:nil] forCellReuseIdentifier:@"cell"];
ไลค์

6

ฉันไม่เคยเข้าใจเลยว่าทำไม Apple สร้างวิธีการใหม่กว่านี้คือ dequeueReusableCellWithIdentifier: forIndexPath: เอกสารของพวกเขาเกี่ยวกับพวกเขายังไม่สมบูรณ์และค่อนข้างทำให้เข้าใจผิด ความแตกต่างเพียงอย่างเดียวที่ฉันสามารถแยกแยะระหว่างสองวิธีคือวิธีเก่านั้นสามารถคืนค่าศูนย์ได้หากไม่พบเซลล์ที่มีตัวระบุส่งผ่านในขณะที่วิธีการใหม่ล้มเหลวหากไม่สามารถคืนค่าได้ เซลล์ ทั้งสองวิธีรับประกันว่าจะคืนเซลล์หากคุณตั้งค่าตัวระบุอย่างถูกต้องและสร้างเซลล์ในกระดานเรื่องราว ทั้งสองวิธีนี้รับประกันว่าจะส่งคืนเซลล์หากคุณลงทะเบียนคลาสหรือ xib และทำให้เซลล์ของคุณเป็นรหัสหรือไฟล์ xib


3
วิธีการใหม่ใช้เส้นทางดัชนีเพื่อกำหนดขนาดที่เหมาะสมสำหรับเซลล์
rob mayoff

1
@robmayoff แต่มันมีความหมายไหม? หากไม่มีวิธีการใหม่ขนาดของเซลล์ยังคงสามารถตั้งค่าได้อย่างถูกต้อง วิธีการใหม่นี้มอบความสะดวกสบายให้หรือไม่?
fujianjin6471

1
อ่านย่อหน้าสุดท้ายของคำตอบของฉันสำหรับรายละเอียด
rob mayoff

นี่หมายความว่าถ้าเซลล์ทั้งหมดของฉันมีขนาดเท่ากันในตารางมันไม่สำคัญว่าฉันจะเรียกวิธีไหน
Happiehappie

2
ถ้าฉันให้tableView.estimateHeightขนาดของเซลล์จะถูกกำหนดอย่างเหมาะสมเช่นกัน ฉันยังไม่ได้รับประโยชน์จากวิธีการใหม่
Ryan

1

สั้น ๆ:

dequeueReusableCell(withIdentifier, for)ใช้งานได้กับเซลล์ต้นแบบเท่านั้น หากคุณพยายามที่จะใช้งานเมื่อไม่มีเซลล์ต้นแบบมันจะทำให้แอปเสียหาย

Hollemans M. 2016, รายการตรวจสอบตอนที่ 2, IOS Apprentice (รุ่นที่ 5) pp: 156


-2

ฉันขอแนะนำให้ใช้ทั้งสองหากคุณใช้เนื้อหาที่สร้างขึ้นแบบไดนามิก มิฉะนั้นแอปของคุณอาจผิดพลาดโดยไม่คาดคิด คุณสามารถใช้ฟังก์ชั่นของคุณเองเพื่อดึงเซลล์ที่นำกลับมาใช้ใหม่ได้ หากเป็นเช่นนั้นnilคุณควรส่งคืนเซลล์ว่างที่มองไม่เห็น:

สวิฟท์ 3

// Extensions to UITableView
extension UITableView
{
    // returns nil, if identifier does not exist. 
    // Otherwise it returns a configured cell for the given index path
    open func tryDequeueReusableCell (
        withIdentifier identifier: String, 
        for indexPath: IndexPath) -> UITableViewCell?
    {
        let cell = self.dequeueReusableCell(withIdentifier: identifier)
        if cell != nil {
            return self.dequeueReusableCell(withIdentifier: identifier, for: indexPath)
        }  
        return nil
    }
}

และส่วนขยายเพื่อส่งคืนเซลล์ว่าง:

// Extension to UITableViewCell
extension UITableViewCell
{
    // Generates an empty table cell that is not visible
    class func empty() -> UITableViewCell
    {
        let emptyCell = UITableViewCell(frame:CGRect(x:0, y:0, width:0, height:0))
        emptyCell.backgroundColor = UIColor.clear
        return emptyCell
    }
}

ตัวอย่างที่สมบูรณ์ของวิธีใช้:

import Foundation
import UIKit

// A protocol is used to identify if we can configure
// a cell with CellData
protocol ConfigureAbleWithCellData
{
    func configure(_ data: CellData)
}

class MyCustomTableViewCell :
    UITableViewCell,
    ConfigureAbleWithCellData
{
    @IBOutlet weak var title:UILabel! = nil
    func configure(_ data: CellData)
    {
        self.title.text = data.title
    }
}

// This actually holds the data for one cell
struct CellData
{
    var title:String = ""
    var reusableId:String = ""
}

class CosmoConverterUnitTableViewController:
    UIViewController,
    UITableViewDelegate,
    UITableViewDataSource
{
    // Storage
    var data = Array<Array<CellData>>()

    func loadData()
    {
        var section1:[CellData] = []
        var section2:[CellData] = []

        section1.append(CellData(title:"Foo", reusableId:"cellType1"))
        section2.append(CellData(title:"Bar", reusableId:"cellType2"))

        data.append(section1)
        data.append(section2)
    }

    func tableView(_ tableView: UITableView,
                   numberOfRowsInSection section: Int) -> Int
    {
        return data[section].count
    }

    public func numberOfSections(in tableView: UITableView) -> Int
    {
        return data.count
    }

    func tableView(
        _ tableView: UITableView,
        cellForRowAt indexPath: IndexPath) -> UITableViewCell {

        guard
            indexPath.row < data[indexPath.section].count
            else
        {
            fatalError("this can't be")
        }

        let cellData = data[indexPath.section][indexPath.row]

        if let cell = tableView.tryDequeueReusableCell(
            withIdentifier: cellData.reusableId,
            for: indexPath)
        {
            if let configurableCell = cell as? ConfigureAbleWithCellData
            {
                configurableCell.configure(cellData)
            }
            else
            {
                // cell is not of type ConfigureAbleWithCellData
                // so we cant configure it.
            }
            return cell
        }
        // id does not exist
        return UITableViewCell.empty()
    }
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.