Creating reusable FooterView for different TableViews (Swift 5) - ios

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)

Related

IOS swift5 UIController call uitableview function problem. Please assist

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.

How can I override the method of view in Controller?

I made a custom View Cell that want to reuse. I want to add action to button in customView, that show side menu. So I wrote a code ReusableCustomView.swift like below.
class ResuableCustomView: UIView {
let nibName = "ReusableCustomView"
#IBOutlet weak var categoryMenuBTn: UIButton!
#IBOutlet weak var containerView: UIStackView!
func showMenu(){
print("tapped")
}
#IBAction func menuBtnClicked(_ sender: UIButton) {
self.showMenu()
}
#IBAction func buttonTap(_ sender: UIButton) {
print("Tappedd")
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
func commonInit() {
guard let view = loadViewFromNib() else { return }
view.frame = self.bounds
self.addSubview(view)
}
func loadViewFromNib() -> UIView? {
let nib = UINib(nibName: nibName, bundle: nil)
return nib.instantiate(withOwner: self, options: nil).first as? UIView
}
}
showing SideMenu Code is like below.
func showSlideMenu(){
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let sideMenuViewController: SideMenuViewController = storyboard.instantiateViewController(withIdentifier: "SideMenuViewController") as! SideMenuViewController
let menu = CustomSideMenuNavigation(rootViewController: sideMenuViewController)
present(menu, animated: true, completion: nil)
}
The customized view is added to the storyboard and the ui is coming out well, and I want to override showMenu function so that i can show the side menu.
How Can I Override that function by code? I'd appreciate it if you could let me know.
ViewController code is below:
class categoryToViewController: UIViewController {
#IBOutlet weak var customViewCell: ResuableCustomView!
override func viewDidLoad() {
super.viewDidLoad()
}
}
storyBoard image that adopts ResuableCustomView Class
ReusableCell.xib image
You can directly add the button action menuBtnClicked inside the categoryToViewController since the ReusableCustomView is placed inside this ViewController.
class categoryToViewController: UIViewController {
#IBOutlet weak var customViewCell: ResuableCustomView!
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func menuBtnClicked(_ sender: Any) {
print("pressed")
}
}
first you better inherit from UIControll instead of UIView.
Because you can get the event.
1- change define a class to this line:
class ResuableCustomView: UIControl { ... }
2- add to UIView to custom UIViewController and sed Custom class to ResuableCustomView:
3- create function in ViewController for get Event:
#IBAction private func customViewTapped(_ sender: ResuableCustomView) {
print("hello my friend :-)")
}
4- inside ResuableCustomView class set Action by append this line to your function:
self.sendActions(for: .touchUpInside)
this line send event for use.
Here I used type touchUpInside.
change your showMenu function to below code:
func showMenu(){
// set here callBack
self.sendActions(for: .touchUpInside) // and create event function with touchUpInside type
print("tapped")
}
5- now back to storyboard and connect IBAction to callBack like This Picture:
run and click on button and see print like this:
all code for CustomView:
// inheritance from UIControl instead of UIView
class ResuableCustomView: UIControl {
let nibName = "ReusableCustomView"
#IBOutlet weak var categoryMenuBTn: UIButton!
#IBOutlet weak var containerView: UIStackView!
func showMenu(){
// set here callBack
self.sendActions(for: .touchUpInside) // and create event function with touchUpInside type
print("tapped")
}
#IBAction func menuBtnClicked(_ sender: UIButton) {
self.showMenu()
}
#IBAction func buttonTap(_ sender: UIButton) {
print("Tappedd")
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
func commonInit() {
guard let view = loadViewFromNib() else { return }
view.frame = self.bounds
self.addSubview(view)
}
func loadViewFromNib() -> UIView? {
let nib = UINib(nibName: nibName, bundle: nil)
return nib.instantiate(withOwner: self, options: nil).first as? UIView
}
}
all code for ViewController:
class ViewController: UIViewController {
#IBOutlet weak var customViewCell: ResuableCustomView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBAction private func customViewTapped(_ sender: ResuableCustomView) {
print("hello my friend :-)")
}
}

NSInvalidArgumentException : Workout_Tracker.QuickAddViewController collectionView:numberOfItemsInSection:]: unrecognized selector sent to instance

