How can I override the method of view in Controller? - ios

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 :-)")
}
}

Related

Make ViewController become a listener of a button clicked, located inside a custom view which located inside a TableViewCell xib?

I have a ViewController which holds a UITableView.
I created a UITableViewCell class named - CustomTableViewCell and connected it to a xib file with the same name. Inside this xib file I have a custom view named - CustomTextFieldView, and this custom view has a button in it, named - listButton.
My goal is that the ViewController will be a listener of the listButtonPressed action so that later help me to expand and collapse the table view cell every time this button pressed.
This is what i have tried
But I got exception when the relevant button clicked
Thanks in adavanced
class CustomTableViewCell: UITableViewCell {
#IBOutlet weak var customView: CustomTextFieldView!
#IBOutlet weak var label: UILabel!
static let identifier = "CustomTableViewCell"
static func nib() -> UINib {
return UINib(nibName: "CustomTableViewCell", bundle: nil)
}
override func awakeFromNib() {
super.awakeFromNib()
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
func configure(withCell cell : PTTableModel) {
self.customView.initView(labelText: cell.label, placeHolder: cell.label, type: .name, hasList: cell.hasList)
}
}
protocol CustomTextFieldDelegate : class {
func didPressButton(button: UIButton)
}
class CustomTextFieldView: UIView {
#IBOutlet weak var listButton: UIButton!
#IBOutlet weak var bottomLine: UIView!
#IBOutlet weak var textField: UITextField!
#IBOutlet weak var topLabel: UILabel!
#IBOutlet var contenView: UIView!
var delegate : CustomTextFieldDelegate!
static let identifier = "CustomTextFieldView"
static func nib() -> UINib {
return UINib(nibName: "CustomTextFieldView", bundle: nil)
}
override init(frame: CGRect) {
super.init(frame : frame)
commonInit()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
#IBAction func listButtonClicked(_ sender: Any) {
delegate.didPressButton(button: listButton)
}
}
class PetProfileDeatilsViewController: BaseUiViewController, UITableViewDelegate, UITableViewDataSource, CustomTextFieldDelegate {
#IBOutlet weak var tableView: UITableView!
#IBOutlet weak var nextButton: UIButton!
private var cells = ViewModel.createTableSction()
override func viewDidLoad() {
super.viewDidLoad()
headerView.initView(mainLabel: "Create", subLabel: "a profile")
let vc = CustomTextFieldView()
vc.delegate = self
self.view.addSubview(vc)
tableView.register(CustomTableViewCell.nib(), forCellReuseIdentifier: CustomTableViewCell.identifier)
}
func didPressButton(button: UIButton) {
print("worked")
}

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)

Nib awakefromNib() crashes

I have a class with a label and a collectionView, here is my code.
class CardSensors: UIView {
#IBOutlet weak var botName: UILabel!
#IBOutlet weak var sensorsCollectionView: UICollectionView!
var viewModel: NewsFeedViewModel! {
didSet {
setUpView()
}
}
override func awakeFromNib() {
super.awakeFromNib()
let nibName = UINib(nibName: "SensorCollectionViewCell", bundle:nil)
sensorsCollectionView.register(nibName, forCellWithReuseIdentifier: "SensorCollectionViewCell")
// Initialization code
}
func setUpView() {
botName.text = viewModel.botName
}
}
Obviously my cell has the correct Id and the correct reuse identifier.
And when I try to call the nib like this.
let sensorView = CardSensors()
sensorView.awakeFromNib()
I get Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value. I tried to force the values but I always get some errors.
What am I doing wrong?
As others have mentioned you should not call awakeFromNib() manually, it is something similar to how viewDidLoad() and viewWillAppear() get called automatically, the OS will fire it to let you know that your view has been loaded.
To initialize your view you could use the following static function, that allows you to call on the class to create a new instance of your CardSensors.
class CardSensors: UIView {
#IBOutlet weak var botName: UILabel!
#IBOutlet weak var sensorsCollectionView: UICollectionView!
var viewModel: NewsFeedViewModel! {
didSet {
setUpView()
}
}
static func loadFromNib() -> CardSensors {
return Bundle.main.loadNibNamed("CardSensors", owner: nil, options: nil)?.first as! CardSensors
}
override func awakeFromNib() {
super.awakeFromNib()
sensorsCollectionView.register(nibName, forCellWithReuseIdentifier: "SensorCollectionViewCell")
}
func setUpView() {
botName.text = viewModel.botName
}
}
let sensorView = CardSensors.loadFromNib()
You shouldn't call awakefromNib by yourself because awakefromNib will be called after you init your view by using UINib or load Nib from Bundle.main.loadNibNamed.
You can fix your error by using this:
class CardSensors: UIView {
#IBOutlet weak var botName: UILabel!
#IBOutlet weak var sensorsCollectionView: UICollectionView!
var viewModel: NewsFeedViewModel! {
didSet {
setUpView()
}
}
override init CardSensors() {
super.init()
let nibName = UINib(nibName: "SensorCollectionViewCell", bundle:nil)
sensorsCollectionView.register(nibName, forCellWithReuseIdentifier: "SensorCollectionViewCell")
// Initialization code
}
func setUpView() {
botName.text = viewModel.botName
}
}

Pass Data From View Controller to View in MVC - Swift

Passing data between UIViewControllers is fairly straightforward, but passing data from a controller to a view is something I can't seem to figure out.
In my specific case the user taps on an "event" cell, and transitions into a detailed view.
How do I pass the "event" from the tapped cell, to the detail view?
// Inside Starting View Controller
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let vc = EventDetailController()
let vcView = vc.view as! EventDetailView
vcView.event = model.events[indexPath.item]
navigationController?.pushViewController(vc, animated: true)
}
// Second View
class EventDetailView: UIView {
var event: Event?
let model = EventsModel()
override init(frame: CGRect) {
super.init(frame: frame)
print("selected event: \(event)") <<<<--- Prints "nil"
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
// Second Controller
class EventDetailController: UIViewController {
var eventDetailView: EventDetailView!
override func loadView() {
super.loadView()
view = eventDetailView
}
override func viewDidLoad() {
super.viewDidLoad()
}
}
You are passing data which is not the correct way to do this. You should never access the view until it's been shown or loaded correctly.
In your class EventDetailController add the property
var event: Event?
and replace this method like below
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let vc = EventDetailController()
vc.event = model.events[indexPath.item]
navigationController?.pushViewController(vc, animated: true)
}
and
override func viewDidLoad() {
super.viewDidLoad()
eventDetailView.event = self.event
}
Hope it helps
EDIT
In load view method add the following line at the start of the method
`eventDetailView = EventDetailView(frame:CGRect(x:0,y:0,width:UIScreen.main.bounds.width,height:UIScreen.main.bounds.height))`
// First View Controller
let vc = EventDetailController()
vc.eventDetailView.event = "TEST"
navigationController?.pushViewController(vc, animated: true)
// Second View
class EventDetailView: UIView {
var event: String?
var model = EventsModel()
override init(frame: CGRect) {
super.init(frame: frame)
print("selected event: \(event)")
}
override func encode(with aCoder: NSCoder) {
super.encode(with: aCoder)
aCoder.encode(event as Any?, forKey: "event")
aCoder.encode(model as Any?, forKey: "model")
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
event = aDecoder.decodeObject(forKey: "event") as? String? ?? ""
model = aDecoder.decodeObject(forKey: "model") as! EventsModel? ?? EventsModel()
}
}
// Second Controller
class EventDetailController: UIViewController {
var eventDetailView = EventDetailView()
override func loadView() {
super.loadView()
view = eventDetailView
}
override func viewDidLoad() {
super.viewDidLoad()
print(eventDetailView.event)
}
}

Cannot assign value of type 'UIViewController' to type 'LeftViewController!'. Why?

I have a class LeftViewController:
protocol LeftMenuProtocol: class {
func actionOpenHome()
func actionOpenTakeSnap()
func actionOpenGallery()
func actionOpenProfiles()
func actionOpenSettings()
func actionOpenHelp()
func actionOpenContactUs()
func actionOpenBarCode()
func actionOpenAbout()
func actionCloseSideVC()
}
class LeftViewController : UIViewController {
#IBOutlet weak var tableView: UITableView!
var leftMenuDelegate : LeftMenuProtocol?
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
self.view.layoutIfNeeded()
}
}
I am trying to add it into another class like this
var leftViewController: LeftViewController!
leftViewController = self.storyboard!.instantiateViewControllerWithIdentifier("LeftViewController")
self.addChildViewController(leftViewController)
self.view!.addSubview(leftViewController.view!)
leftViewController.didMoveToParentViewController(self)`
But getting this error. Somebody knows what is wrong?
change the variable declaration to
leftViewController = self.storyboard!.instantiateViewControllerWithIdentifier("LeftViewController") as! LeftViewController
You need to explicitly cast it.

Resources