1

So I'm a newbie and trying some reusability. I've a class called SingleButtonFooterView which subclasses UIView and UI is done in an .xib.

Now I want to use this in a UITableViewCell. I've tried almost all possible solutions nothing is working for me.

Code for the class:

class SingleButtonFooterView: UIView {

@IBOutlet weak var button: Button50!

override init(frame: CGRect) {
    super.init(frame: frame)
    commonInit()
}

required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
    commonInit()
}

private func commonInit(){
    let view = UINib(nibName: "SingleButtonFooterView", bundle: nil).instantiate(withOwner: self, options: nil).first as! UIView
    view.frame = self.bounds
    self.addSubview(view)
}

override func awakeFromNib() {
    super.awakeFromNib()
    button.backgroundColor = .clear
    button.layer.cornerRadius = 5
    button.layer.masksToBounds = true

    button.titleLabel?.font = UIFont.futura(with: .medium, size: 16)
    button.setTitleColor(.white, for: .normal)
    self.contentMode = .scaleAspectFit
    self.layer.masksToBounds = true
}
}

Now for cellForRowAt:

let cellId = "SingleButtonFooterView"
            var cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath)

            if cell == nil {
                cell = UITableViewCell(style: UITableViewCell.CellStyle.default, reuseIdentifier: cellId)
                let subView = SingleButtonFooterView(frame: cell.frame)

                cell.contentView.attachViewWithConstraints(subView)
                let _ = subView.viewLoadedFromNibAttached(name: cellId)
            }

            return cell

and in viewDidLoad() of my VC class.

tableView.register(UINib(nibName: "SingleButtonFooterView", bundle: nil), forCellReuseIdentifier: "SingleButtonFooterView")

In short -> I want to use a UIView class (UI done using interface builder -> .xib) in a UITableViewCell

4
  • what is it in attachViewWithConstraints Commented Aug 5, 2019 at 9:05
  • yep, I forgot to add that snippet infact I'll share the SO answer from where I've been taking help, it basically has all the snippets, stackoverflow.com/questions/41851892/… Commented Aug 5, 2019 at 9:09
  • stackoverflow.com/questions/57015324/… Commented Aug 5, 2019 at 9:19
  • Thanks for the comment @ElTomato but in my case tableview rendering is done all the way programmatically. Also the post shared by you, doesn't really goes with the question asked by me. =) Commented Aug 5, 2019 at 9:20

3 Answers 3

1

If I can suggest you something just do it "by the book".

Create a custom cell and .xib. Then you can do whatever you want with the UIViews (remember that you can create your own class and put it into xib by changing the class here: Where you change class of the UIView

Having awakeFromNib and Init in one class it's somehow a code smell because either you use .xib or code to create a view.

Remember that adding subviews from the VC it's always risky because you need to take care of recycle, what means that this subview may stay and be not wanted unless you handle this situation.

Remember about prepareForReuse() to handle cells' recycle.

Sign up to request clarification or add additional context in comments.

5 Comments

Actually I've already designed the UI in the .xib which inherits from UIView. Now as per the UI of the product, The UI in the .xib is basically used in some other screen as well. Now obviously why would I be re-designing it since I've already Designed it.? and the other screen has a UITableView...
@YashBedi could you rephrase your comment? It is hardly understandable
ok, Let me put it this way. I want to use a UIView class (UI done using interface builder -> .xib) in a UITableViewCell.
@YashBedi my post above answers your question. Again, make a simple UIView in the .xib and attach it to cell. Next, change class of this UIView which you attached to the cell and it is ready to go. Just don't use awake from nib method. It is going to be used from required init?(coder aDecoder: NSCoder) Here is followup: stackoverflow.com/questions/38386339/…
I really appreciate you taking out time to write the answer. But it doesn't answer my question at all. And on top of it your explanation is really vague, for e.g.-> Then you can do whatever you want with the... ? If you can write a working solution or if you think your code will work or if you really know the Answer to a question asked on SO please then you should post your answer, else always prefer comments.
0

You have two issues here:

1) Bundle.main.loadNibNamed(name, owner: self, options: nil) in viewLoadedFromNibAttached (if you use the exact same code from the other question) and you have the same nib load in commonInit. You have to decide where to put it, IMO you can get rid of

    let subView = SingleButtonFooterView(frame: cell.frame)
    cell.contentView.attachViewWithConstraints(subView)
    let _ = subView.viewLoadedFromNibAttached(name: cellId)

and put that part in the cell (this way you can easily maintain the livecycle of the SingleButtonFooterView

2) Guess: the File owner of the xib is empty or using wrong class, that's why commonInit and required init?(coder aDecoder: NSCoder) are causing infinite loop

EDIT:

Step 1: subclass UITableViewCell, lets call it SingleButtonCell

Step 2: custom view (SingleButtonFooterView in your case). NOTE! File's owner should be the class itself -> SingleButtonFooterView

 @IBOutlet var contentView: UIView!

    override init(frame: CGRect) {
        super.init(frame: frame)

        loadXib()
        setup()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)

        loadXib()
    }

    private func loadXib() {
        Bundle.main.loadNibNamed(String(describing: type(of: self)), owner: self, options: nil)

        addSubview(contentView)
        contentView.frame = bounds
    }

This is what I use to load views from .xib. contentView is the outlet of the main view in the .xib file

Step 3: Two options here:

  • Option 3.1: (easier to maintain IMO)

    With the SingleButtonCell you create it's own .xib file

  • 3.1.1: Add view in the cell's xib (SingleButtonCell.xib) and change it's class to SingleButtonFooterView

  • Option 3.2: Do not create cell xib. Instead instantiate the view (SingleButtonFooterView) inside the cell and add it as subview (add constraints if you want). Here you have to be careful where to instantiate the view, because there is a chance to add it multiple times

1 Comment

before all the above, can you suggest that how should I register the cell for it.
0

What I can see is that dequeueReusableCell(withIdentifier:for:) never returns nil. So the code in your nil-check will never be called. You may be thinking of the "old" dequeueReusableCell(withIdentifier:) which can return nil and can be used similar to how you do.

Since it's never nil, you need an additional parameter, say "hasBeenInitialized" to keep track of if you have added your custom UIView yet or not.

// In loading viewDidLoad, register cellId for type for tableView

let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath)

if !hasBeenInitialized {
    // Load view from xib
    // Add view as subview to cell contentview
    // Add surrounding constraints
    hasBeenInitialized = true
}
return cell

1 Comment

Thank you Sunkas, I'll try the solution and will update here, Thank you again.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.