iOS照片库中的透明PNG文件存在奇怪问题

iOS照片库中的透明PNG文件存在奇怪问题,ios,swift,uiimage,uiimagepickercontroller,Ios,Swift,Uiimage,Uiimagepickercontroller,我有一个非常奇怪的问题,来自照片应用程序的透明PNG文件 问题是,我正在编写一个应用程序,允许用户调出一个实例,在该实例中,用户选择一个图像,然后通过其属性将该图像添加到 很简单,是吗?问题是库中的图像是透明PNG 无论出于什么原因,每当我试图渲染图像时,它的背景总是白色的 据我所知,该图像以透明PNG格式存储在库中。当我把它拖出来,用图像编辑器检查它时,一切正常。正是我所期望的 但当我以编程方式提取它时,它有一个白色背景。我似乎无法让它透明化 下面是我用来提取图像的代码,它是一个选择器回调:

我有一个非常奇怪的问题,来自照片应用程序的透明PNG文件

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

很简单,是吗?问题是库中的图像是透明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()
        }
    }
}
它实际上不是一个按钮。这是一个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回购。

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

这里有一个简单的例子来检查。运行时看起来是这样的:

代码创建红色和蓝色图像视图,其中.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会显示透明部分的复选框。如果我将代码分解到一个单独的游乐场,它会工作,但我无法在游乐场中复制图像选择器。不。这对我不起作用,请看问题中的更新。我应该提到的是,我的照片应用程序已经打开了iCloud存储,并且有完整的HDR。我记得大约三年前的一份bug报告说,iCloud照片会影响透明度。我会试着把它关掉,看看这会不会有什么不同。这是iCloud照片。当我关闭它时,透明度开始工作。@ChrisMarshall-小搜索。。。是的,如果保存照片的编辑版本,透明度将丢失。此外,如果为iCloud照片选择优化iPhone存储,则检索到的照片将不是原始照片,除非您使用代码专门下载原始版本。