Using custom XIB in table view cell - ios

I have followed this tutorial to create a custom .xib, which I plan to use in a table view's cell:
https://medium.com/#brianclouser/swift-3-creating-a-custom-view-from-a-xib-ecdfe5b3a960
Here is the .xib's class I created:
class UserView: UIView {
#IBOutlet var view: UIView!
#IBOutlet weak var username: UILabel!
override init(frame: CGRect) {
super.init(frame: frame)
initialize()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
initialize()
}
private func initialize() {
Bundle.main.loadNibNamed("UserView", owner: self, options: nil)
addSubview(view)
view.frame = self.bounds
view.autoresizingMask = [.flexibleHeight, .flexibleWidth]
}
}
Previously, I was creating my table view cell within the storyboard, but I've come to realize that I want a more flexible view so that I can use it in different parts of my app, so I created the above custom .xib, UserView.
I have updated the table view cell in the storyboard to use the custom .xib:
https://i.stack.imgur.com/t7Tr7.png
Here is what my table view controller class looked like prior to creating the custom .xib (i.e. making the layout in the storyboard):
class UserTableViewController: UITableViewController {
// MARK: Properties
let provider = MoyaProvider<ApiService>()
var users = [User]()
override func viewDidLoad() {
super.viewDidLoad()
tableView.estimatedRowHeight = 100
tableView.rowHeight = UITableViewAutomaticDimension
// Fetch the user by their username
provider.request(.getUsers()) { result in
switch result {
case let .success(response):
do {
let results = try JSONDecoder().decode(Pagination<[User]>.self, from: response.data)
self.users.append(contentsOf: results.data)
self.tableView.reloadData()
} catch {
print(error)
}
case let .failure(error):
print(error)
break
}
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return users.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cellIdentifier = "UserTableViewCell"
guard let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as? UserTableViewCell else {
fatalError("The dequeued cell is not an instance of UserTableViewCell.")
}
let user = users[indexPath.row]
cell.username.text = user.username
return cell
}
}
Here is the table view cell class:
class UserTableViewCell: UITableViewCell {
//MARK: Properties
#IBOutlet weak var userView: UserView!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
My question is, how do I update the above table view controller class to use my custom .xib, instead of using the storyboard layout?

You can use 2 ways:
Create UITableViewCell (better)
1) Change UIView to UITableViewCell
class CustomTableViewCell: UITableViewCell {
...
class var identifier: String {
return String(describing: self)
}
}
2) Register your cell
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.registerNib(UINib(nibName: CustomTableViewCell.identifier, bundle: nil), forCellReuseIdentifier: CustomTableViewCell.identifier)
...
}
3) Use cellForRow(at:)
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: CustomTableViewCell.identifier) as! CustomTableViewCell
cell.username.text = user.username
return cell
}
OR Add view as subview to cell (only in rare cases)
1) Add this to UserView
class UserView: UIView {
...
class func fromNib() -> UserView {
return UINib(nibName: String(describing: self), bundle: nil).instantiate(withOwner: nil, options: nil)[0] as! UserView
}
}
2) Use cellForRow(at:)
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cellIdentifier = "UserTableViewCell"
guard let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as? UserTableViewCell else {
fatalError("The dequeued cell is not an instance of UserTableViewCell.")
}
let userView = UserView.fromNib()
let user = users[indexPath.row]
userView.username.text = user.username
//Use frame size, but for me better to add 4 constraints
userView.frame = CGRect(x: 0, y: 0, width: cellWidth, height: cellHeight)
cell.contentView.addSubview(UserView)
return cell
}

Related

swift tableView in custom view programatically - losing reference to controllers delegate and data source

