6 Replies
      Latest reply: Dec 5, 2016 12:09 PM by Gargoyle RSS
      Gargoyle Level 2 Level 2 (70 points)

        In an extranal library that I pull in via a pod I've got this class defined.

         

        open class ExpandableDatePickerCell : UITableViewCell {
            public static func reusableCell(for indexPath: IndexPath, in tableView: UITableView) -> ExpandableDatePickerCell {
                return tableView.dequeueReusableCell(withIdentifier: ExpandableDatePickerCell.identifier, for: indexPath) as! ExpandableDatePickerCell
            }
        }
        

         

        Obviously there's some other stuff, that's the interesting method.  Then in my project I inherit from that cell.

         

        final class UserProfileDatePickerCell : ExpandableDatePickerCell {
        

         

        When I hit tableView(_:cellForRowAt:) I'm trying to now get one of those cells, but it's crashing.  I call it like so:

         

        let cell = UserProfileDatePickerCell.reusableCell(for: indexPath, in: tableView) as! UserProfileDatePickerCell
        

         

        But it's getting a SIGABRT with the message:

         

        Could not cast value of type 'ExpandableDatePicker.ExpandableDatePickerCell' (0x103ed3e60) to 'TeamKnect.UserProfileDatePickerCell' (0x103807c78).

         

        I don't understand why it can't do the cast since it's a child.

        • Re: Unable to cast between types
          QuinceyMorris Level 7 Level 7 (4,275 points)

          Well, you can't *cast* a superclass to a subclass, because the object (of the superclass) doesn't actually have the additional behavior of the subclass.

           

          What you're going to have to do is "tell" the reusableCell method what subclass to create.

            • Re: Unable to cast between types
              eskimo Apple Staff Apple Staff (7,805 points)

              What you're going to have to do is "tell" the reusableCell method what subclass to create.

              Specifically, dequeueReusableCell(withIdentifier:for:) returns a cell of the type determined by looking up the identifier parameter in the registered cell classes for table.  In most cases you set this up in the interface editor by:

              1. Adding a prototype cell to the table view

              2. Using the identity inspector (View > Utilities > Identify Inspector) to set the class for it

              3. Using the attributes inspector (View > Utilities > Attributes Inspector) to set the reuse identifier for it

              Share and Enjoy

              Quinn “The Eskimo!”
              Apple Developer Relations, Developer Technical Support, Core OS/Hardware
              let myEmail = "eskimo" + "1" + "@apple.com"

                • Re: Unable to cast between types
                  QuinceyMorris Level 7 Level 7 (4,275 points)

                  Yes, except that because this is crossing a bundle boundary, it's an API decision whether to supply an identifier as a parameter (which needs to be managed depending where the table is defined) or a class (which might mean using registerClass in the framework). I don't think we got enough information to make it clear what the best approach might be.

                    • Re: Unable to cast between types
                      Gargoyle Level 2 Level 2 (70 points)

                      So the library was registering the table cells to make things easier on the caller, so it has a function like so:

                       

                      static func registerCells() {
                          tableView.register(ExpandableDatePickerCell.self, forCellReuseIdentifier: ExpandableDatePickerCell.identifier)
                          tableView.register(ExpandableDatePickerTimeZoneCell.self, forCellReuseIdentifier: ExpandableDatePickerTimeZoneCell.identifier)
                      }
                      

                       

                      And then in viewDidLoad() of the caller I can just call that.  How would I modify the signature of that method where I could optionally pass in a different class to use if they decided to subclass the time zone cell, for example?

                        • Re: Unable to cast between types
                          eskimo Apple Staff Apple Staff (7,805 points)

                          QuinceyMorris wrote:

                          Yes, except that because this is crossing a bundle boundary …

                          Ah, yes, missed that bit.


                          Gargoyle wrote:

                          How would I modify the signature of that method where I could optionally pass in a different class to use if they decided to subclass the time zone cell, for example?

                          Do you need to?  register(_:forCellReuseIdentifier:) allows you to replace a registration (by calling it again with the same reuse identifier), so why not have your viewDidLoad() call registerCells() and then replace the registrations it wants to override?

                          Share and Enjoy

                          Quinn “The Eskimo!”
                          Apple Developer Relations, Developer Technical Support, Core OS/Hardware
                          let myEmail = "eskimo" + "1" + "@apple.com"