I have custom cell of tableview which is used as header as well as normal cell.
In custom cell I have this properties
public var btnReplyTappedClousre:((CommentCell) -> (Void))?
public var btnBulbTappedClousre:((CommentCell) -> (Void))?
public var btnOtherReplyTapped:((CommentCell) -> (Void))?
And Call it with
@IBAction func btnReplyTapped(_ sender: Any) {
self.btnReplyTappedClousre?(self)
}
//--------------------------------------------------------------------------------
@IBAction func btnBulbTapped(_ sender: Any) {
self.btnBulbTappedClousre?(self)
}
//--------------------------------------------------------------------------------
@IBAction func btnViewPreviousReplyTapped(_ sender: Any) {
self.btnOtherReplyTapped?(self)
}
In view Controller i have implemented that closure
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: StoryBoard.Cells.CommentCell, for: indexPath) as! CommentCell
cell.isForHeaderCell = false
cell.btnReplyTappedClousre = {[weak self] (cell) in
self?.btnViewAllReplyTapped(cell)
}
cell.btnBulbTappedClousre = {[weak self] (cell) in
self?.btnBulbTapped(cell)
}
return cell
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let cell = tableView.dequeueReusableCell(withIdentifier: StoryBoard.Cells.CommentCell) as! CommentCell
cell.isForHeaderCell = true
cell.btnReplyTappedClousre = {[weak self] (cell) in
self?.btnViewAllReplyTapped(cell)
}
cell.btnBulbTappedClousre = {[weak self] (cell) in
self?.btnBulbTapped(cell)
}
cell.btnReplyTappedClousre = {[weak self] (cell) in
self?.btnReplyTapped(cell)
}
return cell
}
Is there any better way to do it ? Or can i improve it ?
1 Answer 1
Personally I never mutate the cell inside the cellForRowAt
func but I send it a bunch of things that it needs to (re)build itself through a configure
function. So:
cell.configure(with: isForHeaderCell, whenA: { /* someblock */ }, whenB: { /* another block */ }) /* etcetera */
You seem to be forwarding a lot of calls that are doing nothing except forwarding. So the only thing you really need to do is to forward the function you want to call in the end. What you need to understand is that functions can be parameters where blocks are asked and vice versa. A block is an anonymous function and if the in/out matches you can use it.
So technically you only need to pass the function that is going to do the required work all the way up and you're good. For example I have a class that fetches some data:
DataFetcher.fetch(for: id, whenDone: { result in /* etc */ })
This could be wrapped in a block:
{ (id: Int) in
DataFetcher.fetch(for: id, whenDone: { result in /* etc */ })
}
And this block could be passed all the way up the chain, so you call down once.
func configure(with fetchStuff: (Int) -> ())
I also don't understand why you pass the Cell itself as a parameter. Perhaps because the way the @IBAction
functions are structured by default but it's better to ignore it unless you have some really pressing reason to got back to exactly that cell.
Last but not least, providing defaults is not that bad when dealing with blocks that almost surely will be set during runtime:
public var btnReplyTappedClousre:((CommentCell) -> (Void))?
Could be:
private var replyAction: (Int) -> () = { _ in
assertionFailure("This block should always be replaced by the configure function before the cell is shown")
}
Let me explain the changes in detail:
- It can be
private
because it's set through yourconfigure
function and will only ever be called from within the cell. Better encapsulation. - It does not relate to the button but to what actually happens when you tap the button. So the
@IBAction func replyButtonTapped()
will callreplyAction(chatId)
- It only passes the required data (in this case the chat id). Nobody outside of
CommentCell
needs to know anything aboutCommentCell
- The empty block containing only the
assertionFailure
will warn any boneheaded colleague that did not callconfigure
during testing but will not crash when released (if it managed to escape during testing) - The block will always be set so you're not dealing with optionals anymore
isForHeaderCell
but in non header you forget to setcell.btnReplyTappedClousre
to nil - because you can also get an reused cell from header \$\endgroup\$