Converting from UIImage to a SwiftUI Image results in a blank image of the same size - uikit

I am trying to convert a UIImage to a SwiftUI Image using the init(uiImage:) initializer. My UIImage itself is created from a CIImage generated by a CIQRCodeGenerator CIFilter. I am running my code on a Playground in Xcode 11.1 GM seed 1. Here is the entirety of my code:
import SwiftUI
import UIKit
func qrCodeImage(for string: String) -> Image? {
let data = string.data(using: String.Encoding.utf8)
guard let qrFilter = CIFilter(name: "CIQRCodeGenerator") else { return nil }
qrFilter.setValue(data, forKey: "inputMessage")
guard let ciImage = qrFilter.outputImage else { return nil }
let uiImage = UIImage(ciImage: ciImage)
let image = Image(uiImage: uiImage)
return image
}
let image = qrCodeImage(for: "fdsa")
And here is the result:
Even when I transform the image with CGAffineTransform(scaleX: 10, y: 10), the resulting SwiftUI Image at the end is still the same size, but blank.

Following solution provided in: Generating QR Code with SwiftUI shows empty picture
Here is the code:
var ciContext = CIContext()
func qrCodeImage(for string: String) -> Image? {
let data = string.data(using: String.Encoding.utf8)
guard let qrFilter = CIFilter(name: "CIQRCodeGenerator") else { return nil }
qrFilter.setValue(data, forKey: "inputMessage")
guard let ciImage = qrFilter.outputImage else { return nil }
let cgImage = ciContext.createCGImage(ciImage, from: ciImage.extent)
let uiImage = UIImage(cgImage: cgImage!)
let image = Image(uiImage: uiImage)
return image
}
let image = qrCodeImage(for: "fdsa")
Result:
screenshot in swift playground

Can confirm I encounter the same issue with a SwiftUI Image using a UIImage initialized from data. Can verify that the image is loaded when paused in debugging, but it does not display in the SwiftUI Image.
This solution worked for me: explicitly specify the image rendering mode. In my case I added the following: .renderingMode(.original)

#Eugene remark worked for me:
let image = Image(uiImage: uiImage).renderingMode(.original)

Related

set image color of a template image

