0

I currently have a data type called MovieCard that holds all the data regarding a movie and it needs to be displayed onto a card, aka a UIView. I have created a custom UIView class called MovieCardView and have linked it to a XIB file through File's Owner. The MovieCardView contains a few labels and an image view.

My question is, what do I have to write inside the MovieCardView file (I'm confused regarding bundle loading) and how can I pass in the movie card to the MovieCardView? I want the MovieCardView to utilise the data present in the movie card to set some values for the labels and ImageView in the MovieCardView.

This is what is in my view controller and what I wish to happen

func koloda(_ koloda: KolodaView, viewForCardAt index: Int) -> UIView {
        
        let movieCard = cards[index]

        return MovieCardView(movieCard: movieCard)
    }

And my current view file looks like this

class MovieCardView: UIView {
        @IBOutlet var moviePoster: UIImageView!
        @IBOutlet var movieTitle: UILabel!
        @IBOutlet var movieYear: UILabel!

       override init(frame: CGRect) {
           super.init(frame: frame)
           commonInit()
       }

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

      func commonInit() {
         Bundle.main.loadNibNamed("MovieCardView", owner: self, options: nil)
       }

}

I'm not sure how to modify the code to allow me to pass in the movieCard from my view controller and programatically set the IBOutlets to certain properties of my movieCard. I also do not wish to programatically change the frame at all. Any help would be appreciated. Thank you!

Update I have tried the following

import UIKit
import Kingfisher

class MovieCardView: UIView {
    @IBOutlet weak var poster: UIImageView!
    
    let movieCard: MovieCard
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }
   required init?(coder aDecoder: NSCoder) {
       super.init(coder: aDecoder)
       commonInit()
   }

    func commonInit() {
        Bundle.main.loadNibNamed("MovieCardView", owner: self, options: nil)
        
        guard let posterString = movieCard.poster_path else {
            return
        }
        
        let urlString = "https://image.tmdb.org/t/p/w300" + posterString
        
        guard let posterImageURL = URL(string: urlString) else {
            return
        }
        
        poster.kf.setImage(with: posterImageURL)
        
    }

 
}

I am getting errors saying Property 'self.movieCard' not initialized at super.init call for both override init and required init methods. How do I rectify this? Thanks!

user844288
  • 17
  • 1
  • 6

3 Answers3

0

I am getting errors saying Property 'self.movieCard' not initialized at super.init call for both override init and required init methods.

The Swift compiler will prevent you from creating objects that aren't fully initialized, so before you initialize the superclass you need to provide values for your class's properties.

How do I rectify this?

You need to either provide a value for your movieCard property in your initializers before you call super.init, or make movieCard optional so that the compiler will let you leave it unset until later. For example, you could change the declaration to:

var movieCard = MovieCard()

to just give it an empty MovieCard instance, and make it var so that you can change it later on. You could also mark the variable lazy so that the empty MovieCard isn't actually created unless you try to access the value of the property:

lazy var movieCard = MovieCard()
Caleb
  • 124,013
  • 19
  • 183
  • 272
0

Copy/paste following code into your project.

public extension UIView {
    
    class var bundle: Bundle { Bundle(for: self) }
    class var identifier: String { String(describing: self) }
    
    class var nibName: String { identifier }
    class var nib: UINib { UINib(nibName: nibName, bundle: bundle) }

    class func instantiateFromNib<T: UIView>() -> T {
        return nib.instantiate(withOwner: nil, options: [:])[0] as! T
    }
}

Now you can do this when you want to instantiate your custom UIView.

let view: MovieCardView = MovieCardView.instantiateFromNib()

From MovieCardView you can remove all of your init overrides and commonInit methods. Add one method like this.

func populateMovieCard(_ movieCard: MovieCard) {
    // populate your details on to UI here
}
Tarun Tyagi
  • 9,364
  • 2
  • 17
  • 30
  • Thank you for the response. I am running into an error when I implement your code: Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[ setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key poster.' How do I rectify this? I am assuming it has something to do with xib file. – user844288 Jul 12 '21 at 06:02
  • Yes, it seems like your XIB has an issue. See https://stackoverflow.com/questions/3088059/xcode-how-to-fix-nsunknownkeyexception-reason-this-class-is-not-key-valu for fixing your issue. – Tarun Tyagi Jul 12 '21 at 06:39
-1
import UIKit
import Kingfisher

class MovieCardView: UIView {
    @IBOutlet weak var poster: UIImageView!
    
    var movieCard: MovieCard? = nil
    
    override init(frame: CGRect) {
        super.init(frame: frame)
    }
   required init?(coder aDecoder: NSCoder) {
       super.init(coder: aDecoder)
   }

    func setup(_ movieCard: MovieCard) {
         self.movieCard = movieCard
         guard let posterString = movieCard.poster_path else {
            return
        }
        
        let urlString = "https://image.tmdb.org/t/p/w300" + posterString
        
        guard let posterImageURL = URL(string: urlString) else {
            return
        }
        
        poster.kf.setImage(with: posterImageURL)
       }
}

// from controller when you initialising customview call this function from there like below

func koloda(_ koloda: KolodaView, viewForCardAt index: Int) -> UIView {
        
        let movieCard = cards[index]
        guard let customView = Bundle.main.loadNibNamed("MovieCardView", owner: nil, options: nil) as? MovieCardView else { return UIView() }

customView.setUp(movieCard);

        return customView
    }
Inder
  • 1
  • 3
  • Thank you for the response. I am running into an error when I implement your code: Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[ setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key poster.' How do I rectify this? – user844288 Jul 12 '21 at 06:07