2017-10-09 5 views
0

エラーが発生します。 DispatchQueue呼び出しでnilを返しています。アンラッピング中にクラッシュするオプション値

DispatchQueue.main.async { 
    self.headerImageView.image = image // <- CRASH 
} 

これは私がクラッシュするところです。

headerImageViewは、ストーリーボードにIBOutletで接続されていない変数です。ここにコード全体があります:

import UIKit 
import WebKit 

let offset_HeaderStop:CGFloat = 40.0 
let offset_B_LabelHeader:CGFloat = 95.0 
let distance_W_LabelHeader:CGFloat = 35.0 

class MovieDetailsViewController: UIViewController, UIScrollViewDelegate { 

    var movie: Movie? 
    var urlBuilder = MovieUrlBuilder() 
    var config = TMDBConfiguration() 

    @IBOutlet weak var scrollView: UIScrollView! 
    @IBOutlet weak var headerView: UIView! 
    @IBOutlet weak var headerLabel: UILabel! 
    @IBOutlet weak var titleLabel: UILabel! 
    @IBOutlet weak var genreLabel: UILabel! 
    @IBOutlet weak var ratingLabel: UILabel! 
    @IBOutlet weak var releaseLabel: UILabel! 
    @IBOutlet weak var descriptionText: UITextView! 
    @IBOutlet weak var trailerContainerView: UIView! 
    @IBOutlet weak var trailerLabel: UILabel! 
    @IBOutlet weak var videoContainer: WKWebView! 

    var headerImageView: UIImageView! 

    override func viewDidLoad() { 
     super.viewDidLoad() 

     scrollView.delegate = self 
    } 

    override func viewDidAppear(_ animated: Bool) { 

     headerImageView = UIImageView(frame: headerView.bounds) 
     headerImageView.image = UIImage(named: "Poster") 
     headerImageView.contentMode = .scaleAspectFill 
     headerView.insertSubview(headerImageView, belowSubview: headerLabel) 
     headerView.clipsToBounds = true 
    } 

    override func viewWillAppear(_ animated: Bool) { 
     super.viewWillAppear(animated) 
     fetchPopularMovies() 
    } 

    func scrollViewDidScroll(_ scrollView: UIScrollView) { 

     let offset = scrollView.contentOffset.y 
     var headerTransform = CATransform3DIdentity 

     if offset < 0 { 
      let headerScaleFactor: CGFloat = -(offset)/headerView.bounds.height 
      let headerSizeVariation = ((headerView.bounds.height * (1.0 + headerScaleFactor)) - headerView.bounds.height)/2.0 
      headerTransform = CATransform3DTranslate(headerTransform, 0, headerSizeVariation, 0) 
      headerTransform = CATransform3DScale(headerTransform, 1.0 + headerScaleFactor, 1.0 + headerScaleFactor, 0) 

      headerView.layer.transform = headerTransform 
     } else { 
      headerTransform = CATransform3DTranslate(headerTransform, 0, max(-offset_HeaderStop, -offset), 0) 
      let labelTransform = CATransform3DMakeTranslation(0, max(-distance_W_LabelHeader, offset_B_LabelHeader - offset), 0) 
      headerLabel.layer.transform = labelTransform 
     } 

     headerView.layer.transform = headerTransform 
    } 

    override var preferredStatusBarStyle: UIStatusBarStyle{ 
     return UIStatusBarStyle.lightContent 
    } 

    private func fetchPopularMovies() { 
     if let poster = movie?.backdropPath { 
      let baseURL = URL(string: config.baseImageURL)! 
      let url = baseURL.appendingPathComponent("w500").appendingPathComponent(poster) 
      let request = URLRequest(url: url) 

      let task = urlBuilder.session.dataTask(with: request) {(data, response, error) in 
       do { 
        if let image = UIImage(data: data!) { 
         DispatchQueue.main.async { 
          self.headerImageView.image = image // <- CRASH 
         } 
        } 
       } 
      } 
      task.resume() 
     } 
    } 

} 

ありがとうございます!

答えて

0

あなたは、画像ビューが移入されviewWillAppear呼び出されますviewDidAppearの画像ビューを初期化しています。非同期タスクがあるにも関わらず、ビューを初期化してからデータが生成されることは保証されません。

イメージビューを遅延させて初期化することをお勧めします。プロパティーはオプションではなく、最初にアクセスしたときに一度初期化されるという利点があります。

lazy var headerImageView: UIImageView = { 
    let view = UIImageView(frame: self.headerView.bounds) 
    view.image = UIImage(named: "Poster") 
    view.contentMode = .scaleAspectFill 
    self.headerView.insertSubview(view, belowSubview: self.headerLabel) 
    view.clipsToBounds = true 
    return view 
}() 
0

fetchPopularMoviesには、viewWillAppearという非同期コールがあります。画像ビューをviewDidAppearに初期化します.viewWillAppearの後にと呼びます。

基本的に、headerImageViewはnilです。

"私はasyncブロックに電話をかけました!だから、viewDidAppearはイメージビューのイメージを設定するときに既に呼び出されているはずですか?"あなたが言った。

まあ、asyncに依存すると、「特定のメソッドが返された後にこのオブジェクトを実行したい」というのは良い考えではありません。それが必ずしも保証されるとは限りません。

この問題を解決するには、を初期化してから、fetchPopularMoviesを呼び出す必要があります。例えば、viewDidLoadに入れることができます。

関連する問題