xiaolingxiao
xiaolingxiao

Reputation: 4895

`UITableViewCell` call function from parent TableView

I have a UI widget as follows

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

    }
}

And in loadImage, I would like to call the parent's image loading view, and when the image is loaded, display it in roundImageView. ProfileBubbleCell is really meant to be as generic as possible, its only concern is making the image round and centering it.

This looks like a very common use case and I would like to delegate the loading image task to the parent, but I am not sure how to express it.

In the parent I instantiate the cell as follows:

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

Upvotes: 0

Views: 691

Answers (1)

Kin Wei
Kin Wei

Reputation: 96

Here show you some about delegate use.

// 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
      }
    })
  }
}

Upvotes: 3

Related Questions