【问题标题】:Strange Issue With Transparent PNG Files From iOS Photo LibraryiOS照片库中透明PNG文件的奇怪问题
【发布时间】:2020-10-14 13:36:26
【问题描述】:

我在使用来自照片应用的透明 PNG 文件时遇到了一个非常奇怪的问题。

问题是我正在编写一个应用程序,允许用户调出UIImagePickerController 的实例,他们在其中选择一个图像,然后该图像通过其image 属性添加到UIImageView

很简单,嗯?问题是库中的图像是透明 PNG。

无论出于何种原因,每当我尝试渲染图像时,它的背景总是白色。

据我所知,图像以透明 PNG 的形式存储在库中。当我把它拖出来并用图像编辑器检查它时,它很好。正是我所期望的。

但是当我以编程方式提取它时,它有一个白色背景。我似乎无法让它变得透明。

这是我用来提取图像的代码(这是一个选择器回调):

func imagePickerController(_ inPicker: UIImagePickerController, didFinishPickingMediaWithInfo inInfo: [UIImagePickerController.InfoKey: Any]) {
    let info = Dictionary(uniqueKeysWithValues: inInfo.map { key, value in (key.rawValue, value) })

    guard let image = (info[UIImagePickerController.InfoKey.editedImage.rawValue] as? UIImage ?? info[UIImagePickerController.InfoKey.originalImage.rawValue] as? UIImage)?.resizeThisImage(toNewWidth: Self.maximumImageWidthAndHeightInPixels) else { return }
    
    organization?.icon = image
    
    inPicker.dismiss(animated: true) { DispatchQueue.main.async { [weak self] in
        self?.imageButton?.image = image
        self?.imageButton?.alpha = 1.0
        self?.imageButton?.tintColor = self?.view.tintColor 
        self?.updateUI()
        }
    }
}

它实际上不是UIButton。这是一个UIImageView,带有一个附加的点击识别器。

resizeThisImage() 方法在我为UIImage 编写的扩展中。它工作正常。我一直在使用它:

func resizeThisImage(toNewWidth inNewWidth: CGFloat? = nil, toNewHeight inNewHeight: CGFloat? = nil) -> UIImage? {
    guard nil == inNewWidth,
          nil == inNewHeight else {
        var scaleX: CGFloat = (inNewWidth ?? size.width) / size.width
        var scaleY: CGFloat = (inNewHeight ?? size.height) / size.height

        scaleX = nil == inNewWidth ? scaleY : scaleX
        scaleY = nil == inNewHeight ? scaleX : scaleY

        let destinationSize = CGSize(width: size.width * scaleX, height: size.height * scaleY)
        let destinationRect = CGRect(origin: .zero, size: destinationSize)

        UIGraphicsBeginImageContextWithOptions(destinationSize, false, 0)
        defer { UIGraphicsEndImageContext() }   // This makes sure that we get rid of the offscreen context.
        draw(in: destinationRect, blendMode: .normal, alpha: 1)
        return UIGraphicsGetImageFromCurrentImageContext()
    }
    
    return nil
}

无论如何,无论我是否使用resizeThisImage() 方法,都会发生这种情况。这不是问题。

有没有人知道是什么导致了这个问题?

更新:我实现了@DonMag 的示例,这就是我得到的:

注意生成的“A”被白色包围了。

我应该注意我使用的是经典的故事板 UIKit 应用程序(没有场景内容)。我不认为这应该是一个问题,但我很高兴提供我的小示例应用程序。我认为不值得为其创建 GH 存储库。

