【问题标题】:image is not being displayed in UIImageView even though it is being set即使正在设置图像,也不会在 UIImageView 中显示
【发布时间】:2019-02-13 16:16:40
【问题描述】:

我有一个带有 UIImageView 的 collectionViewHeader,设置为灰色背景颜色。有一个 addPhotoButton,它将用户带到另一个控制器。当他们选择图像时,它会返回到原始控制器,并且应该是 UIImageView 中的图像。

这是来自 collectionViewHeader 的代码:

var selectedImage: UIImage? {
    didSet {
        self.imageView.image = selectedImage!
        print("This is the selectedImage:", selectedImage!)
    }
}


let imageView: UIImageView = {
    let iv = UIImageView()
    iv.backgroundColor = UIColor.rgb(red: 234, green: 246, blue: 246)
    iv.contentMode = .scaleAspectFill
    iv.clipsToBounds = true
    return iv
}()

这是我遇到的每个教程或 StackOverflow 帖子都说要做的。以及打印语句:

print("This is the selectedImage:", selectedImage!)

正在打印:

This is the selectedImage: <UIImage: 0x600001000380>, {485, 482}

所以图像在那里,但它没有被放入 UIImageView。

正在将 imageView 添加到子视图并锚定以在标题的 init 中设置大小:

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

    addSubview(imageView)
    addSubview(addimageButton)
    addSubview(addimageText)

    if #available(iOS 11.0, *){
        eventImageView.anchor(top: safeAreaLayoutGuide.topAnchor, left: leftAnchor, bottom: nil, right: rightAnchor, paddingTop: 0, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: frame.width, height: 175)
    } else {
        // Fallback on earlier OS?
    }
    addimageButton.anchor(top: eventImageView.topAnchor, left: eventImageView.leftAnchor, bottom: nil, right: imageView.rightAnchor, paddingTop: 25, paddingLeft: (imageView.bounds.size.width - addimageButton.bounds.size.width) / 2, paddingBottom: 0, paddingRight: 0, width: 0, height: 0)
    addimageText.anchor(top: nil, left: imageView.leftAnchor, bottom: imageView.bottomAnchor, right: imageView.rightAnchor, paddingTop: 0, paddingLeft: (imageView.bounds.size.width - addimageButton.bounds.size.width) / 2, paddingBottom: 25, paddingRight: 0, width: 0, height: 0)
}

selectedImage 在此处设置在另一个控制器的标头中:

override func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
    let header = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: headerId, for: indexPath) as! PhotoSelectorHeader

    self.header = header

    header.photoImageView.image = selectedImage

    if let selectedImage = selectedImage {
        if let index = self.images.index(of: selectedImage) {
            let selectedAsset = self.assets[index]

            let imageManager = PHImageManager.default()
            let targetSize = CGSize(width: 600, height: 600)
            imageManager.requestImage(for: selectedAsset, targetSize: targetSize, contentMode: .default, options: nil, resultHandler: { (image, info) in

                header.photoImageView.image = image

            })

        }
    }

    return header
}

fileprivate func setupNavigationButtons() {
    navigationController?.navigationBar.tintColor = .black
    navigationItem.leftBarButtonItem = UIBarButtonItem(title: "Cancel", style: .plain, target: self, action: #selector(handleCancel))

    navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Select", style: .plain, target: self, action: #selector(handleNext))
}

@objc func handleNext() {
    let addPhotoHeader = AddPhotoHeader()
    addPhotoHeader.selectedImage = header?.photoImageView.image
    navigationController?.popViewController(animated: true)
}

我确定我在这里缺少一个快速步骤,但我已经卡了几天了!

如果您认为问题可能出在我项目的其他地方,请告诉我。

提前感谢您的帮助!!

【问题讨论】:

  • 你在哪里设置图像?
  • 您需要使用协议将图像视图发送到根视图控制器
  • @Bot 我刚刚编辑了帖子以显示我设置图像的位置。如果您还需要什么,请告诉我!
  • photoImageView的frame或者size在哪里设置?
  • 你在哪里添加它作为子视图?

标签: ios swift uicollectionview uiimageview uiimage


【解决方案1】:

两个问题:

  1. 你没有在主线程上调用它(如果它是从主线程设置的,可能工作,但我假设它不是,因为 UIImageView 不是' t 适当更新。

  2. (可选修复)您要求通过强制转换可选的 selectedImage 来导致崩溃。

var selectedImage: UIImage? {
    didSet {
        DispatchQueue.main.async { [weak self] in
            // Don't force-cast (!) the optional selectedImage.
            // Since you can assign a nil image to self?.imageView.image, do that instead
            // Other option is adding a guard-statement here:
            // guard selectedImage != nil else { return }
            self?.imageView.image = selectedImage
            print("This is the selectedImage:", selectedImage)
        }
    }
}

只要设置了与 UI 相关的内容但没有进行视觉更新,并且其他一切看起来都很好,那么跳转到主线程很有可能会起作用。主线程保留用于所有用户界面更改,例如更新图像、刷新表视图或弄乱约束。

几乎所有包含UI (UIImageView) 的对象都只需要在主线程上更新。可能出现的问题范围从 UI 对象更新的严重延迟到崩溃。

但是,任何长时间运行的任务或繁重的进程,例如网络请求或处理像素级别的图片,都应该在主线程上运行,否则您的用户界面在该过程完成时停止。

@twostraws (Twitter) 很好地解释了这里的主线程:https://www.hackingwithswift.com/read/9/4/back-to-the-main-thread-dispatchqueuemain

他提到了这一点,我完全同意:

这非常重要,需要重复两次:在后台线程上执行用户界面工作是绝对不行的。

希望这会有所帮助!

【讨论】:

  • 你不需要[weak self]
  • 我选择尽可能避免对 self 进行强引用,是否包含它取决于开发人员
  • 好的,这很有意义,因为我使用DispatchQueue.global(qos: .background).async 获取照片。但是,现在图像打印为nil。知道为什么会这样吗?
  • 它是否偶然打印了两次nil?推送到另一个线程可能需要很短的时间,实际上是毫秒/纳秒,但如果你不小心将它第二次设置为 nil,那可能就足够长了。
  • 实际上,我注意到您确实设置了两次。在您使用nil 设置它然后使用图像设置它时,总是存在种族冲突的可能性,但它以相反的顺序设置它们。问题是它发生得太快了,我们的眼睛注意到了。除了这个问题,我不相信将某些东西移到主线程可以做到这一点。您是否进行了其他更改? (此外,您在 imageManager 中对标头进行了强引用。由于它是一个闭包,它可能会在您返回标头后运行。这可能是您的问题的一部分——请查看前两个 cmets。)跨度>
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-03-27
  • 1970-01-01
  • 1970-01-01
  • 2023-03-22
  • 2022-01-14
  • 2016-10-29
  • 1970-01-01
相关资源
最近更新 更多