nil value in protocol delegation in tableview and customcell - swift

Im new in Swift, sorry.
I want push value from customcell class to TableViewController class.
But always get "nil".
protocol DetailsDelegate {
func labelDelegateMethodWithString(controller: TableViewController) -> String }
class TableViewController: UITableViewController {
var delegate: DetailsDelegate?
#IBAction func butttonFunc(sender: UIButton) {
let importNumber = delegate!.labelDelegateMethodWithString(self)
println(importNumber)
}
and here my custom cell class:
class TableViewCell: UITableViewCell, UITextFieldDelegate, DetailsDelegate {
func labelDelegateMethodWithString(controller: TableViewController) -> String {
return "efgfsd"
}
What im doing wrong? thanks.

Related

Delegate not get called

protocol WeatherManagerDelegate {
func didUpdateWeater(weather: ConsolidatedWeather)
func didFailWithError(error: Error)
}
ViewController: where i am setting value didSelectRowAt and using performSegue going to another viewController
class WeatherListViewController: UIViewController {
var delegate: WeatherManagerDelegate?
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let index = weatherViewModel.didSelect(at: indexPath.row)
self.delegate?.didUpdateWeater(weather: index)
performSegue(withIdentifier: K.DetailsView.segueIndentifier, sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let destinationVc = segue.destination as! DetailsViewController
}
}
this is my ViewModel calss: from my ViewModel, I will send value to ViewController and Update UI
class DetailsWeatherViewModel{
var w = WeatherListViewController()
func a(){
print("aaa")
w.delegate = self
}
}
extension DetailsWeatherViewModel: WeatherManagerDelegate{
func didUpdateWeater(weather: ConsolidatedWeather) {
weatherData = weather
print("weatherData: \(String(describing: weatherData))")
}
func didFailWithError(error: Error) {
print(error)
}
}
what I am doing wrong...????
You should be careful of memory leaks when using delegate pattern. I think you can solve this problem by making protocol limit to class and declare property by weak var. Although WeatherListViewController disappeared, WeatherListViewController and DetailsWeatherViewModel are not likely to be deinit unless you use weak reference. Try this.
protocol WeatherManagerDelegate : class {
func didUpdateWeater(weather: ConsolidatedWeather)
func didFailWithError(error: Error)
}
weak var delegate: WeatherManagerDelegate?
If you are following MVVM architecture then you can create a viewModel object inside your viewcontroller and then use the updated values in VM directly using VM object.
Else if you want to use delegate then you need to write the protocols in viewModel and use it in VC. You shouldn't be creating Viewcontroller object inside the Viewmodel.

#IBDesignable with protocol

I have a UIview xib within a view controller, UIview class have two buttons with protocol function, but the protocol function never called when I press button, storyboard image like below
protocol method like below
import UIKit
#objc protocol TopViewDelegate: NSObjectProtocol {
#objc optional func pressRefreshButton()
#objc optional func pressMenuButton()
}
UIView class
#IBDesignable class OnJob_Top: UIView,TopViewDelegate {
weak var delegate : TopViewDelegate? = nil
#IBAction func refreshButtonTouchUpInside(_ sender: UIButton) {
delegate?.pressRefreshButton!()
}
#IBAction func menuButtonTouchUpInside(_ sender: UIButton) {
delegate?.pressMenuButton!()
print("come come")
}
view controller class
class HomeViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let topView = OnJob_Top()
topView.delegate = self
}
}
extension HomeViewController:TopViewDelegate {
func pressMenuButton() {
print("come") // never come here
}
func pressRefreshButton() {
print("come") // never come here
}
}
Consider this code:
let topView = OnJob_Top()
topView.delegate = self
In the first line, you create a completely new OnJob_Top view.
In the second line, you make it the delegate.
In the third line... but there is no third line. The view vanishes in a silent puff of smoke. It is useless.
Meanwhile, the view in the storyboard never gets a delegate. So its delegate methods are never called.

Delegates is not working?

here is my protocol definition.
protocol ActivityIndicatorDelegate: class {
func showIndicator()
func hideIndicator()
func barcodeError()
func categoryError()
func descError()
func reasonError()
func costError()
}
Then in my Custom cell class I create weak reference and I call delegate function
class ProductTableViewCell: UITableViewCell {
weak var indicatorDelegate: ActivityIndicatorDelegate?
#IBAction func stockUpdate(_ sender: Any) {
indicatorDelegate?.categoryError()
}
}
Then in my UITableViewController class
class ProductTableViewController:
UITableViewController,ActivityIndicatorDelegate{
override func viewDidLoad() {
super.viewDidLoad()
let cellDelegate = ProductTableViewCell()
cellDelegate.indicatorDelegate = self
}
func categoryError() {
//self.showAlert(alertTitle: "Error!", alertMessage: "Category Should not be empty")
print("Error")
}
}
I have written all these in a single file. What I'm doing wrong here? Can some one help me to solve this. Thanks in advance.
You should not set the delegate in viewDidLoad. This will only set the delegate of the cell that you just created, instead of all the cells in the table view.
You should do this in celForRowAtIndexPath:
let cell = tableView.dequeue...
// configure the cell...
cell.indicatorDelegate = self