【问题讨论】:

    标签: ios swift uiimage uiimagepickercontroller


    【解决方案1】:

    您的代码似乎没有任何问题,所以我想知道您的图像是否真的,真的具有透明度?

    这是一个简单的检查示例。运行时是这样的:

    代码使用.contentMode = .center 创建红色和蓝色图像视图。

    点击“创建”按钮将使用 SF 符号生成UIImage——绿色,透明背景,红色图像视图的大小——并将其保存为具有透明度的 PNG 格式的照片。

    点击“加载”按钮将调出图像选择器。选择一张图片(例如刚刚创建和保存的图片)将加载该图片并 - 使用您的扩展程序 - 将其大小调整为 80 x 80 并将其分配给蓝色图像视图的 .image 属性。

    如您所见,从 Photo Picker 加载的图像仍然具有透明度。

    你的 UIImage 扩展用于调整大小

    extension UIImage {
        func resizeThisImage(toNewWidth inNewWidth: CGFloat? = nil, toNewHeight inNewHeight: CGFloat? = nil) -> UIImage? {
            guard nil == inNewWidth,
                  nil == inNewHeight else {
                var scaleX: CGFloat = (inNewWidth ?? size.width) / size.width
                var scaleY: CGFloat = (inNewHeight ?? size.height) / size.height
                
                scaleX = nil == inNewWidth ? scaleY : scaleX
                scaleY = nil == inNewHeight ? scaleX : scaleY
                
                let destinationSize = CGSize(width: size.width * scaleX, height: size.height * scaleY)
                let destinationRect = CGRect(origin: .zero, size: destinationSize)
                
                UIGraphicsBeginImageContextWithOptions(destinationSize, false, 0)
                defer { UIGraphicsEndImageContext() }   // This makes sure that we get rid of the offscreen context.
                draw(in: destinationRect, blendMode: .normal, alpha: 1)
                return UIGraphicsGetImageFromCurrentImageContext()
            }
            
            return nil
        }
    }
    

    UIImage 扩展以保存到照片 以透明的 PNG 格式

    extension UIImage {
        // save to Photos in PNG format with transparency
        func saveToPhotos(completion: @escaping (_ success:Bool) -> ()) {
            if let pngData = self.pngData() {
                PHPhotoLibrary.shared().performChanges({ () -> Void in
                    let creationRequest = PHAssetCreationRequest.forAsset()
                    let options = PHAssetResourceCreationOptions()
                    creationRequest.addResource(with: PHAssetResourceType.photo, data: pngData, options: options)
                }, completionHandler: { (success, error) -> Void in
                    if success == false {
                        if let errorString = error?.localizedDescription  {
                            print("Photo could not be saved: \(errorString))")
                        }
                        completion(false)
                    } else {
                        print("Photo saved!")
                        completion(true)
                    }
                })
            } else {
                completion(false)
            }
        }
    }
    

    示例视图控制器(基本上)使用您的func imagePickerController 来加载照片

    class TestImageViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
        
        var imgViewA: UIImageView = UIImageView()
        var imgViewB: UIImageView = UIImageView()
    
        override func viewDidLoad() {
            super.viewDidLoad()
            
            let vStack = UIStackView()
            vStack.axis = .vertical
            vStack.spacing = 20
            
            let btnStack = UIStackView()
            btnStack.axis = .horizontal
            btnStack.distribution = .fillEqually
            btnStack.spacing = 20
            
            let btnCreate = UIButton()
            let btnLoad = UIButton()
    
            btnCreate.setTitle("Create", for: [])
            btnLoad.setTitle("Load", for: [])
            
            [btnCreate, btnLoad].forEach { b in
                b.setTitleColor(.white, for: .normal)
                b.setTitleColor(.lightGray, for: .highlighted)
                b.backgroundColor = UIColor(red: 0.0, green: 0.5, blue: 0.75, alpha: 1.0)
                btnStack.addArrangedSubview(b)
            }
            
            vStack.translatesAutoresizingMaskIntoConstraints = false
            
            view.addSubview(vStack)
            
            [btnStack, imgViewA, imgViewB].forEach { v in
                vStack.addArrangedSubview(v)
            }
            
            [imgViewA, imgViewB].forEach { v in
                v.contentMode = .center
            }
            
            let g = view.safeAreaLayoutGuide
            NSLayoutConstraint.activate([
                vStack.centerXAnchor.constraint(equalTo: g.centerXAnchor),
                vStack.centerYAnchor.constraint(equalTo: g.centerYAnchor),
                vStack.widthAnchor.constraint(equalToConstant: 200.0),
                
                imgViewA.heightAnchor.constraint(equalTo: imgViewA.widthAnchor),
                imgViewB.heightAnchor.constraint(equalTo: imgViewB.widthAnchor),
            ])
            
            imgViewA.backgroundColor = .red
            imgViewB.backgroundColor = .blue
            
            btnCreate.addTarget(self, action: #selector(self.createAndSave(_:)), for: .touchUpInside)
            btnLoad.addTarget(self, action: #selector(importPicture(_:)), for: .touchUpInside)
            
        }
        
        @objc func createAndSave(_ sender: Any) {
            
            let w = imgViewA.frame.width
            
            // create a Green image with transparent background
            if let img = drawSystemImage("a.circle.fill", at: 80, centeredIn: CGSize(width: w, height: w)) {
                imgViewA.image = img
                
                // save it to Photos in PNG format with transparency
                img.saveToPhotos { (success) in
                    if success {
                        // image saved to photos
                        print("saved")
                    }
                    else {
                        // image not saved
                        fatalError("save failed")
                    }
                }
            }
            
        }
        
        // create UIImage from SF Symbol system image
        //  at Point Size
        //  centered in CGSize
        // will draw symbol in Green on transparent background
        private func drawSystemImage(_ sysName: String, at pointSize: CGFloat, centeredIn size: CGSize) -> UIImage? {
            let cfg = UIImage.SymbolConfiguration(pointSize: pointSize)
            guard let img = UIImage(systemName: sysName, withConfiguration: cfg)?.withTintColor(.green, renderingMode: .alwaysOriginal) else { return nil }
            let x = (size.width - img.size.width) * 0.5
            let y = (size.height - img.size.height) * 0.5
            let renderer = UIGraphicsImageRenderer(size: size)
            return renderer.image { context in
                img.draw(in: CGRect(origin: CGPoint(x: x, y: y), size: img.size))
            }
        }
        
        @objc func importPicture(_ sender: Any) {
            let picker = UIImagePickerController()
            picker.allowsEditing = true
            picker.delegate = self
            present(picker, animated: true)
        }
        
        func imagePickerController(_ inPicker: UIImagePickerController, didFinishPickingMediaWithInfo inInfo: [UIImagePickerController.InfoKey: Any]) {
            let info = Dictionary(uniqueKeysWithValues: inInfo.map { key, value in (key.rawValue, value) })
            
            guard let image = (info[UIImagePickerController.InfoKey.editedImage.rawValue] as? UIImage ?? info[UIImagePickerController.InfoKey.originalImage.rawValue] as? UIImage)?.resizeThisImage(toNewWidth: 80) else { return }
            
            // organization?.icon = image
            
            inPicker.dismiss(animated: true) {
                DispatchQueue.main.async { [weak self] in
                    self?.imgViewB.image = image
                    //self?.imageButton?.image = image
                    //self?.imageButton?.alpha = 1.0
                    //self?.imageButton?.tintColor = self?.view.tintColor
                    //self?.updateUI()
                }
            }
        }
    }
    

    【讨论】:

    • 感谢您详尽的回复!。我会审查一切。我相当确定图像是透明的。我测试的方法是把它从照片中拖出来,然后用 Photoshop 打开它(它以“.png”的形式出现,Photoshop 显示透明部分的复选框)。如果我将代码分解到一个单独的 Playground 中,它可以工作,但我无法在 Playground 中重现图像选择器。
    • 不。它对我不起作用(请参阅问题中的更新)。
    • 我应该提到的是,我的照片应用程序启用了 iCloud 存储以及完整的 HDR。我记得大约三年前的一个错误报告说 iCloud 照片会干扰透明度。我会尝试将其关闭,看看是否会有所不同。
    • 是的。这是 iCloud 照片。当我关闭它时,透明度开始起作用。
    • @ChrisMarshall - 很少搜索...是的,如果您保存照片的编辑版本,透明度会丢失。另外,如果iCloud Photos选择了“优化iPhone存储”,则检索到的照片将不是原始照片(除非您使用代码专门下载原始版本)。
    猜你喜欢
    • 2018-05-04
    • 2021-07-15
    • 2021-05-25
    • 2013-08-14
    • 1970-01-01
    • 2010-09-12
    • 2013-08-26
    • 1970-01-01
    • 2010-11-18
    相关资源
    最近更新 更多