I have an image like this:
(Rendered as a template image)
I tried this code:
#IBOutlet weak var imgAdd: NSImageView!
imgAdd.layer?.backgroundColor = CGColor.white
Which only changes the background color of course.
Is there a way to change the color of this image programmatically?
So far I've tried the code below which doesn't work. (The image color doesn't change.)
func tintedImage(_ image: NSImage, tint: NSColor) -> NSImage {
guard let tinted = image.copy() as? NSImage else { return image }
tinted.lockFocus()
tint.set()
let imageRect = NSRect(origin: NSZeroPoint, size: image.size)
NSRectFillUsingOperation(imageRect, .sourceAtop)
tinted.unlockFocus()
return tinted
}
imgDok.image = tintedImage(NSImage(named: "myImage")!, tint: NSColor.red)
Swift 4
Updated answer for Swift 4
Please note, this NSImage extension is based on #Ghost108 and #Taehyung_Cho's answers, so a larger credit goes to them.
extension NSImage {
func tint(color: NSColor) -> NSImage {
let image = self.copy() as! NSImage
image.lockFocus()
color.set()
let imageRect = NSRect(origin: NSZeroPoint, size: image.size)
imageRect.fill(using: .sourceAtop)
image.unlockFocus()
return image
}
}
Swift 4 version
extension NSImage {
func image(withTintColor tintColor: NSColor) -> NSImage {
guard isTemplate else { return self }
guard let copiedImage = self.copy() as? NSImage else { return self }
copiedImage.lockFocus()
tintColor.set()
let imageBounds = NSMakeRect(0, 0, copiedImage.size.width, copiedImage.size.height)
imageBounds.fill(using: .sourceAtop)
copiedImage.unlockFocus()
copiedImage.isTemplate = false
return copiedImage
}
}
I found the solution with everyone's help:
(Swift 3)
func tintedImage(_ image: NSImage, tint: NSColor) -> NSImage {
guard let tinted = image.copy() as? NSImage else { return image }
tinted.lockFocus()
tint.set()
let imageRect = NSRect(origin: NSZeroPoint, size: image.size)
NSRectFillUsingOperation(imageRect, .sourceAtop)
tinted.unlockFocus()
return tinted
}
imgDok.image = tintedImage(NSImage(named: "myImage")!, tint: NSColor.red)
Important: in interface builder I had to set the "render as" setting of the image to "Default".
The other solutions don't work when the user wants to change between light and dark mode, this method solves that:
extension NSImage {
func tint(color: NSColor) -> NSImage {
return NSImage(size: size, flipped: false) { (rect) -> Bool in
color.set()
rect.fill()
self.draw(in: rect, from: NSRect(origin: .zero, size: self.size), operation: .destinationIn, fraction: 1.0)
return true
}
}
}
Be aware that if you use .withAlphaComponent(0.5) on an NSColor instance, that color loses support for switching between light/dark mode. I recommend using color assets to avoid that issue.
Had to modify #Ghost108's answer little bit for Xcode 9.2.
NSRectFillUsingOperation(imageRect, .sourceAtop)
to
imageRect.fill(using: .sourceAtop)
Thanks.
Since your image is inside an NSImageView, the following should work fine (available since macOS 10.14):
let image = NSImage(named: "myImage")!
image.isTemplate = true
let imageView = NSImageView(image: image)
imageView.contentTintColor = .green
The solution is to apply "contentTintColor" to your NSImageView instead of the NSImage.
See: Documentation
no need to copt:
extension NSImage {
func tint(with color: NSColor) -> NSImage {
self.lockFocus()
color.set()
let srcSpacePortionRect = NSRect(origin: CGPoint(), size: self.size)
srcSpacePortionRect.fill(using: .sourceAtop)
self.unlockFocus()
return self
}
}
Since you can't use the UIImage functions, you can try using CoreImage (CI). I don't know if there is an easier version but this one will work fore sure!
First you create the CIImage
let image = CIImage(data: inputImage.tiffRepresentation!)
Now you can apply all kinds of filters and other stuff to the image, it's a really powerful tool.
The documentation for CI: https://developer.apple.com/documentation/coreimage
The Filter List: https://developer.apple.com/library/content/documentation/GraphicsImaging/Reference/CoreImageFilterReference/index.html
Here is a simple filter example, you basically initialise a filter and then set the values for it, output it and repeat.
let yourFilterName = CIFilter(name: "FilterName")
yourFilterName!.setValue(SomeInputImage, forKey: kCIInputImageKey)
yourFilterName!.setValue(10, forKey: kCIInputRadiusKey)
let yourFilterName = yourFilterName!.outputImage
Now you can just convert the output back as NSImage.
let cgimg = context.createCGImage(yourFilterName!, from: yourFilterName!.extent)
let processedImage = NSImage(cgImage: cgimg!, size: NSSize(width: 0, height: 0))
Try this code it helps.
Swift 3
let theImageView = UIImageView(image: UIImage(named:"foo")!.withRenderingMode(.alwaysTemplate))
theImageView.tintColor = UIColor.red

UIImageView images is not loading on iOS10 and Xcode 8?

UIImageView images are not loading either with downloading with Alamofire or using URL to convert to a data object and convert data object to a UIImage. My images on UITableViewCells are working but not the UIImageviews standalone in a UIView. It is giving me a blank.
UIImageViews: They are a subview of a UIView which is a subview of a UITableView
Here it is the code that Im using:
let imageURL = URL(string: _imageUrl)
let imageData = try? Data(contentsOf: imageURL!)
imageView.image = UIImage(data: imageData!)
Here is the extension that I'm using with Alamofire:
extension UIImageView {
func donwloadImageFrom(_ imageURL: String, contentMode: UIViewContentMode) {
let stringUrl = URL(string: imageURL)!
Alamofire.request(stringUrl, method: .get).response(completionHandler: { response in
if let imageData = response.data {
DispatchQueue.main.async(execute: {
self.contentMode = contentMode
self.image = UIImage(data: imageData)
})
}
})
}
}
I think the awakeFromNib() function bit changed.
I don't know exact reason but, after adding
layoutIfNeeded()
into awakeFromNib().
Made it works.

Saving text labels on top of images

I'm building an app which takes Strings from the user and saves them using NSUserDefaults. Then I recover them and paste them on top of an image (a form or questionnaire) I'm currently saving the image with .write to file but when I check the image I've saved, I discovered that it doesn't save the full image and as a consequence the positions for the fields aren't right. I need to be able to send this image and later on print on a sheet of A4 paper.
The simulator :
http://postimg.org/image/5szujdgw3/
The saved image:
http://postimg.org/image/uq6mea6vb/
Why isn't it saving the full image?
Here's my code :
import UIKit
class StartMorgagesSave: UIViewController {
let rectangle = UIBezierPath(rect: CGRectMake(80, 166, 80, 05)) //x , y , width , height 240, 165
let shapeLayer = CAShapeLayer()
//let label = UILabel()
override func viewDidLoad() {
super.viewDidLoad()
shapeLayer.path = rectangle.CGPath
shapeLayer.strokeColor = UIColor.blueColor().CGColor
shapeLayer.fillColor = UIColor.whiteColor().CGColor
view.layer.addSublayer(shapeLayer)
view.pdfData.writeToURL(NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask).first!.URLByAppendingPathComponent("test53.pdf"), atomically: true) // what it is saved as
view.pdfData.writeToFile("fdfdlo", atomically: false)
print(NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask).first!.path!)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
extension UIView {
var pdfData: NSData {
let result = NSMutableData()
UIGraphicsBeginPDFContextToData(result, frame, nil)
guard let context = UIGraphicsGetCurrentContext() else { return result }
UIGraphicsBeginPDFPage()
layer.renderInContext(context)
UIGraphicsEndPDFContext()
return result
}
}
Niall

Unarchive NSData back to NSColor in swift

I have archived an NSColor to store it in NSUserDefaults:
var data = NSArchiver.archivedDataWithRootObject(NSColor.redColor())
storage.setObject(data, forKey: "color")
storage.synchronize()
But now I need to get the color back from NSData, I have no idea how to do that
You just need to use if let to unwrap your NSData and also you will need a conditional cast as follow:
edit/update:
Swift 3 or later
// archiving
let color: NSColor = .red
let data = NSKeyedArchiver.archivedData(withRootObject: color)
UserDefaults.standard.set(data, forKey: "color")
// unarchiving
if let loadedData = UserDefaults.standard.data(forKey: "color"),
let loadedColor = NSKeyedUnarchiver.unarchiveObject(with: loadedData) as? NSColor {
// you can access loadedColor here
print(loadedColor) // "sRGB IEC61966-2.1 colorspace 1 0 0 1\n"
}
Ran into some errors trying to get Leo's answer working in Swift 5. Came up with this extension which lets UserDefaults store and retrieve colors. Try pasting this into a Playground.
import Cocoa
extension UserDefaults {
func set(_ color: NSColor, forKey: String) {
if let data = try? NSKeyedArchiver.archivedData(withRootObject: color, requiringSecureCoding: false) {
self.set(data, forKey: forKey)
}
}
func color(forKey: String) -> NSColor? {
guard
let storedData = self.data(forKey: forKey),
let unarchivedData = try? NSKeyedUnarchiver.unarchivedObject(ofClass: NSColor.self, from: storedData),
let color = unarchivedData as NSColor?
else {
return nil
}
return color
}
}
// get defaults instance
let defaults = UserDefaults.standard
// create a color
let mycolor = NSColor(red: 0.0, green: 0.5, blue: 0.8, alpha: 0.5)
// save the color
defaults.set(mycolor, forKey: "mycolor")
// read the color back. this returns an optional, may be nil
defaults.color(forKey: "mycolor")

Swift - saving and retrieving images from userDefaults

I'm trying to save images retrieved from Parse.com like this:
let userImageFile = object["Image"] as PFFile
userImageFile.getDataInBackgroundWithBlock {
(imageData: NSData!, error: NSError!) -> Void in
if error == nil {
image = UIImage(data:imageData)
let imageToSave:NSData = UIImagePNGRepresentation(image)
self.saveImage(intRandomNumb, retImage: imageToSave)
}
}
where the saveImage-function looks like this:
func saveImage(imagepath:Int, retImage:NSData){
println("image is being saved")
let defaults = NSUserDefaults.standardUserDefaults()
let imagePathName = "\(imagepath)"
defaults.setObject(retImage, forKey: imagePathName)
}
and later, I'm trying to display this image like this:
var point = gestureRecognizer.locationInView(self.tv)
if let indexPath = self.tv.indexPathForRowAtPoint(point)
{
let data = mainList[indexPath.row] as SecondModel
let fileRef = data.fileReference
let intFileRef = Int(fileRef)
println(intFileRef)
let defaults = NSUserDefaults.standardUserDefaults()
let usedKeyName = "\(intFileRef)"
if let photo = defaults.objectForKey(usedKeyName) as? UIImage {
println("Image created")
let photo = defaults.objectForKey(usedKeyName) as UIImage
var imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: self.view.frame.width, height: self.view.frame.height))
imageView.image = photo
self.view.addSubview(imageView)
}
and the "Image created" never gets printed which means the retrieving somehow doesn't work.
I'm not quite sure if you're able to save images to the userdefaults like I've done here, but that was the best I could come up with, and I couldn't find any previous questions like this for Swift.
Any suggestions on how to proceed would be appreciated.
SOLUTION: The problem was that I tried to load the image directly as a UIImage. I also had to convert the NSData to a UIImage, this all happens in the last section of the code displayed above. Finally my code looks like this:
if let photo = defaults.objectForKey("\(intFileRef)") as? NSData {
println("Image created")
let photo = defaults.objectForKey("\(intFileRef)") as NSData
let imageToView:UIImage = UIImage(data: photo)
var imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: self.view.frame.width, height: self.view.frame.height))
imageView.image = imageToView
self.view.addSubview(imageView)
}
I hope this can help others struggling with something similar to this.
Swift 3
Hey, try this beautiful code here:
Convert your UIImage to Data.
PNG:
yourDataImagePNG = UIImagePNGRepresentation(yourUIImageHere)
JPEG :
yourDataImageJPG = UIImage(data: yourUIImageHere,scale:1.0)
Save in UserDefaults.
UserDefaults().set(yourDataImagePNG, forKey: "image")
Recover from:
UserDefaults.standard.object(forKey: "image") as! Data
I hope to help!
It seems like you do not call defaults.synchronize() so it's not written to the defaults file.

Resources