I am trying to learn MVVM pattern and writing all my views programatically using Snapkit. I am creating hamburger menu which consist of simple tableView and I have a problem, that my tableView in cusom view is losing delegate and data source references on the view controller. I also tried using UITableViewController, but result is the same, here is my code:
ViewModel:
class SideMenuViewModel {
let cellId = "SideMenuCellId"
weak var delegate: SideMenuViewModelDelegate?
private let cells: [SideMenuItemStruct] = [SideMenuItemStruct(type: .allDogs, title: "ALL DOGOS"),
SideMenuItemStruct(type: .randomDog, title: "RANDOM DOGO")]
init(delegate: SideMenuViewModelDelegate) {
self.delegate = delegate
}
var numberOfRows: Int {
return cells.count
}
func selectedMenuItem(indexPath: IndexPath) {
switch SideMenuItemsEnum(rawValue: indexPath.row) {
case .allDogs?:
delegate?.selectedMenuItem(selectedItem: SideMenuItemsEnum.allDogs)
case .randomDog?:
delegate?.selectedMenuItem(selectedItem: SideMenuItemsEnum.randomDog)
default:
print("error when choosing menu item")
}
}
func cellForRow(_ tableView: UITableView, indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath) as? SideMenuCell else {
fatalError("could not deque Side menu cell")
}
cell.selectionStyle = .none
cell.setUpCell(sideMenuItem: cells[indexPath.row])
return cell
}
}
View:
class SideMenuView: UIView {
var sideMenuTableView = UITableView()
let sideMenuButton = UIButton(type: .system)
weak var delegate: UITableViewDelegate? {
get {
return sideMenuTableView.delegate
}
set {
sideMenuTableView.delegate = newValue
}
}
weak var dataSource: UITableViewDataSource? {
get {
return sideMenuTableView.dataSource
}
set {
sideMenuTableView.dataSource = newValue
}
}
override init(frame: CGRect) {
super.init(frame: frame)
initUI()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func awakeFromNib() {
super.awakeFromNib()
}
private func initUI() {
addSubview(sideMenuButton)
addSubview(sideMenuTableView)
setUpSideMenuButton()
setUpSideMenuTableView()
}
private func setUpSideMenuButton() {
sideMenuButton.setTitle("DELEGATE", for: .normal)
sideMenuButton.addTarget(self, action: #selector(buttonPrint), for: .touchUpInside)
sideMenuButton.snp.makeConstraints { (make) in
make.top.equalTo(self)
make.centerX.equalTo(self)
}
}
#objc func buttonPrint() {
print("delegate: \(String(describing: sideMenuTableView.delegate)), data source: \(String(describing: sideMenuTableView.dataSource))")
}
private func setUpSideMenuTableView() {
sideMenuTableView.snp.makeConstraints { (make) in
make.top.equalTo(sideMenuButton.snp.bottom)
make.bottom.equalTo(self)
make.left.equalTo(self)
make.right.equalTo(self)
}
}
}
And my View Controller:
class SideMenuController: UIViewController {
fileprivate let viewModel: SideMenuViewModel
fileprivate var sideMenuView: SideMenuView {
return view as! SideMenuView
}
init(viewModel: SideMenuViewModel) {
self.viewModel = viewModel
super.init(nibName: nil, bundle: nil)
}
override func loadView() {
let sideMenuView = SideMenuView()
sideMenuView.sideMenuTableView.delegate = self
sideMenuView.sideMenuTableView.dataSource = self
view = sideMenuView
}
override func viewDidLoad() {
super.viewDidLoad()
sideMenuView.sideMenuTableView.register(SideMenuCell.self, forCellReuseIdentifier: viewModel.cellId)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
extension SideMenuController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return viewModel.numberOfRows
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
return viewModel.cellForRow(tableView, indexPath: indexPath)
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
viewModel.selectedMenuItem(indexPath: indexPath)
print("awd")
}
}
Simulater after init
Simulator after scroll
DELEGATE button tapped result
I am learning from few tutorials and they didn't had this problem, but they were all using Interface builders, which I want to avoid. Please, let me know, if I am doing something really wrong, thanks.
SOLUTION
I found out, I made a really huge mistake outside of this showed code, I initialized SideMenuController in a function and didn't keep reference to it, so naturaly it was automaticly deinitialized after end of a function. It was a really bad mistake. Thanks for all answers, code here is working, but I refactored it according to answer.
I guess you have been hacking on this for a while and it looks like code has ended up a bit all over the place.
If you are going to follow MVVM then you need to think about the role of each component.
Model - An array of SideMenuItem
ViewModel - In this case it is the same as your Model so you can dispense with the Model and just use the ViewModel. In more complex examples, the ViewModel maps back to the Model, exposing on the data required by the view and performing any required translations
View - The actual visual elements; In this case just a tableview (although you also have a button for debugging)
Finally, you still have the View Controller that brings it all together
ViewModel
struct SideMenuViewModel {
let items = [SideMenuItemStruct(type: .allDogs, title: "ALL DOGOS"),
SideMenuItemStruct(type: .randomDog, title: "RANDOM DOGO")]
}
View
class SideMenuView: UIView {
weak var viewModel: SideMenuViewModel?
weak var delegate: SideMenuViewDelegate? // Was SideMenuViewModelDelegate
private let sideMenuButton = UIButton(type: .system)
private var sideMenuTableView = UITableView()
private let cellId = "YourCellID"
override init(frame: CGRect) {
super.init(frame: frame)
initUI()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func awakeFromNib() {
super.awakeFromNib()
}
private func initUI() {
addSubview(sideMenuButton)
addSubview(sideMenuTableView)
setUpSideMenuButton()
setUpSideMenuTableView()
}
private func setUpSideMenuButton() {
sideMenuButton.setTitle("DELEGATE", for: .normal)
sideMenuButton.addTarget(self, action: #selector(buttonPrint), for: .touchUpInside)
sideMenuButton.snp.makeConstraints { (make) in
make.top.equalTo(self)
make.centerX.equalTo(self)
}
}
#objc func buttonPrint() {
print("delegate: \(String(describing: sideMenuTableView.delegate)), data source: \(String(describing: sideMenuTableView.dataSource))")
}
private func setUpSideMenuTableView() {
sideMenuTableView.snp.makeConstraints { (make) in
make.top.equalTo(sideMenuButton.snp.bottom)
make.bottom.equalTo(self)
make.left.equalTo(self)
make.right.equalTo(self)
}
sideMenuTableView.datasource = self
sideMenuTableView.delegate = self
sideMenuTableView.register(SideMenuCell.self, forCellReuseIdentifier: cellId)
}
}
extension SideMenuView: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return viewModel?.numberOfRows ?? 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath) as? SideMenuCell else {
fatalError("could not deque Side menu cell")
}
cell.selectionStyle = .none
cell.setUpCell(sideMenuItem: self.viewModel!.items[indexPath.row])
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let menuItem = self.viewModel!.items[indexPath.row]
self.delegate?.didSelect(menuItem)
}
}
ViewController
class SideMenuController: UIViewController {
fileprivate let viewModel: SideMenuViewModel
fileprivate var sideMenuView: SideMenuView {
return view as! SideMenuView
}
override func loadView() {
let sideMenuView = SideMenuView()
sideMenuView.delegate = self
sideMenuView.viewModel = viewModel
view = sideMenuView
}
init(viewModel: SideMenuViewModel) {
self.viewModel = viewModel
super.init(nibName: nil, bundle: nil)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
extension SideMenuController: SideMenuViewDelegate {
// TODO: Implement delegate method for menu selection
}

How to create a various type of cells in a single tableView?

I'm currently developing an app which has features like half Uber, half Tinder.
I would like to create a TableView for editing profile to make it look like a view of Tinder's profile editing view.
I successfully made a first cell for picking up profile image and second cell for textLabel.
I want to make a third cell which has an editable TextView(not textField so users can enter several lines of words) But the codes aren't working as I intended.
It seems like although I made a textView in my Custom Cell Class and set it to my third tableView cell in my tableViewController class, it doesn't appear on my simulator.
I'm still not quite sure what's the good way to make several types of cell for a single tableView so if there's other better way to do it, I would love to know.
My codes related to this issue is below.
ViewController
//EditProfileViewController For The Profile Edit view
class EditProfileViewController: UINavigationController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
TableViewController
//MyTableViewController for the EditProfileViewController
class EditProfileTableViewController: UITableViewController, UINavigationControllerDelegate, UIImagePickerControllerDelegate, UITextViewDelegate {
override func viewDidLoad() {
super.viewDidLoad()
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 3
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if section == 0 {
return 5
} else if section == 1 {
return 2
} else {
return 4
}
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
//link my custom Cell here
if let cell = tableView.dequeueReusableCell(withIdentifier: "MyCustomCell", for: indexPath) as? MyCellTableViewCell {
if indexPath.section == 0 {
if indexPath.row == 0 {
//this row is for an Image Picker
cell.textLabel?.text = "edit profile image"
} else if indexPath.row == 1 {
//this row is just like a title of third cell which is editable self description
cell.textLabel?.text = "Describe yourself"
cell.isUserInteractionEnabled = false
} else if indexPath.row == 2 {
//this row is an editable self description
cell.addSubview(cell.selfDescritionTextView)
cell.selfDescritionTextView.delegate = self
cell.selfDescritionTextView.isEditable = true
cell.selfDescritionTextView.text = "Enter some descriptions here"
}
}
else if indexPath.section == 1 {
cell.textLabel?.text = "section 1"
}
else if indexPath.section == 2 {
cell.textLabel?.text = "section 2"
}
return cell
} else {
//Just in case
return UITableViewCell()
}
}
}
TableViewCell
//MyCellTableViewCell
class MyCellTableViewCell: UITableViewCell {
//create a textView for self description
var selfDescritionTextView = UITextView()
//init
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: .default, reuseIdentifier: "MyCustomCell")
selfDescritionTextView = UITextView(frame: self.frame)
self.addSubview(selfDescritionTextView)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
If you need to know more codes than what I showed here, Please let me know.
I'm not obsessed with this method to create my edit profile View so if anyone knows other ways better than this, I would LOVE to know as well.
Thanks
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
tableView.register(UINib(nibName: TableViewCell1.NibName, bundle: nil), forCellReuseIdentifier: TableViewCell1.Identifier)
tableView.register(UINib(nibName: TableViewCell2.NibName, bundle: nil), forCellReuseIdentifier: TableViewCell2.Identifier)
tableView.register(UINib(nibName: TableViewCell3.NibName, bundle: nil), forCellReuseIdentifier: TableViewCell3.Identifier)
}
You'll have 3 Custom Table View Cells, with its subviews, respectively:
class MyCellTableViewCell1: UITableViewCell {
static var NibName: String = String(describing: TableViewCell1.self)
static var Identifier: String = "TableViewCell1"
func configure() { ... }
}
Then:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell: UITableViewCell
// link my custom Cell here.
if indexPath.row == 0, let dequeuedCell = tableView.dequeueReusableCell(withIdentifier: TableViewCell1.Identifier, for: indexPath) as? TableViewCell1 {
dequeuedCell.configure()
cell = dequeuedCell
}
else if indexPath.row == 1, let dequeuedCell = tableView.dequeueReusableCell(withIdentifier: TableViewCell2.Identifier, for: indexPath) as? TableViewCell2 {
dequeuedCell.configure()
cell = dequeuedCell
}
else if indexPath.row == 2, let dequeuedCell = tableView.dequeueReusableCell(withIdentifier: TableViewCell3.Identifier, for: indexPath) as? TableViewCell3 {
dequeuedCell.configure()
cell = dequeuedCell
}
else {
cell = UITableViewCell()
}
return cell
}
Register multiple nib files like this , but it in viewDidLoad
tableView.register(UINib(nibName: "TableViewCell1", bundle: nil), forCellReuseIdentifier: CellIdentifier1)
tableView.register(UINib(nibName: "TableViewCell2", bundle: nil), forCellReuseIdentifier: CellIdentifier2)
tableView.register(UINib(nibName: "TableViewCell3", bundle: nil), forCellReuseIdentifier: CellIdentifier3)

