簡體   English   中英

從集合視圖中的可重用單元格中獲取按鈕單擊(XIB文件)

[英]Get button click from reusable cell in collection view (xib file)

我在XIB中創建了一個可重復使用的單元格,其中包含一個按鈕來進行收集。

我可以在集合視圖中更改標簽和按鈕的文本,但無法獲得click事件。

我嘗試了以下選項:

一種。 這在UICollectionViewCell中: 不起作用

class cellVC: UICollectionViewCell {
    @IBOutlet weak var sampleLabel: UILabel!
    @IBOutlet weak var buttonClicked: UIButton!

    @IBAction func buttonTest(_ sender: Any) {
        print("dsfsdf   111111")
    }
}

b。 我也試過了: 不工作

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
{

    let cell : cellVC = collectionView.dequeueReusableCell(withReuseIdentifier: "cellVC", for: indexPath) as! cellVC
    cell.sampleLabel. = "sample text"
    cell.buttonClicked.tag = indexPath.row
    cell.buttonClicked.addTarget(self, action: #selector(masterAction(sender:)), for: .touchUpInside)

    cell.buttonClicked.backgroundColor = UIColor.cyan
    return cell
}


func masterAction(_ sender: UIButton) {
    print ("button click")
}

由於兩種解決方案均不起作用,我該如何實現。

謝謝

檢查的地方

這里有幾個潛在的失敗點,因為您正在做的事情看起來似乎正確。 我有一個可以正常工作的示例Xcode項目。 (在GitHub上查看)。

您可以仔細檢查以下內容,以確保所有設置均正確:

  • 您的收集視圖的數據源和委托已連接
  • 您的按鈕和樣品標簽的插座已正確連接
  • 您的情節提要中已設置了適當的類別和標識符
  • 您的選擇器名稱正確

似乎,根據您在此處發布的內容,上述所有條件都是正確的,因此,讓我們進一步談談您要執行的操作,並了解所有這些條件的適用性。

為什么這些事情看起來還可以

  • 如果數據源和委托設置不正確,您將看不到單元格。
  • 如果您的插座接線錯誤,則會導致崩潰,因為IBOutlets默認情況下是用力展開的。 (這就是您發布的代碼的工作方式。)
  • 您的情節提要板似乎已正確設置,因為同樣,如果沒有正確設置,您的單元將不會出現。
  • 命名錯誤的選擇器甚至無​​法編譯。

細胞和回收的意義

通常,由於UICollectionView的設計方式,這是一個棘手的問題。 (同樣適用於UITableView 。)

您的按鈕位於可重復使用的集合視圖單元中,這意味着您不僅必須處理輕敲,而且還需要知道按鈕當前所指的數據集中的哪個索引路徑。

此外,放置在UICollectionViewCell實例內部的視圖實際上位於集合視圖單元格的contentView ,這使它的處理工作更多了。

最后,如果您的單元格在屏幕上滾動或移出屏幕,那么當單元格被回收時,您可能最終將動作附加到按鈕上多次。 因此,您還需要考慮到這一點。

實施實例

讓我們使用一個非常簡單的自定義單元格,其中只有一個按鈕: 具有單個居中按鈕的集合視圖單元格的圖像。

該按鈕的代碼如下所示:

import UIKit

class CustomCollectionViewCell: UICollectionViewCell {

    @IBOutlet weak var updateButton: UIButton!
}

真的很簡單。 注意IBOutlet 這也需要在界面生成器中進行連接。

Xcode檢查器的圖像,顯​​示了連接到其插座的按鈕

我們需要做的最后一件事是設置單元的重用標識符,以便我們可以在代碼中訪問它。 在“界面生成器”中選擇單元格后,向該單元格添加一個標識符:

Xcode檢查器的屏幕快照,顯示了集合視圖單元格的標識符

好的,這就是您要做的所有工作。 我們准備實現視圖控制器。 在視圖控制器中,我們將進行一些設置:

  1. 標簽,顯示我們在單元格內的按鈕被點擊
  2. 集合視圖的出口,因此我們可以獲得包含按鈕的單元格的索引路徑。 (如果您使用的是UICollectionViewController而不是UIViewController ,則不需UICollectionViewController 。)
  3. 一些代碼來顯示我們的自定義單元格。
  4. 一些用於處理按鈕點擊的代碼。

讓我們從IBOutlets開始:

class ViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate {

    @IBOutlet weak var lastTappedLabel: UILabel!
    @IBOutlet weak var collectionView: UICollectionView!

我們需要在Interface builder中將它們連接起來。 當您使用它時, 還可以連接集合視圖的數據源,如果尚未連接,則委派它們作為“文件的所有者”。 (同樣,如果您使用的是UICollectionViewController而不是UIViewController ,則不必這樣做。)

接下來,讓我們使用UICollectionViewDataSource實現一些代碼以顯示我們的自定義單元格。 我們需要顯示至少一個部分和至少一個單元格:

// MARK: - UICollectionViewDataSource


func numberOfSections(in collectionView: UICollectionView) -> Int {
    return 1
}

func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    return 36
}

當我們使單元出隊時,我們想強制將已出隊的單元向下轉換為我們之前定義的自定義單元類。 然后,我們可以訪問更新按鈕以向其添加操作。

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {

    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "com.mosheberman.samples.cell", for: indexPath) as! CustomCollectionViewCell

    cell.updateButton.addTarget(self, action: #selector(updateLabel(_:)), for: .touchUpInside)

    return cell
}

現在,我們需要實現訪問單元格索引路徑的方法:

// MARK: - Updating the Label

@objc func updateLabel(_ sender:UIButton)
{
    print("\(sender) tapped. Update label called.")

    guard let button = sender as? UIButton else
    {
        print("Sender isn't a button. Returning.")
        return
    }

    guard let contentView = button.superview else
    {
        print("Button isn't inside a contentView. Returning.")
        return
    }

    guard let cell = contentView.superview as? UICollectionViewCell else
    {
        print("View's superview isn't a UICollectionViewCell instance. Returning.")
        return
    }

    guard let collectionView = self.collectionView else
    {
        print("Our collection view isn't wired up. Returning.")
        return
    }

    guard let indexPathForCell = collectionView.indexPath(for: cell) else
    {
        print("The cell doesn't correspond to an index path. Maybe it's not a child of this collection view?")
        return
    }

    self.lastTappedLabel.text = "Tapped button at \(indexPathForCell.item), \(indexPathForCell.section)"
}

此方法處理您需要處理的所有檢查,並確定實際點擊了哪個項目。

  1. 我們需要確保sender實際上是一個UIButton實例。 這不是很關鍵,但是如果我們至少不能確保它是UIView ,我們將無法獲得superview
  2. 一旦有了這些,請確保該按鈕位於superview內部,我們將其假定為contentView
  3. 檢查內容視圖的超級視圖,我們將擁有單元格本身。
  4. 在這一點上,我們可以向集合視圖(由於上面設置了出口,因此引用了集合視圖)中的單元格索引路徑。
  5. 一旦有了索引路徑,就可以更新標簽或對支持單元格的數據進行處理。

筆記

  • 在測試這一點時,我發現您可能實際上不再需要刪除目標動作了-UIKit現在似乎做對了,只調用一次您的方法。
  • 我們在此處編寫的代碼可在GitHub上找到

編輯:

@DonMag指出您使用的是筆尖而不是故事板,而我最初忽略了這個故事板。 因此,這是使用筆尖進行全部設置的過程:

  1. 我們可以使用與上面相同的單元格類,但是我們需要為其添加一個單獨的nib,因為視圖控制器nib不支持單元原型。
  2. 出於相同的原因,我們需要分別注冊筆尖。 在情節提要中包括單元原型將自動執行此操作。 使用筆尖時,您不會免費獲得此功能。
  3. 我們需要在筆尖中設置視圖控制器。
  4. 幾乎所有其他代碼都可以從情節提要版本中移出。

首先,為CustomCollectionViewCell創建一個新的筆尖。 使用“空”模板,創建一個名為“ CustomCollectionViewCell.xib”的新筆尖。 名稱不必與類匹配,但是稍后將使用它,因此為了遵循慣例,我們稱其為。

在新的筆尖中,從右下方的調色板中拖出一個收集視圖單元格,然后向其添加標簽。 將自定義類設置為CustomViewControllerCell (或任何您稱呼的類),然后將標簽連接到插座。

接下來,讓我們制作一個新的View Controller。 我們可能可以重用最初的一個,但是為了不為同一標識符注冊兩個單元格,讓我們繼續使用新的UIViewController子類。 要獲取筆尖,請選中“還創建XIB文件”。

在新的基於筆尖的視圖控制器中,我們希望擁有與以前相同的插座。 我們還想要相同的UICollectionViewDataSource實現。 這里有一點不同:我們需要注冊單元。

為此,我們添加一個稱為registerCollectionViewCell()的方法,該方法將從viewDidLoad調用。 看起來是這樣的:

// MARK: - Setting Up The Collection View Cell

func registerCollectionViewCell()
{
    guard let collectionView = self.collectionView else
    {
        print("We don't have a reference to the collection view.")
        return
    }

    let nib = UINib(nibName: "CustomCollectionViewCell", bundle: Bundle.main) 

    collectionView.register(nib, forCellWithReuseIdentifier: "com.mosheberman.samples.cell")
}

我們的viewDidLoad()方法現在看起來像這樣:

override func viewDidLoad() {
    super.viewDidLoad()

    self.registerCollectionViewCell()

    // Do any additional setup after loading the view.
}

在筆尖內,像以前一樣布置標簽和收藏視圖。 連接出口和收集視圖數據源,並進行委托,我們應該完成!

其他幾件事:

  • 在我的演示項目中,我還需要介紹視圖控制器的基於筆尖的版本。 我在應用程序委托中通過替換應用程序加載到application(_:didFinishLaunchingWithOptions:)程序中的所有主故事板來完成了此任務application(_:didFinishLaunchingWithOptions:)
  • 故事板筆尖應將文件的所有者保留為NSObject的默認值。 該單元應該具有一個自定義類,該類與要加載的任何類匹配。

代替

 cell.buttonClicked.addTarget(self, action: #selector(masterAction(sender:)), for: .touchUpInside)

 cell.buttonClicked.addTarget(self, action: #selector(masterAction(_:)), for: .touchUpInside)

當按鈕鏈接到文件的所有者而不是您的單元格時,有時會發生這種情況。

右鍵單擊IB中的按鈕,並檢查引用出口是否正確,這應該是您的單元格,而不是下圖所示的文件所有者:

在此處輸入圖片說明

您可以選擇:

  class cellVC: UICollectionViewCell { @IBOutlet weak var sampleLabel: UILabel! @IBOutlet weak var buttonClicked: UIButton! var buttonTapAction: ((cellVC) -> ())? @IBAction func buttonTest(_ sender: Any) { buttonTapAction?(self) } } ////////// func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell : cellVC = collectionView.dequeueReusableCell(withReuseIdentifier: "cellVC", for: indexPath) as! cellVC cell.sampleLabel. = "sample text" cell.buttonClicked.tag = indexPath.row cell.buttonTapAction = { cell in //Tap handling logic } cell.buttonClicked.backgroundColor = UIColor.cyan return cell } 

謝謝@Thomas如果有人想要解決方案,那么答案的頂部已經迅速更新:

self.contentView.isUserInteractionEnabled = false

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM