来自父 TableView 的 `UITableViewCell` 调用函数

`UITableViewCell` call function from parent TableView

我有一个 UI 小部件如下

class ProfileBubbleCell: UITableViewCell {

    var roundImageView: UIImageView?

    override func awakeFromNib() {

        super.awakeFromNib()
        self.contentView.backgroundColor = Color.red
        initImage()
    }

    private func initImage(){

        let imageView = UIImageView()

        let width = self.frame.width
        let height = self.frame.height

        let img_width  = height - 4
        let img_height = img_width

        let y = 2
        let x = width/2 - img_width/2

        imageView.frame = CGRect(
              x: x, y: CGFloat(y), width: img_width, height: img_height
        )

        let rounded = imageView
            .makeRounded()
            .border(width:2.0, color:Color.white.cgColor)

        // attach and save reference
        self.addSubview(rounded)
        self.roundImageView = rounded
    }

    private func loadImage(){
    // @TODO: call parent function

    }
}

而在loadImage中,我想调用父级的图片加载视图,当图片加载完成后,显示在roundImageView中。 ProfileBubbleCell 确实意味着尽可能通用,它唯一关心的是使图像圆润并居中。

这看起来是一个非常常见的用例,我想 delegate 将加载图像任务交给父级,但我不确定如何表达它。

在父级中,我按如下方式实例化单元格:

let cell = tableView.dequeueReusableCell(withIdentifier: "ProfileBubbleCell", for: indexPath) as! ProfileBubbleCell

在这里向您展示一些有关委托的使用。

// 1) define delegate.
protocol ProfileBubbleCellDelegate { 
  func getImage() -> UIImage?
}


class ProfileBubbleCell: UITableViewCell { 

  // 2) declare a variable of ProfileBubbleCellDelegate
  weak var delegate: ProfileBubbleCellDelegate?

  //  
  func configure() {
    self.roundImageView.image = delegate.getImage()
  }
}

// when dequeueReuseCell on cellForRow(at:)
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {  
  guard let cell  = tableView.dequeueReusableCell(withIdentifier: "ProfileBubbleCell", for: indexPath) as ProfileBubbleCell else { return UITableView() }
  // 3) assign delegate to tableView's superView, maybe it's a UIViewController or UIView on your class.
  cell.delegate = self
  cell.configure()
  return cell
}

// 4) extension Your class who be assigned delegate of ProfileBubbleCellDelegate
//    and implement delegate's method.
extension YourClass: ProfileBubbleCellDelegate { 
  func getImage() -> UIImage? { 
    // 5) provide image.
    return hereProvideYourImage
  }
}

// or if you want immediately download image when cell.roundedImageView need it.
// modify the getImage() of Delegate like this.
protocol ProfileBubbleCellDelegate { 

  func getImage(completion: @escaping ((UIImage?) -> Void))
}

// then the extension implement will be 
extension YourClass: ProfileBubbleCellDelegate { 
  func getImage(completion: @escaping ((UIImage?) -> Void)) { 
    downloadImageTask.downloadImage(url: imageUrl, completion: { downloadedImage in  
      // because completion will be passed on other closure of downloadImage(completion:), 
      //   so delegate method need add `@escaping` that means the completion can escape from itself closure.
      completion?(downloadedImage)
     })
  }
}

// don't forget replace called method on cell. 
class ProfileBubbleCell: UITableViewCell { 
  // ...   
  func configure() { 
    delegate.getImage(completion: { downloadedImage in 
      DispatchQueue.main.async { 
        self.roundImageView.image = downloadedImage
      }
    })
  }
}