I have a class MenuTabs: UIView that corresponds to MenuTabs.xib. I linked them in the identity inspector. In the view is a UICollectionView. I set the UIView as the delegate and datasource for the collection view in storyboard. I'm using the MenuTabs class in a ViewController, but I keep getting this error
'NSInvalidArgumentException', reason: '-[Workout_Tracker.QuickAddViewController collectionView:numberOfItemsInSection:]: unrecognized selector sent to instance 0x7fbbe970a120'
Here are my MenuTabs and QuickAddViewController files
import UIKit
class MenuTabs: UIView {
let workoutTypes = ["", "", "", ""]
let cellId = "cellId"
#IBOutlet weak var contentView: UIView!
#IBOutlet weak var collectionView: UICollectionView!
override init(frame: CGRect) {
super.init(frame: frame)
collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: cellId)
setup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
if self.subviews.count == 0 {
self.setup()
}
}
func setup() {
Bundle.main.loadNibNamed("MenuTabs", owner: self, options: nil)
guard let content = contentView else { return }
contentView.frame = self.bounds
contentView.autoresizingMask = [.flexibleHeight, .flexibleWidth]
addSubview(content)
}
}
// MARK: - Delegate and Datasource methods for UICollectionView
extension MenuTabs: UICollectionViewDelegate, UICollectionViewDataSource,
UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return workoutTypes.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath)
cell.backgroundColor = UIColor.red
return cell
}
}
import UIKit
class QuickAddViewController: UIViewController {
enum MuscleGroup: String {
case abs = "Abs"
case arms = "Arms"
case back = "Back"
case calves = "Calves"
case chest = "Chest"
case legs = "Glutes & Legs"
case shoulders = "Shoulders"
}
enum WorkoutType: String {
case bodyWeight = "Body Weight"
case weightTraining = "Weight Training"
case sportsAndRecreation = "Sports & Recreation"
case cardio = "Cardio"
}
#IBOutlet weak var workoutTypesMenu: UIView!
let exercisesData = ExerciseDatabase()
var workoutTypesDictionary = Dictionary<String,Dictionary<String,Array<String>>>()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
workoutTypesDictionary = self.exercisesData.exercisesByWorkoutType
tabBarController?.tabBar.isTranslucent = false
// Load workoutTypesMenu View
if let wtMenu = Bundle.main.loadNibNamed("MenuTabs", owner: self, options: nil)?.first as! MenuTabs? {
workoutTypesMenu.addSubview(wtMenu)
}
}
// MARK: - Get data from ExerciseDatabase.swift
// Get the workout types
func getWorkoutTypes() -> [String] {
var workoutTypesArray : [String] = []
for workoutType in workoutTypesDictionary.keys {
workoutTypesArray.append(workoutType)
}
return workoutTypesArray
}
// Get the list of muscles or options
func getMusclesOrOptions(for workoutType: String) -> [String] {
var musclesOrOptionsArray : [String] = []
let musclesOrOptions = workoutTypesDictionary[workoutType]!.keys
for muscleOrOption in musclesOrOptions {
musclesOrOptionsArray.append(muscleOrOption)
}
return musclesOrOptionsArray
}
// Get the list of exercises
func getExercisesArray(for workoutType: String, for muscleOrOption: String) -> [String] {
var exercisesArray : [String] = []
exercisesArray = workoutTypesDictionary[workoutType]![muscleOrOption]!
return exercisesArray
}
// Get the selected exercise
func getSelectedExercise(in workoutType: String, for muscleOrOption: String, at index: Int) -> String {
var selectedExercise : String = ""
selectedExercise = workoutTypesDictionary[workoutType]![muscleOrOption]![index]
return selectedExercise
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - WorkoutTypesBar and Muscles and Options Bar
// Set up the WorkoutTypes bar and the muscles and option types bar
}
Instead of setting up the delegate and datasource through the nib file, I set them in the init method of MenuTabs.swift
override init(frame: CGRect) {
super.init(frame: frame)
collectionView.delegate = self
collectionView.dataSource = self
collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: cellId)
setup()
}

Update content of user defined UIView inside UICollectionViewCell

