Search code examples
swiftinheritanceuiviewcontrollerinitialization

Missing argument for parameter 'coder' in call when presenting a view from CollectionViewCell


I have a ViewController with a CollectionView inside and a CollectionViewCell with a TableView inside. When the user click on a TableViewCell i want to present a ViewController showing a detailed view of the user's task but i get this error "Missing argument for parameter 'coder' in call" at this line let vc = MyTasksDetailController(). Here's my code :

ProfileController

final class ProfileController: UIViewController {
   
private var collectionView: UICollectionView?
    let layout = UICollectionViewFlowLayout()
        layout.scrollDirection = .vertical
        layout.minimumLineSpacing = 1
        layout.minimumInteritemSpacing = 1
        layout.sectionInset = UIEdgeInsets(top: 0, left: 1, bottom: 0, right: 1)
        let size = (view.width - 4)/3
        layout.itemSize = CGSize(width: size, height: size)
        collectionView = UICollectionView(frame: .zero,
                                          collectionViewLayout: layout)
        // Headers
        collectionView?.register(ProfileInfoHeader.self,
                                 forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader,
                                 withReuseIdentifier: ProfileInfoHeader.identifier)
        
        collectionView?.register(MyTasksCollectionCell.self,
                                 forCellWithReuseIdentifier: MyTasksCollectionCell.identifier)
        
        collectionView?.delegate = self
        collectionView?.dataSource = self
        guard let collectionView = collectionView else {
            return
     }
        view.addSubview(collectionView)
    }
extension ProfileController: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
    func numberOfSections(in collectionView: UICollectionView) -> Int {
        return 1 
    }
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return 1 
    }
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: MyTasksCollectionCell.identifier,
                                                      for: indexPath)as! MyTasksCollectionCell
        
            return cell
        
    }

MyTaskCollectionCell

class MyTasksCollectionCell: UICollectionViewCell, UITableViewDelegate, UITableViewDataSource {

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
                 tableView.deselectRow(at: indexPath, animated: true)
 let task = {() -> Add in
        switch (displayedTask) {
          case .current:
             // First segment tapped
            return self.tasks[indexPath.row]
          case past:
             // Second segment tapped
            return self.pastTasks[indexPath.row]
        }
    }()
     let vc = MyTasksDetailController() //ERROR HERE: Missing argument for parameter 'coder' in call : Insert 'coder: <#NSCoder#>'
self.present(vc, animated: true, completion: nil)

}

MyTaskTableCell

class MyPostsTableCell: UITableViewCell {
    
    var setdescriptionTitleLabel: String? {
        didSet {
            descriptionTitleLabel.text = setdescriptionTitleLabel ?? ""
        }
    }
    var setdateLabel: String? {
        didSet {
            dateLabel.text = setdateLabel ?? ""
        }
    }
    var sethourLabel: String? {
        didSet {
            hourLabel.text = sethourLabel ?? ""
        }
    }
    var setDateIcon: UIImage? {
        didSet {
            dateIcon.image = UIImage()
        }
    }
    var setHourIcon: UIImage? {
        didSet {
            hourIcon.image = UIImage()
        }
    }

MyTasksDetailController

class MyTasksDetailController: UIViewController  {

internal var task: Add? {
       didSet {
            if let task = task {
                setDescriptionLabel = task.description
                setDescriptionTitleLabel = task.descriptionTitle
             }
        }
    }    
    var setDescriptionLabel: String? {
        didSet {
            descriptionLabel.text = setDescriptionLabel ?? ""
        }
    }
    var setdescriptionTitleLabel: String? {
        didSet {
            descriptionTitleLabel.text = setdescriptionTitleLabel ?? ""
        }
    }
     let descriptionLabel: UILabel = {
        let label = UILabel()
        label.translatesAutoresizingMaskIntoConstraints = false
        label.font = UIFont.systemFont(ofSize: 28, weight: .bold)
        label.textAlignment = .center
        return label
    }()
    