Designing a UITableView/Cell - iOS

I'm designing a UITableView using subviews to populate the reusable cell of it, and I wish some opinion about that.
As I had tested, it works well. But, I don't know if it is a good solution.
The scenario is: I have a tableview with different kind of cells (layouts). When I was designing, it grows fast (my controller code), as I had to register a lot of cell and handle cellForRow. Then I come with that idea, to instantiate different subviews for one unique reusable cell and use a 'Presenter' to handle delegate/datasource. You think is that a problem? And is that a good approach?
Thanks in advance!
Ps.: sorry for any english error!
EDITED:
Here is the session in project followed by de codes:
Codes at:
OrderDetailCell
class OrderDetailCell: UITableViewCell {
//MARK: Outlets
#IBOutlet weak var cellHeight: NSLayoutConstraint!
#IBOutlet weak var viewContent: UIView!
//Variables
var didUpdateLayout = false
internal func setupLayoutWith(view: UIView){
cellHeight.constant = view.frame.height
viewContent.frame = view.frame
viewContent.addSubview(view)
updateConstraints()
layoutIfNeeded()
didUpdateLayout = true
}
}
OrderDetailSubview
class OrderDetailSubview: UIView {
var type: OrderDetailsSubViewType?
var height: CGFloat = 1
class func instanceFromNib(withType type: OrderDetailsSubViewType) -> OrderDetailSubview {
let view = UINib(nibName: type.rawValue, bundle: nil).instantiate(withOwner: nil, options: nil)[0] as! OrderDetailSubview
switch type {
case .OrderDetailSubviewStatus:
view.height = 258
case .OrderDetailSubViewItem:
view.height = 129
case .OrderDetailSubViewStoreInformation:
view.height = 317
case .OrderDetailSubViewEvaluation:
view.height = 150
}
view.updateConstraints()
view.layoutIfNeeded()
return view
}
}
OrderDetailPresenter
enum OrderDetailsSubViewType: String {
case OrderDetailSubviewStatus = "OrderDetailSubviewStatus",
OrderDetailSubViewItem = "OrderDetailSubViewItem",
OrderDetailSubViewStoreInformation = "OrderDetailSubViewStoreInformation",
OrderDetailSubViewEvaluation = "OrderDetailSubViewEvaluation"
static let types = [OrderDetailSubviewStatus, OrderDetailSubViewItem, OrderDetailSubViewStoreInformation, OrderDetailSubViewEvaluation]
}
class OrderDetailPresenter {
//Constants
let numberOfSections = 4
//Variables
// var order: Order?
func setup(reusableCell: UITableViewCell, forRowInSection section: Int) -> OrderDetailCell {
let cell = reusableCell as! OrderDetailCell
for sub in cell.viewContent.subviews {
sub.removeFromSuperview()
}
let subView = OrderDetailSubview.instanceFromNib(withType: OrderDetailsSubViewType.types[section])
cell.setupLayoutWith(view: subView)
return cell
}
func numberOfRowsForSection(_ section: Int) -> Int {
switch section {
case 1:
//TODO: count de offerList
return 4
default:
return 1
}
}
}
OrderDetailViewController
class OrderDetailViewController: BaseViewController {
//MARK: Outlets
#IBOutlet weak var tableView: UITableView!
var presenter = OrderDetailPresenter()
override func setupView() {
setupTableView()
}
}
extension OrderDetailViewController: UITableViewDataSource, UITableViewDelegate {
internal func setupTableView() {
tableView.delegate = self
tableView.dataSource = self
tableView.estimatedRowHeight = 600
tableView.rowHeight = UITableViewAutomaticDimension
tableView.register(UINib(nibName: "OrderDetailCell", bundle: nil), forCellReuseIdentifier: "OrderDetailCell")
}
func numberOfSections(in tableView: UITableView) -> Int {
return presenter.numberOfSections
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return presenter.numberOfRowsForSection(section)
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let reusableCell = tableView.dequeueReusableCell(withIdentifier: "OrderDetailCell") as! OrderDetailCell
let cell = presenter.setup(reusableCell: reusableCell, forRowInSection: indexPath.section)
return cell
}
}
*Sorry for indentation here...
Thats it! What you think?
Here you want to have multiple UITableViewCell subclasses that implement the different layouts that you want, and then select the relevant one in you table view data source.
class Cell1: UITableViewCell {
let label = UILabel()
override init(style: UITableViewCellStyle, reuseIdentifier: String) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
self.contentView.addSubview(label)
}
... whatever other setup/layout you need to do in the class ...
}
class Cell2: UITableViewCell {
let imageView = UIImageView()
override init(style: UITableViewCellStyle, reuseIdentifier: String) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
self.contentView.addSubview(imageView)
}
... whatever other setup/layout you need to do in the class ...
}
Then in your view controller
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(Cell1.self, forCellReuseIdentifier: "cell1Identifier")
tableView.register(Cell2.self, forCellReuseIdentifier: "cell2Identifier")
}
...
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row % 2 == 0 { // just alternating rows for example
let cell = tableView.dequeueReusableCell(withIdentifier: "cell1Identifier", for: indexPath) as! Cell1
// set data on cell
return cell
} else {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell2Identifier", for: indexPath) as! Cell2
// set data on cell
return cell
}
}
So this is just an example, but is using two different cell subclasses for alternating rows in the table view.
let dynamicCellID: String = "dynamicCellID" //One Cell ID for resuse
class dynamicCell: UITableViewCell {
var sub: UIView // you just need to specify the subview
init(sub: UIView) {
self.sub = sub
super.init(style: .default, reuseIdentifier: dynamicCellID)
self.addSubview(sub)
self.sub.frame = CGRect(x: 0, y: 0, width: sub.frame.width, height: sub.frame.height)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
And you need to create a views array the give that view to every cell in delegate
let views: [UIView] = []
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
return views.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let v = views[indexPath.row]
return dynamicCell(sub: v)
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
let v = views[indexPath.row]
return v.frame.height + 10 //offset is 10 point
}

Why is child table view controller unresponsive to touches and scrolling?

I have a basic view controller with the following code:
class List2ViewController: UIViewController {
#IBOutlet weak var segmentedControl: UISegmentedControl!
#IBOutlet weak var mapView: MKMapView!
override func viewDidLoad() {
super.viewDidLoad()
segmentedControl.addTarget(self, action: #selector(showDummyViewController), for: UIControlEvents.valueChanged)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func showDummyViewController(){
let controller = storyboard?.instantiateViewController(withIdentifier: "tableController")
if let controller = controller {
controller.view.frame = view.bounds
view.addSubview(controller.view)
addChildViewController(controller)
controller.didMove(toParentViewController: self)
}
}
}
As you can see I am adding a child view controller that happens to be table view controller. The code for this table view controller is as follows:
class MyTableViewController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
let nib = UINib(nibName: CustomCellTypeIdentifiers.NoResultCell, bundle: nil)
tableView.register(nib, forCellReuseIdentifier: CustomCellTypeIdentifiers.NoResultCell)
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 100
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: CustomCellTypeIdentifiers.NoResultCell, for: indexPath) as! CellType
return cell
}
}
My problem is that when my table view is added as a child controller, it is not responsive to any touch inputs. The table view shows up but it does not scroll through or select any rows. Additionally, my cells do not show up in rows. Can you please explain to what I am missing in this implementation? Thank you in advance.
Heres a picture of the result:
One issue that i see here:
let cell = tableView.dequeueReusableCell(withIdentifier: CustomCellTypeIdentifiers.NoResultCell, for: indexPath)
should be like this:
let cell = tableView.dequeueReusableCell(withIdentifier: CustomCellTypeIdentifiers.NoResultCell, for: indexPath) as? CustomTableViewCellName
where CustomTableViewCellName should be replaced by the value of CustomCellTypeIdentifiers.NoResultCell i.e. your custom cell name, as i.e. your nibName also:
let nib = UINib(nibName: CustomCellTypeIdentifiers.NoResultCell, bundle: nil)