Setting delegate of another class with screen view to self

I'm fairly new at iOS programming. I have this setup:
ViewController view on IB, with class ViewController
SecondController view on IB, with class secondController
I have protocol:
protocol SecondControllerDelegate {
func getSomething() -> String
}
and I have delegate variable on SecondController:
class secondController: UIViewController {
var delegate: SecondControllerDelegate?
#IBOutlet weak var labelStatus: UILabel!
override func ViewDidLoad() {
super.viewDidLoad()
}
#IBAction func buttonTouch(sender: AnyObject) {
labelStatus.text = delegate?.getSomething()
}
func try () {
labelStatus.text = "testing"
}
}
Now, according to the hints everywhere, in order so I can call delegate?.getSomething() at SecondController.buttonTouch(), I need to set like this on viewController:
class ViewController: UIViewController, SecondControllerDelegate {
override func viewDidLoad () {
super.viewDidLoad()
SecondController.delegate = self
}
func doSomething () -> String {
return "testing"
}
}
But this generates error 'SecondController.type' does not have a member named 'delegate'.
Some other websites say:
class ViewController: UIViewController, SecondControllerDelegate {
var secondController = SecondController()
override func viewDidLoad () {
super.viewDidLoad()
secondController.delegate = self
}
func doSomething () -> String {
return "testing"
}
}
With this, there are no error. But if I do something on the second screen that should call the delegate, it doesn't call the delegate, like the SecondController is two different objects (one is created by StoryBoard, one is created manually within the ViewController), i.e. the labelStatus that should have changed to "testing", doesn't change at all. But it changes if function try() is called. How am I supposed to do this?
EDIT: I forgot to mention that I used NavigationController, and segue to transition from first screen to second screen.
Because you try to learn how to build a delegate in Swift, I have written you a plain delegate example below
protocol SecondViewControllerDelegate {
func didReceiveInformationFromSecondViewcontroller (information: String)
}
class ViewController: UIViewController, SecondViewControllerDelegate {
func openSecondViewController () {
if let secondViewControllerInstance: SecondViewController = storyboard?.instantiateViewControllerWithIdentifier("SecondViewController") as? SecondViewController {
secondViewControllerInstance.delegate = self
navigationController?.pushViewController(secondViewControllerInstance, animated: true)
}
}
func didReceiveInformationFromSecondViewcontroller(information: String) {
////Here you get the information, after sendInfoToViewController() has been executed
}
}
class SecondViewController: UIViewController {
var delegate: SecondViewControllerDelegate?
func sendInfoToViewController () {
delegate?.didReceiveInformationFromSecondViewcontroller("This ist the information")
}
}
UPDATE
Following the same thing in using Storyboard Segues
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if let secondViewControllerInstance: SecondViewController = segue.destinationViewController as? SecondViewController {
secondViewControllerInstance.delegate = self
}
}

How do you access a UIViewController function from within a UICollectionCell?

I have a function within a UICollectionViewCell that requires access to the
hosting UIViewController. Currently 'makeContribution()' can't be accessed:
What is the proper way of accessing the host UIViewController that has the desired function?
Thanks to the insightful responses, here's the solution via delegation:
...
...
...
{makeContribution}
This is a mildly controversial question - the answer depends a little on your philosophy about MVC. Three (of possibly many) options would be:
Move the #IBAction to the view controller. Problem solved, but it might not be possible in your case.
Create a delegate. This would allow the coupling to be loose - you could create a ContributionDelegate protocol with the makeContribution() method, make your view controller conform to it, and then assign the view controller as a weak var contributionDelegate: ContributionDelegate? in your cell class. Then you just call:
contributionDelegate?.makeContribution()
Run up the NSResponder chain. This answer has a Swift extension on UIView that finds the first parent view controller, so you could use that:
extension UIView {
func parentViewController() -> UIViewController? {
var parentResponder: UIResponder? = self
while true {
if parentResponder == nil {
return nil
}
parentResponder = parentResponder!.nextResponder()
if parentResponder is UIViewController {
return (parentResponder as UIViewController)
}
}
}
}
// in your code:
if let parentVC = parentViewController() as? MyViewController {
parentVC.makeContribution()
}
Well, CollectionView or TableView?
Anyway, Set your ViewController as a delegate of the cell. like this:
#objc protocol ContributeCellDelegate {
func contributeCellWantsMakeContribution(cell:ContributeCell)
}
class ContributeCell: UICollectionViewCell {
// ...
weak var delegate:ContributeCellDelegate?
#IBAction func contributeAction(sender:UISegmentedControl) {
let isContribute = (sender.selectedSegmentIndex == 1)
if isContribute {
self.delegate?.contributeCellWantsMakeContribution(self)
}
else {
}
}
}
class ViewController: UIViewController, UICollectionViewDataSource, ContributeCellDelegate {
// ...
func collectionView(_ collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
cell = ...
if cell = cell as? ContributeTableViewCell {
cell.delegate = self
}
return cell
}
// MARK: ContributeCellDelegate methods
func contributeCellWantsMakeContribution(cell:ContributeCell) {
// do your work.
}
}