IOS swift5 UIController call uitableview function problem. Please assist - ios

I have a UIController class and a Tableview class. I would like to include tableview inside uicontroller. But i cannot access tableview inside functions (even static or public). how can i access tableview function from uicontroller?
Million thanks. Struggling with this problem long
import Foundation
import UIKit
class UIController: UIViewController {
private var tableView: UITableView = RegDropdownMenu(identifier: RegStepTwoIndentifier.regStepTwoTable)
init(identifier: String) {
super.init()
view.addSubview(tableView)
// Cannot call the func
tableView.testingFunc()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
class TableView: UITableView {
private var identifier: String
init(identifier: String) {
self.identifier = identifier
super.init(frame: .zero, style: .plain)
configTable()
}
private func configTable() {
self.delegate = self
self.dataSource = self
self.translatesAutoresizingMaskIntoConstraints = false
self.register(CellClass.self, forCellReuseIdentifier: identifier)
self.allowsSelection = true
self.separatorStyle = .none
self.layer.masksToBounds = true
self.backgroundColor = .white
self.layer.borderColor = Styles.borderColor.cgColor
self.layer.borderWidth = Styles.borderWidth
}
func testingFunc() {
print("123")
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}

You have created a custom UITableView subclass called TableView
This is the line of code that does this class TableView: UITableView
However, in your ViewController you are creating a generic UITableView
private var tableView: UITableView
This should be changed to
private var tableView: TableView which is the name of the custom UITableView class you created.
After this, tableView.testingFunc() should work.

Related

Why doesn't my table view cell class instance run its initializer in my Swift code?

I have a custom subclass of UITableViewCell shown below
class GroupSelectionTableViewCell: UITableViewCell {
// MARK: - Properties
var groupSelectionLabel: UILabel = {
let label = UILabel()
label.textColor = .white
label.backgroundColor = .clear
label.font = UIFont.systemFont(ofSize: 18)
return label
}()
var groupSelectionImageView: UIImageView = {
let iv = UIImageView()
iv.backgroundColor = .clear
iv.setHeight(height: 25)
iv.setWidth(width: 25)
iv.contentMode = .scaleAspectFill
return iv
}()
// MARK: - LifeCycle
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
configureUI()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// MARK: - Helper Functions
private func configureUI() {
self.addSubview(groupSelectionLabel)
groupSelectionLabel.centerY(inView: self)
groupSelectionLabel.anchor(left: self.leftAnchor, paddingLeft: 12)
self.addSubview(groupSelectionImageView)
groupSelectionImageView.centerY(inView: self)
groupSelectionImageView.anchor(right: self.rightAnchor, paddingRight: 12)
self.backgroundColor = .black
self.selectionStyle = .none
}
}
and a custom subclass of UITableView shown below...
class GroupSelectionView: UITableView {
// MARK: - Properties
private let cellID = "GroupSelectionTableViewCell"
override init(frame: CGRect, style: UITableView.Style) {
super.init(frame: frame, style: style)
backgroundColor = .red
setHeight(height: 450)
setWidth(width: 300)
register(GroupSelectionTableViewCell.self, forCellReuseIdentifier: cellID)
rowHeight = 60
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
extension GroupSelectionView: UITableViewDelegate {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
return
}
}
extension GroupSelectionView: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
6
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellID, for: indexPath) as! GroupSelectionTableViewCell
cell.groupSelectionLabel.text = "None"
cell.groupSelectionImageView.image = UIImage(named: "Tick")
return cell
}
}
But when I add my table view instance to a UIView() as a subview the table view cell class isn't running its initializer. Am I using the register method correctly? I have tried instantiating the table view subclass and putting a print statement in the initializer of the table view cell subclass but it doesn't get printed. Am I forgetting to set some property on the table view subclass? Do I need to use the Nib version of register instead? Any help would be greatly appreciated.
Forgot to set the dataSource and the delegate!

Creating reusable FooterView for different TableViews (Swift 5)

I created class FooterView: UIView. I use it to create FooterView for my TableView. But also I'd like to reuse this class to create FooterView in my other TableViews. When I try to add an argument to the setupElements () function to pass the required TableView there and change the Label text depending on it, but the initializer does not allow me to do this.
How can i do this? Also, Swift doesn't allow me to directly call the setupElements () function even when I remove the "private". My code:
class FooterView: UIView {
private var footerLabel: UILabel = {
...
}()
private func setupElements() {
addSubview(footerLabel)
...
footerLabel.text = "Table is empty"
}
override init(frame: CGRect) {
super.init(frame: frame)
setupElements()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
...
class TableViewController: UIViewController {
private lazy var footerView = FooterView()
private var array: [String] = []
func viewDidLoad() {
super.viewDidLoad()
setupTableView()
}
private func setupTableView() {
tableView.register(UINib(nibName: "Cell", bundle: nil), forCellReuseIdentifier: LibraryCell.reuseId)
if array.count == 0 {
tableView.tableFooterView = footerView
}
}
}
extension TableViewController: UITableViewDelegate, UITableViewDataSource {...}
After a lot of searching, I solved my problem. I removed the initialization and create a FooterView through the setupFooterView function (for vc: UIViewController) into which I pass the ViewController I need as a parameter. The code looks like this:
class FooterView: UIView {
private var footerLabel: UILabel = {
let label = UILabel()
...
return label
}()
func setupFooterView(for vc: UIViewController) -> UIView {
let footerView = UIView()
footerView.addSubview(footerLabel)
...
if vc is TableViewController {
footerLabel.text = "..."
} else if vc is SecondTableViewController {
footerLabel.text = "..."
}
return footerView
}
}
Adding FooterView to the ViewController I need:
...
private lazy var footerView = FooterView()
...
tableView.tableFooterView = footerView.setupFooterView(for: self)

How replace a table view by a collection view?

I want to replace a table view by a collection view, the table view is inside a tab from XLPagerTabStrip.
I already tried to replace some things but I get some errors, can someone put me on the right direction ?
import UIKit
import XLPagerTabStrip
class BooksChildViewController: UITableViewController,
IndicatorInfoProvider {
let cellIdentifier = "postCell"
var blackTheme = false
var itemInfo = IndicatorInfo(title: "Livros")
init(style: UITableViewStyle, itemInfo: IndicatorInfo) {
self.itemInfo = itemInfo
super.init(style: style)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
}
// MARK: - IndicatorInfoProvider
func indicatorInfo(for pagerTabStripController:
PagerTabStripViewController) -> IndicatorInfo {
return itemInfo
}
}
Your class BooksChildViewController should inherit UICollectionViewController instead of UITableViewController.
Also, you need modify your init() . UICollectionViewController can be initializer with a UICollectionViewLayout instead with a style like UITableViewController.

Not able to use Custom Cell with UITableView

I am trying to populate uitableview with custom but getting the following error:
Fatal error: Use of unimplemented initializer 'init(style:reuseIdentifier:)' for class 'Appname.PostCellView'
Code:
import UIKit
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 4
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell") as! PostCellView
return cell
}
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(PostCellView.self, forCellReuseIdentifier: "Cell")
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
Post View Cell: (this class is loaded in the File's Owner of the view)
import UIKit
protocol PostCellViewDelegate: class {
}
class PostCellView: UITableViewCell {
weak var delegate: PostCellViewDelegate?
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
let _ = commonInitialization()
}
func customise(imageName : String , color : UIColor, logoLabelValue : String, websiteValue: String)
{
}
func commonInitialization() -> UIView
{
let bundle = Bundle.init(for: type(of: self))
let nib = UINib(nibName: "PostCellView", bundle: bundle)
let view = nib.instantiate(withOwner: self, options: nil)[0] as! UIView
view.frame = bounds
view.autoresizingMask = [UIViewAutoresizing.flexibleWidth, UIViewAutoresizing.flexibleHeight]
addSubview(view)
return view
}
}
Please help me finding what's wrong with my code and how I should rectify the same.
Override init(style:reuseIdentifier:)
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
// Code
}
Please check this tutorial
Please shift the call to commonInitialization() from initWithCoder to method awakeFromNib. Also remove the initWithCoder method
In your PostCellView code add this method and remove the initWithCoder.
override func awakeFromNib() {
super.awakeFromNib()
let _ = commonInitialization()
}

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
}

Resources