Delegate didSet property of UIView not being called inside custom UITableViewCell

I've been banging my head on the desk for the past 2 hours trying to figure this out so any help will be greatly appreciated.
I'm using CVCalendar (the develop branch) and I'm trying to place the CalendarView inside a custom UITableViewCell so I can make it look something like this:
The problem I'm facing is that it doesn't set the delegate property when I call self.calendarView.calendarDelegate = self inside the custom UITableViewCell
I know it's not being set because I've placed a println inside the delegate didSet property that's not being outputted.
Here is the CalendarViewDelegate outlet inside the CalendarView
// MARK: - Calendar View Delegate
#IBOutlet weak var calendarDelegate: AnyObject? {
set {
if let calendarDelegate = newValue as? Delegate {
delegate = calendarDelegate
println("delegate did set property called")
}
}
get {
return delegate
}
}
Here is my code for the custom UITableViewCell I'm working with.
import UIKit
class CalendarCell: UITableViewCell {
var calendarView: CVCalendarView!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
backgroundColor = UIColor.redColor()
selectionStyle = .None
self.calendarView = CVCalendarView(frame: self.frame)
self.calendarView.calendarDelegate = self
self.calendarView.calendarAppearanceDelegate = self
self.calendarView.animatorDelegate = self
contentView.addSubview(calendarView)
}
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func layoutSubviews() {
super.layoutSubviews()
self.calendarView.commitCalendarViewUpdate()
}
}
Here is my UITableViewController code:
import UIKit
class CalendarTableViewController: UITableViewController {
#IBOutlet var myTableView: UITableView!
var items = ["Apple", "Banana"];
override func viewDidLoad() {
super.viewDidLoad()
self.myTableView.registerClass(CalendarCell.self, forCellReuseIdentifier: NSStringFromClass(CalendarCell))
self.myTableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: "Cell")
self.myTableView.dataSource = self
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func creatHeaderCell() -> UITableViewCell {
var headerCell = self.myTableView.dequeueReusableCellWithIdentifier(NSStringFromClass(CalendarCell)) as! CalendarCell
return headerCell
}
// MARK: - Table view data source
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
if (indexPath.section == 0) {
return 300
}
return 44
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 2
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
switch (section) {
case 0:
return 1
default:
return items.count
}
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if (indexPath.section == 0) {
return self.creatHeaderCell()
}
let cell = self.myTableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! UITableViewCell
cell.textLabel!.text = "Thomas"
// Configure the cell...
return cell
}
}
Your println statement is probably not reached because your if condition if let calendarDelegate = newValue as? Delegate is not true because what you pass in as calenderDelegate is a CalenderCell, not a Delegate or CVCalendarViewDelegate (typealias).
Additionally your CalendarCell does not conform to CVCalendarViewAppearanceDelegate and CVCalendarViewAnimatorDelegate.
You have to make your cell conform to those protocols to be able to set them as delegates.

Resources