     let descriptionTitleLabel: UILabel = {
        let label = UILabel()
        label.translatesAutoresizingMaskIntoConstraints = false
        label.font = UIFont.systemFont(ofSize: 18, weight: .bold)
        label.textAlignment = .center
        label.numberOfLines = 3
        return label
    }()
   let container: UIView = {
        let v = UIView()
        v.translatesAutoresizingMaskIntoConstraints = false
        v.clipsToBounds = true
        v.backgroundColor = .white
        v.layer.cornerRadius = 24

        v.backgroundColor =
            // 1
            UIColor { traitCollection in
              // 2
              switch traitCollection.userInterfaceStyle {
              case .dark:
                // 3
                 v.layer.borderColor = UIColor.label.cgColor
                return UIColor.systemBackground

              default:
                // 4
                 v.layer.borderColor = UIColor.black.cgColor
                return UIColor.systemBackground
              }
            }
        return v
    }()
 lazy var stackContainer: UIStackView = {
       let stackContainer = UIStackView(arrangedSubviews: [stackDesLabel, stackDesTitLabel])
        stackContainer.translatesAutoresizingMaskIntoConstraints = false
        stackContainer.axis = .vertical
        stackContainer.distribution  = UIStackView.Distribution.fillEqually
       return stackContainer
   }()
    lazy var stackDesLabel: UIStackView = {
        let stackDesLabel = UIStackView(arrangedSubviews: [descriptionLabel])
        stackDesLabel.translatesAutoresizingMaskIntoConstraints = false
        stackDesLabel.axis = .vertical
        stackDesLabel.distribution  = UIStackView.Distribution.fillProportionally
        return stackDesLabel
    }()
    lazy var stackDesTitLabel: UIStackView = {
       let stackDesTitLabel = UIStackView(arrangedSubviews: [descriptionTitleLabel])
        stackDesTitLabel.translatesAutoresizingMaskIntoConstraints = false
        stackDesTitLabel.axis = .horizontal
        stackDesTitLabel.distribution  = UIStackView.Distribution.fillEqually
       return stackDesTitLabel
   }()
    override func viewDidLoad() {
        view.addSubview(stackDesLabel)
        view.addSubview(stackDesTitLabel)

        stackContainer.leadingAnchor.constraint(equalTo: container.leadingAnchor).isActive = true
        stackContainer.trailingAnchor.constraint(equalTo: container.trailingAnchor).isActive = true
        stackContainer.centerYAnchor.constraint(equalTo: container.centerYAnchor).isActive = true
        stackContainer.centerXAnchor.constraint(equalTo: container.centerXAnchor).isActive = true
        stackContainer.heightAnchor.constraint(equalTo: container.heightAnchor).isActive = true
        stackContainer.widthAnchor.constraint(equalTo: container.widthAnchor).isActive = true
        
        stackDesTitLabel.topAnchor.constraint(equalTo: stackContainer.topAnchor, constant: 50).isActive = true
         stackDesTitLabel.widthAnchor.constraint(equalTo: stackContainer.widthAnchor).isActive = true
        stackDesTitLabel.centerXAnchor.constraint(equalTo: stackContainer.centerXAnchor).isActive = true
        stackDesLabel.topAnchor.constraint(equalTo: stackDesTitLabel.bottomAnchor, constant: 50).isActive = true
        stackDesLabel.leadingAnchor.constraint(equalTo: stackContainer.leadingAnchor, constant: 5).isActive = true
        stackDesLabel.widthAnchor.constraint(equalTo: stackContainer.widthAnchor).isActive = true
 }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

Add(Data struct)

struct Add {
    static var details: Add = Add()
    var descriptionTitle: String = ""
    var description: String = ""
    var id: String?
    var date: String = ""
    var hour: String = ""

    func getDict() -> [String: Any] {
              let dict = [
                          "descriptionTitle": self.descriptionTitle,
                          "description": self.description,
                          "date": self.date,
                          "hour": self.hour,
                ] as [String : Any]
               return dict
         }

}

Solution

  • As I suspected, the issue is in MyTasksDetailController with your implementation of

    required init?(coder aDecoder: NSCoder)

    If you just want a quick solution, you can do one of 2 things based on what makes the most sense to your application:

    1. Remove the required init?(coder aDecoder: NSCoder)
    2. Add your own initializer like this:
      init() {
          // Keep nil if you are not initializing from XIB
          super.init(nibName: nil, bundle: nil)
      }
      

    Explanation

    What you tried to achieve breaks the rules of initialization inheritance I believe which is why you get that error.

    I am looking at all rules from here.

    When you do this: class MyTasksDetailController: UIViewController - you are automatically inheriting the initializers of from UIViewController if you follow these rules:

    Automatic Initializer Inheritance

    Rule 1

    If your subclass doesn’t define any designated initializers, it automatically inherits all of its superclass designated initializers.

    Rule 2

    If your subclass provides an implementation of all of its superclass designated initializers—either by inheriting them as per rule 1, or by providing a custom implementation as part of its definition—then it automatically inherits all of the superclass convenience initializers.

    These rules apply even if your subclass adds further convenience initializers.

    So now when you added:

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    

    It breaks rule 1, you are adding your own designated initializer and so you lost all other initializers you inherited previous so you get this error:

    Missing argument for parameter 'coder' in call Swift Initialization Inheritance Classes UIViewController

    So now you have 2 options as described earlier.

    1. Remove the required init?(coder aDecoder: NSCoder)

    This version of init is needed if you create view controllers in storyboard as it is called automatically called in that situation. If you are not planning on doing that you can remove it. If you are planning on using it in storyboard, you need to complete it:

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }
    

    2. Add your own designated initializer

    By adding required init?(coder aDecoder: NSCoder), you lost the default initializer you were inheriting before and so now you need to make your own:

    init() {
        // Initialize your local vars
        
        // Call designated initializer from the super class
        // Keep nil if you are not initializing from XIB
        super.init(nibName: nil, bundle: nil)
    }
    

    Either of these should fix your problem:

    Missing argument for parameter 'coder' in call Swift Initializer Inheritance

    Update with final thoughts

    1. The above should fix your errors with initialization
    2. However, you will have an error on the next line self.present(vc, animated: true, completion: nil)

    In short, present (documentation reference) can only be called by view controllers.

    In your code MyTasksCollectionCell is a UICollectionViewCell so you need to notify the view controller that your collection view is in that a cell was tapped and the view controller needs to handle presenting the new view.

    You can do this in two ways:

    1. Delegates - give responsibility to your view controller to handle cell tap and present another view controller
    2. Observers - notify your view controller to handle cell tap and present another view controller
    3. Closures

    One of these should help in your situation.

    These are just quick links to give you an idea but I suggest googling bit more on them to see which is the best for your situation