My ViewController - I am just selecting an image and then updating imageView. Still, whenever I am leaving the view, the ImageView is back to "face-unknown", because newImage is nill.
Why doesn't the variable newImage remember the image after the view changes?
var newImage: UIImage!
#IBOutlet weak var imageView: UIImageView!
var imagePicker = UIImagePickerController()
override func viewDidLoad() {
super.viewDidLoad()
if((newImage == nil)){
imageView.image = UIImage(named:"face-unknown")!
}else{
imageView.image = newImage
}
}
func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject]) {
if let possibleImage = info["UIImagePickerControllerEditedImage"] as? UIImage {
imageView.image = resizeImage(possibleImage,newWidth: 150)
newImage = resizeImage(possibleImage,newWidth: 150)
} else if let possibleImage = info["UIImagePickerControllerOriginalImage"] as? UIImage {
imageView.image = resizeImage(possibleImage,newWidth: 150)
newImage = resizeImage(possibleImage,newWidth: 150)
} else {
return
}
Your imageView is back to "face-unknown" because the viewDidLoad() method is called only once. Not because your UIImage is nil. Instead try to update your UIImageView in the viewWillAppear() method, it should works
Related
Is there a way that I can use the Vision framework to scan an existing image from the user's photo library? As in, not taking a new picture using the camera, but just choosing an image that the user already has?
Yes, you can. Adding on to #Zulqarnayn's answer, here's a working example to detect and draw a bounding box on rectangles.
1. Set up the image view where the image will be displayed
#IBOutlet weak var imageView: UIImageView!
#IBAction func pickImage(_ sender: Any) {
let picker = UIImagePickerController()
picker.delegate = self
self.present(picker, animated: true)
}
override func viewDidLoad() {
super.viewDidLoad()
imageView.layer.borderWidth = 4
imageView.layer.borderColor = UIColor.blue.cgColor
imageView.contentMode = .scaleAspectFill
imageView.backgroundColor = UIColor.green.withAlphaComponent(0.3)
imageView.layer.masksToBounds = false /// allow image to overflow, for testing purposes
}
2. Get the image from the image picker
extension ViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate {
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
guard let image = info[.originalImage] as? UIImage else { return }
/// set the imageView's image
imageView.image = image
/// start the request & request handler
detectCard()
/// dismiss the picker
dismiss(animated: true)
}
}
3. Start the vision request
func detectCard() {
guard let cgImage = imageView.image?.cgImage else { return }
/// perform on background thread, so the main screen is not frozen
DispatchQueue.global(qos: .userInitiated).async {
let request = VNDetectRectanglesRequest { request, error in
/// this function will be called when the Vision request finishes
self.handleDetectedRectangle(request: request, error: error)
}
request.minimumAspectRatio = 0.0
request.maximumAspectRatio = 1.0
request.maximumObservations = 1 /// only look for 1 rectangle
let imageRequestHandler = VNImageRequestHandler(cgImage: cgImage, orientation: .up)
do {
try imageRequestHandler.perform([request])
} catch let error {
print("Error: \(error)")
}
}
}
4. Get the result from the Vision request
func handleDetectedRectangle(request: VNRequest?, error: Error?) {
if let results = request?.results {
if let observation = results.first as? VNRectangleObservation {
/// get back to the main thread
DispatchQueue.main.async {
guard let image = self.imageView.image else { return }
let convertedRect = self.getConvertedRect(
boundingBox: observation.boundingBox,
inImage: image.size,
containedIn: self.imageView.bounds.size
)
self.drawBoundingBox(rect: convertedRect)
}
}
}
}
5. Convert observation.boundingBox to the UIKit coordinates of the image view, then draw a border around the detected rectangle
I explain this more in detail in this answer.
func getConvertedRect(boundingBox: CGRect, inImage imageSize: CGSize, containedIn containerSize: CGSize) -> CGRect {
let rectOfImage: CGRect
let imageAspect = imageSize.width / imageSize.height
let containerAspect = containerSize.width / containerSize.height
if imageAspect > containerAspect { /// image extends left and right
let newImageWidth = containerSize.height * imageAspect /// the width of the overflowing image
let newX = -(newImageWidth - containerSize.width) / 2
rectOfImage = CGRect(x: newX, y: 0, width: newImageWidth, height: containerSize.height)
} else { /// image extends top and bottom
let newImageHeight = containerSize.width * (1 / imageAspect) /// the width of the overflowing image
let newY = -(newImageHeight - containerSize.height) / 2
rectOfImage = CGRect(x: 0, y: newY, width: containerSize.width, height: newImageHeight)
}
let newOriginBoundingBox = CGRect(
x: boundingBox.origin.x,
y: 1 - boundingBox.origin.y - boundingBox.height,
width: boundingBox.width,
height: boundingBox.height
)
var convertedRect = VNImageRectForNormalizedRect(newOriginBoundingBox, Int(rectOfImage.width), Int(rectOfImage.height))
/// add the margins
convertedRect.origin.x += rectOfImage.origin.x
convertedRect.origin.y += rectOfImage.origin.y
return convertedRect
}
/// draw an orange frame around the detected rectangle, on top of the image view
func drawBoundingBox(rect: CGRect) {
let uiView = UIView(frame: rect)
imageView.addSubview(uiView)
uiView.backgroundColor = UIColor.clear
uiView.layer.borderColor = UIColor.orange.cgColor
uiView.layer.borderWidth = 3
}
Result | Demo repo
Input image
Result
Yes, you can. First, take an instance of UIImagePickerController & present it.
let picker = UIImagePickerController()
picker.delegate = self
picker.sourceType = .photoLibrary
present(picker, animated: true, completion: nil)
Then implement the delegate method take the desired image
extension YourViewController: UIImagePickerControllerDelegate {
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
if let pickedImage = info[.originalImage] as? UIImage {
## here start your request & request handler
}
picker.dismiss(animated: true, completion: nil)
}
}
Hi I'm using IOS swift 3 to let user pick images from library or album.I have an UIImage variable.How can we use property Observer to update the UIImage when user finished pick an Image
Some thing like
var image: UIImage = {
didSet....
}
Currently I'm doing this
func show(image: UIImage) {
imageView.image = image
imageView.isHidden = false
imageView.frame = CGRect(x: 10, y: 10, width: 260, height: 260)
addPhotoLabel.isHidden = true
}
func imagePickerController(_ picker: UIImagePickerController,
didFinishPickingMediaWithInfo info: [String : Any]) {
image = info[UIImagePickerControllerEditedImage] as? UIImage
if let theImage = image {
show(image: theImage)
}
dismiss(animated: true, completion: nil)
}
Thinking of using property Observer to improve the approach.Any help is much appreciate.Thanks!
If you really want to update the image view any time the image property is set, then simply put all of the code in your show method in the didSet block for the image property.
var image: UIImage = {
didSet {
imageView.image = image
imageView.isHidden = false
imageView.frame = CGRect(x: 10, y: 10, width: 260, height: 260)
addPhotoLabel.isHidden = true
}
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
if let theImage = info[UIImagePickerControllerEditedImage] as? UIImage {
image = theImage
}
picker.dismiss(animated: true, completion: nil)
}
I want to add custom UIImage in Camera Screen while user capture the photo. Also i want that image in foreground of that captured image on the same position the image is added.
Here the demo image that explain what i want.
To work around this i have try
let picker = UIImagePickerController()
picker.sourceType = .Camera
picker.view.addSubView(imageView)
But using this code it will only add image not add image with the capture image
Any help is appreciate.
You can use UIImagePickerController's cameraOverlayView property to add the imageView on top of the camera screen:
let picker = UIImagePickerController()
picker.delegate = self
picker.sourceType = .Camera
picker.cameraOverlayView = imageView
presentViewController(picker, animated: true, completion: nil)
For adding the image to the foreground of the captured photo you can create an extension for UIImage:
extension UIImage {
func imageWithOverlayImage(overlayImage: UIImage) -> UIImage {
let imageRect = CGRectMake(0, 0, self.size.width, self.size.height)
UIGraphicsBeginImageContext(self.size)
self.drawInRect(imageRect)
overlayImage.drawInRect(imageRect)
let resultImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return resultImage
}
}
Then you can call it from the delegate method of the image picker:
func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject]) {
let image = info[UIImagePickerControllerOriginalImage] as! UIImage
finalImage = image.imageWithOverlayImage(overlayImage!)
dismissViewControllerAnimated(true, completion: nil)
}
try putting Overlayview. add custom image to overlayview.
func openCamera(){
let pickercontroller : UIImagePickerController = UIImagePickerController()
pickercontroller.delegate = self
if(UIImagePickerController .isSourceTypeAvailable(UIImagePickerControllerSourceType.Camera)){
pickercontroller.sourceType = UIImagePickerControllerSourceType.Camera
}
else{
}
pickercontroller.editing = false
pickercontroller.showsCameraControls = false
let graphRect:CGRect = CGRectMake(0, 0,pickercontroller.view.frame.size.width-20 ,pickercontroller.view.frame.size.height)
let overlayView : UIView = UIView(frame: graphRect)
overlayView.backgroundColor = UIColor(colorLiteralRed: 0.0/255.0, green: 0/255.0, blue: 0.0/255.0, alpha: 0.5)
overlayView.layer.opaque = false
overlayView.opaque = false
var maskLayer : CAShapeLayer = CAShapeLayer()
overlayView.layer.mask = maskLayer
}
I have a camera feature in my app and when you take the picture it places that picture into an imageView. I have a button that I've hidden and what I want is for when the image is placed in the imageView for the button to be unhidden.
#IBOutlet weak var imageView: UIImageView!
#IBOutlet weak var toGoFurther: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
toGoFurther.hidden = true
if (self.imageView.image != nil){
toGoFurther.hidden = false
}
let testObject = PFObject(className: "TestObject")
testObject["foo"] = "bar"
testObject.saveInBackgroundWithBlock { (success: Bool, error: NSError?) -> Void in
print("Object has been saved.")
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func continueNextPage(sender: AnyObject) {
}
#IBAction func takePhoto(sender: AnyObject) {
if !UIImagePickerController.isSourceTypeAvailable(UIImagePickerControllerSourceType.Camera){
return
}
let imagePicker = UIImagePickerController()
imagePicker.delegate = self
imagePicker.sourceType = UIImagePickerControllerSourceType.Camera;
//Create camera overlay
let pickerFrame = CGRectMake(0, UIApplication.sharedApplication().statusBarFrame.size.height, imagePicker.view.bounds.width, imagePicker.view.bounds.height - imagePicker.navigationBar.bounds.size.height - imagePicker.toolbar.bounds.size.height)
let squareFrame = CGRectMake(pickerFrame.width/2 - 400/2, pickerFrame.height/2 - 400/2, 640, 640)
UIGraphicsBeginImageContext(pickerFrame.size)
let context = UIGraphicsGetCurrentContext()
CGContextSaveGState(context)
CGContextAddRect(context, CGContextGetClipBoundingBox(context))
CGContextMoveToPoint(context, squareFrame.origin.x, squareFrame.origin.y)
CGContextAddLineToPoint(context, squareFrame.origin.x + squareFrame.width, squareFrame.origin.y)
CGContextAddLineToPoint(context, squareFrame.origin.x + squareFrame.width, squareFrame.origin.y + squareFrame.size.height)
CGContextAddLineToPoint(context, squareFrame.origin.x, squareFrame.origin.y + squareFrame.size.height)
CGContextAddLineToPoint(context, squareFrame.origin.x, squareFrame.origin.y)
CGContextEOClip(context)
CGContextMoveToPoint(context, pickerFrame.origin.x, pickerFrame.origin.y)
CGContextSetRGBFillColor(context, 0, 0, 0, 1)
CGContextFillRect(context, pickerFrame)
CGContextRestoreGState(context)
let overlayImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext();
let overlayView = UIImageView(frame: pickerFrame)
overlayView.image = overlayImage
imagePicker.cameraOverlayView = overlayView
self.presentViewController(imagePicker, animated: true, completion: nil)
}
func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject]) {
imageView.image = info[UIImagePickerControllerOriginalImage] as? UIImage
dismissViewControllerAnimated(true, completion: nil)
}
Try putting this in the viewDidAppear method. That should do it.
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
toGoFurther.hidden = self.imageView.image == nil
}
So, you want to unhide your button when image is captured and set on view. You need to do this in your didFinishPickingMediaWithInfo function like this:
func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [NSObject: AnyObject]) {
imageView.image = info[UIImagePickerControllerOriginalImage] as? UIImage
// dissmiss the image picker controller window
self.dismissViewControllerAnimated(true, completion:^{
toGoFurther.hidden = false
})
}
I have two images bg-land and bg-port for different orientation. I wanna change the image on chnaging orientation. But, the image does not change. When I load the Simulator in LandScape, bg-land works perfect but does not work with Rotation. Code:
override func viewDidLoad() {
super.viewDidLoad()
loadBackground()
}
func loadBackground(){
if(UIApplication.sharedApplication().statusBarOrientation.isPortrait){
var bgImage = UIImage(named: "bg-port")
var background = UIImageView(frame: self.view.bounds);
background.image = bgImage
self.view.addSubview(background)
self.view.sendSubviewToBack(background)
println(bgImage)
} else {
var bgImage = UIImage(named: "bg-land")
var background = UIImageView(frame: self.view.bounds);
background.image = bgImage
self.view.addSubview(background)
self.view.sendSubviewToBack(background)
println(bgImage)
}
}
override func didRotateFromInterfaceOrientation(fromInterfaceOrientation: UIInterfaceOrientation) {
loadBackground()
}
This code works fine for me try this:
override func viewDidLoad() {
super.viewDidLoad()
self.loadBackground()
}
func loadBackground(){
if(UIApplication.sharedApplication().statusBarOrientation.isPortrait){
let imageName = "Texture.jpg"
let image = UIImage(named: imageName)
let imageView = UIImageView(image: image!)
imageView.frame = self.view.bounds
view.addSubview(imageView)
} else {
let imageName = "AppIcon.png"
let image = UIImage(named: imageName)
let imageView = UIImageView(image: image!)
imageView.frame = self.view.bounds
view.addSubview(imageView)
}
}
override func didRotateFromInterfaceOrientation(fromInterfaceOrientation: UIInterfaceOrientation) {
loadBackground()
}
Don't forget to change image name from this code.
May be this will help you.
It's not changing because you are sending the newly added UIImageView to the back of your previously created UIImageView. Instead of doing that, create a member variable in your class for UIImageView. (Name it as background)
func loadBackground()
{
if self.background == nil
{
self.background = UIImageView()
self.view.addSubview(self.background)
self.view.sendSubviewToBack(self.background)
}
if(UIApplication.sharedApplication().statusBarOrientation.isPortrait)
{
var bgImage = UIImage(named: "bg-port")
self.background.frame = self.view.bounds;
self.background.image = bgImage
}
else
{
var bgImage = UIImage(named: "bg-land")
self.background.frame = self.view.bounds;
background.image = bgImage
}
}
But it is better to add a UIImageView outlet to your interface and loading the image using code, rather than creating one at run-time. And I prefer that would be a nice approach.