I have UICollectionView with cells, that contains UIScrollView and inside it, there is a user defined UIView (via XIB)
I am using this code to init cell with data:
func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
for v in cell.contentView.subviews {
if let scroll: UIScrollView = v as? UIScrollView {
scroll.contentOffset = CGPoint(x: 0, y: 0)
for s in scroll.subviews {
if let content: DetailedView = s as? DetailedView {
content.fillData()
}
}
}
}
}
In my DetailedView, I have:
#IBDesignable
class DetailedView: UIView {
#IBOutlet weak var btnTemp: UIButton!
override init(frame: CGRect) {
super.init(frame: frame)
setupView()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setupView()
}
// Performs the initial setup.
private func setupView() {
let view = viewFromNibForClass()
view.frame = bounds
// Auto-layout stuff.
view.autoresizingMask = [
UIViewAutoresizing.flexibleWidth,
UIViewAutoresizing.flexibleHeight
]
addSubview(view)
}
// Loads a XIB file into a view and returns this view.
private func viewFromNibForClass() -> UIView {
let bundle = Bundle(for: type(of: self))
let nib = UINib(nibName: String(describing: type(of: self)), bundle: bundle)
let view = nib.instantiate(withOwner: self, options: nil).first as! UIView
return view
}
func fillData(){
self.btnTemp.titleLabel?.text = "btn temp"
print("fill data")
}
#IBAction func btnTempClick(_ sender: Any) {
print("Click")
}
}
All is working - view is visible, btnTempClick is called, but fillData is not working. It does not change content of button, there is still default text "Button". How to fix this?
Just to make it official removing it from comment and putting it as an answer.
have you tried using self.btnTemp.setTitle("btn temp", for:.normal)?
i think you can try with setTitle forControlState

objc custom view with nib not visible on tableviewcell

I have an UITableViewCell contain a custom view, the custom view contains two label. The view hierarchical likes that:
|---Label 1
XIB---MyTableViewCell---MyView---|
|---Label 2
But run application, just shown 'MyView', Label 1 and Label 2 not visible! If I wrote code on viewDidLoad, take 'MyView' as viewController.view's subview, the label 1 and 2 is appeared. Hope you can help me.
Have you tried this?
Create MyView like this:
class MyView: UIView {
#IBOutlet weak var _label1: UILabel!
#IBOutlet weak var _label2: UILabel!
// Our custom view from the XIB file
var view: UIView!
//MARK: - Setup -
private func xibSetup() {
view = loadViewFromNib()
view.frame = bounds
// Make the view stretch with containing view
view.autoresizingMask = [.FlexibleWidth, .FlexibleHeight]
addSubview(view)
}
private func loadViewFromNib() -> UIView {
let bundle = NSBundle(forClass: self.dynamicType)
let nib = UINib(nibName: "MyView", bundle: bundle)
let view = nib.instantiateWithOwner(self, options: nil)[0] as! UIView
return view
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
xibSetup()
}
override func awakeFromNib() {
super.awakeFromNib()
}
func setLabeles(lbl1: String, lbl2: String) {
_label1!.text = lbl1
_label2!.text = lbl2
}
}
Make Label1 and Label2 outlet to respective ones.
Then add it to custom tableViewCell as:
(Pink colour is for highlighting!)
Make My View outlet.
Custom cell code:
#IBOutlet weak var _myView: MyView!
class func identifier() -> String {
return "CustomTableViewCellId"
}
class func nib() -> UINib {
let nib = UINib(nibName: "CustomTableViewCell", bundle: NSBundle.mainBundle())
return nib
}
//MARK: Public Methods
func setCellIndexLabel(index: Int) {
_myView!.setLabeles("lbl1: \(index)", lbl2: "lbl2: \(index)")
}
Then there is no need to do extra anything in table view, just do:
In viewDidLoad()-
tableView!.registerNib(CustomTableViewCell.nib(), forCellReuseIdentifier: CustomTableViewCell.identifier())
//then
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
// Configure the cell...
let cell = tableView.dequeueReusableCellWithIdentifier(CustomTableViewCell.identifier(), forIndexPath: indexPath) as! CustomTableViewCell
cell.setCellIndexLabel(indexPath.row)
return cell
}
And you will get this:
Is this what you are looking for?
Forgive me for providing swift code, but it's not much different that objective-c!

Resources