打开可选值时崩溃

时间:2017-10-09 15:35:28

标签: swift

我收到了错误消息。它在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()
        }
    }

}

感谢您的支持!

2 个答案:

答案 0 :(得分:0)

您的异步呼叫位于fetchPopularMoviesviewWillAppear呼叫。您在viewDidAppear中初始化了图片视图,在 viewWillAppear之后称为

基本上,headerImageView是零。

“但是我把电话放在了async区块中!所以当我设置图像视图的图像时,应该已经调用了viewDidAppear,对吧?”你说。

好吧,依靠async说“我希望在某个方法返回后运行这个东西”不是一个好主意。并不总能保证这样做。

要解决此问题,您需要在调用 fetchPopularMovies之前初始化标题图像视图。例如,您可以将其放在viewDidLoad中。

答案 1 :(得分:0)

您正在初始化viewDidAppear中的图片视图,该图片将在 viewWillAppear之后被称为,其中填充了图片视图。尽管有异步任务,但不能保证在填充之前初始化视图。

我建议懒洋洋地初始化图片视图,好处是该属性是非可选的,并且在第一次访问时会初始化一次。

 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
 }()