I have a UIViewController that allows the user to select a photo or a video via UIImagePickerController. Once they select one of them the image is being assigned to a property called var selectedImage (if they select a video, there's a method that generates a thumbnail for that video). It works as expected.
The problem is that I want to handle the state of 2 UIBarButtonItems. I want to disable them when selectedImage is nil and set the isEnabled once there's an image.
Pretty straight forward, right? Well, the problem is that initially it loads the UIViewController, the buttons are disabled and visible, then I select a photo from the gallery, didFinishPickingMediaWithInfo is called and in there, I also call handlePost() method that should take care of the state (enabled/disabled) of the buttons. Well, for some reason, even though there's an image in selectedImage, the buttons are not being displayed (practically invisible, but as far as I know, there's no isHidden for UIBarButtonItem.
They are still active, if i tap on them (despite not seeing them) they do their job. Now, the problem is why are not they visible? I can guarantee Colors.tint has a value (I use this class throughout the app, it works) and the selectedImage has a value (I've put a break point and it calls the handlePost() method, goes through the first if statement where that property has a value. Yet, I don't see the buttons being displayed. The question is why?
#IBOutlet weak var clearButton: UIBarButtonItem!
#IBOutlet weak var filterButton: UIBarButtonItem!
var selectedImage: UIImage?
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
handlePost()
}
func handlePost() {
if selectedImage != nil {
clearButton.isEnabled = true
clearButton.tintColor = Colors.tint
filterButton.isEnabled = true
filterButton.tintColor = Colors.tint
shareButton.isEnabled = true
shareButton.backgroundColor = Colors.tint
shareButton.setTitleColor(.white, for: .normal)
}
else {
clearButton.isEnabled = false
clearButton.tintColor = .darkGray
filterButton.isEnabled = false
filterButton.tintColor = .darkGray
shareButton.isEnabled = false
shareButton.backgroundColor = .darkGray
shareButton.setTitleColor(.lightGray, for: .normal)
}
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
if let videoUrl = info[UIImagePickerController.InfoKey.mediaURL] as? URL {
if let thumbnail = self.generateThumbnailForImage(videoUrl) {
self.videoUrl = videoUrl
self.photoImageView.image = thumbnail
self.selectedImage = thumbnail
handlePost()
}
}
if let image = info[UIImagePickerController.InfoKey.originalImage] as? UIImage {
selectedImage = image
photoImageView.image = image
handlePost()
}
dismiss(animated: true, completion: nil)
}
As I mentioned in the comments, I'm not sure what's the issue because there's no problem for this to happen. But the simply solution was to remove the UIBarButtonItems and just replace them with UIButtons instead. This did the trick and now they're behaving the way they should.
Related
This question already has answers here:
Picking two different images in the same view controller using imagePickerController in Swift
(1 answer)
Select Multiple Images (UIImagePickerController or Photos.app Share UI)
(8 answers)
Closed 3 years ago.
I want to pick images from phone library when I tap on two different UIImageViews and after selection, display them on two different UIImageView's,
but I when run the following code, the same image displays at two different UIImageViews, How can I fix it?
'''
extension SettingProfileViewController:UIImagePickerControllerDelegate, UINavigationControllerDelegate {
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
if let image = info[UIImagePickerController.InfoKey.originalImage] as? UIImage {
profilePhoto.image = image
print("profile")
}
if let wallImage = info[UIImagePickerController.InfoKey.originalImage] as? UIImage {
wallpaperPhoto.image = wallImage
print("wallpaper")
}
dismiss(animated: true, completion: nil)
}
}
override func viewDidLoad() {
super.viewDidLoad()
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(SettingProfileViewController.handleSelectProfilePhotoView))
profilePhoto.addGestureRecognizer(tapGesture)
profilePhoto.isUserInteractionEnabled = true
let wallTapGesture = UITapGestureRecognizer(target: self, action: #selector(SettingProfileViewController.handleSelectWallpaperImageView))
wallpaperPhoto.addGestureRecognizer(wallTapGesture)
wallpaperPhoto.isUserInteractionEnabled = true
}
#objc func handleSelectProfilePhotoView(){
let pickerController = UIImagePickerController() //открывает галерею
pickerController.delegate = self
present(pickerController, animated: true, completion: nil)
}
#objc func handleSelectWallpaperImageView(){
let pickerCont = UIImagePickerController()
pickerCont.delegate = self
present(pickerCont, animated: true, completion: nil)
}
'''
What you observe is that when the user taps on any of the image views (wallpaperPhoto or profilePhoto), the UIImagePickerController always uses self as its delegate. Then, when the user picks an image, the delegate cannot distinguish any more which image view originally was tapped.
You could simply add a weak optional variable indicating the "active" tapped image view, and then set only it's image. Also, you can reduce the tap handler to a single function with a sender argument, which is the image view the user tapped on, and set the activeImageViewto this.
extension SettingProfileViewController:UIImagePickerControllerDelegate, UINavigationControllerDelegate {
weak var activeImageView:UIImageView? = nil
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
if let image = info[UIImagePickerController.InfoKey.originalImage] as? UIImage {
activeImageView.image = image
}
dismiss(animated: true, completion: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(SettingProfileViewController.handleSelect(_:))
profilePhoto.addGestureRecognizer(tapGesture)
profilePhoto.isUserInteractionEnabled = true
let wallTapGesture = UITapGestureRecognizer(target: self, action: #selector(SettingProfileViewController.handleSelect(_:)))
wallpaperPhoto.addGestureRecognizer(wallTapGesture)
wallpaperPhoto.isUserInteractionEnabled = true
}
#objc func handleSelect(sender:UIGestureRecognizer) {
guard let sendingImageView = sender.view as? UIImageView else {
print("Ooops, received this gesture not from an ImageView")
return
}
activeImageView = sendingImageView
let pickerController = UIImagePickerController() //открывает галерею
pickerController.delegate = self
present(pickerController, animated: true, completion: nil)
}
// ...
Since iOS 9 and Xcode 7 I am no longer able to implemet a UIImagePickerController on an iPad (both device and simulator). The code below works on the iPad but only prior to iOS 9. When using iOS 9+ the presented image (after the UIImagePickerController is dismissed) is an incorrect version of the selected image. Without re-sizing or cropping the final image is only the top right corner of the original image ?? Plus another problem - If imagePicker.allowsEditing = false, you are unable to select images from the PhotoLibrary ??
#IBAction func photoButton(sender: AnyObject) {
imagePicker.allowsEditing = true
imagePicker.sourceType = .PhotoLibrary
self.presentViewController(imagePicker, animated: false, completion: nil)
}
func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject]) {
if let pickedImage = info[UIImagePickerControllerEditedImage] as? UIImage {
self.imageView.image = pickedImage
dismissViewControllerAnimated(true, completion: { () -> Void in
})
}
Heres an example of a selected image presented in a UIImagePickerController. (notice how the selected image is presented very small and not full size/width of screen as before)
After selecting the use button within the UIImagePickerController the final image is only the top right of the original image. What am I doing wrong or is UIImagePickerController broken on iOS 9 ?
This is a bug from Apple:
http://openradar.appspot.com/radar?id=5032957332946944
Current lousy workaround:
if UIDevice.currentDevice().userInterfaceIdiom == .Pad {
imagePicker.allowsEditing = false
} else {
imagePicker.allowsEditing = true
}
Swift 3.0:
if UIDevice.current.userInterfaceIdiom == .pad {
}
Refer the below coding
#IBAction func photoButton(sender: AnyObject)
{
if(UIImagePickerController .isSourceTypeAvailable(UIImagePickerControllerSourceType.Camera))
{
picker!.sourceType = UIImagePickerControllerSourceType.Camera
self .presentViewController(picker!, animated: true, completion: nil)
}
else
{
let alertWarning = UIAlertView(title:"Warning", message: "You don't have camera", delegate:nil, cancelButtonTitle:"OK", otherButtonTitles:"")
alertWarning.show()
}
}
func openGallary()
{
picker!.sourceType = UIImagePickerControllerSourceType.PhotoLibrary
self.presentViewController(picker!, animated: true, completion: nil)
}
//PickerView Delegate Methods
func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [NSObject : AnyObject])
{
picker .dismissViewControllerAnimated(true, completion: nil)
imageView.image=info[UIImagePickerControllerOriginalImage] as? UIImage
}
func imagePickerControllerDidCancel(picker: UIImagePickerController)
{
println("picker cancel.")
}
You need to make sure you have the correct frame for your imageview and the contentmode for your imageview should be aspectfit and the image returned by the picker is of type [UIImagePickerControllerOriginalImage]
[imageView setFrame:imageViewRect];
[imageView setContentMode:UIViewContentModeScaleAspectFit];
Swift:
imageView.setFrame(imageViewRect)
imageView.setContentMode(UIViewContentModeScaleAspectFit)
and coming to the allowsEditing property, It is not related to photo selection.
It is a Boolean value indicating whether the user is allowed to edit a
selected still image or movie.
If you want to select photos from camera library, then you need to modify the source type to
picker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
Update:
LEts say you have selected one image and display it on the image view. I assume you have one imageview on the view, and the imageview frame is equal to the view frame. If the IB is of freeform , I assume the size 600x600 for the imageview frame in the IB.
if you simply do:
_startImageView.image=_img;
The result will be:
Now let's make some changes to the image to be displayed in the imageview:
CGRect scaledRect = AVMakeRectWithAspectRatioInsideRect(_img.size, CGRectMake(0, 0, self.startImageView.frame.size.width, self.startImageView.frame.size.height));
UIGraphicsBeginImageContext(CGSizeMake(_startImageView.frame.size.width,_startImageView.frame.size.height));
[_img drawInRect:scaledRect];
UIImage *scaledImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
_startImageView.image=scaledImage;
and now the image will be:
The original image choosen is of size 640x426, when not scaled.
The original image choosen is of size 1536x1286 when scaled to max(with two finger zoom action).
As you can see, still there is no huge change in the image, it is because the image is already cropped/scaled by the time your imageview recieved the image!!!
So even though you try to do:
[_img drawInRect:_startImageView.frame];
The image will not be drawn as per our need, as the image is already scaled right at them moment as the image given by the picker is the edited image.
Solution:
To fix this, you need to select the original image from picker in didFinishPickingMediaWithInfo: method
info[UIImagePickerControllerOriginalImage];
which gives you the image:
This code is relatively similar to #pkc456, just a little shorter.
This works perfectly for me:
import UIKit
class PostImageViewController: UIViewController, UINavigationControllerDelegate, UIImagePickerControllerDelegate {
#IBOutlet var imageToPost: UIImageView!
#IBAction func chooseImage(sender: AnyObject) {
var image = UIImagePickerController()
image.delegate = self
image.sourceType = UIImagePickerControllerSourceType.PhotoLibrary
image.allowsEditing = false //or true additional setup required.
self.presentViewController(image, animated: true, completion: nil)
}
func imagePickerController(picker: UIImagePickerController, didFinishPickingImage image: UIImage, editingInfo: [String : AnyObject]?) {
self.dismissViewControllerAnimated(true, completion:nil)
}
Does this work?
I am working on xcode 7.1(Swift) and found your code appropriate. I also wrote the below code on my project and it is working successfully.
func showPicker(){
let type = UIImagePickerControllerSourceType.PhotoLibrary
if(UIImagePickerController.isSourceTypeAvailable(type)){
let pickerController = UIImagePickerController()
pickerController.delegate = self
pickerController.sourceType = UIImagePickerControllerSourceType.PhotoLibrary
pickerController.allowsEditing = false
self.presentViewController(pickerController, animated: true, completion: nil)
}
}
func imagePickerController(picker: UIImagePickerController!, didFinishPickingImage image: UIImage!, editingInfo: NSDictionary!) {
let imageView = self.view.viewWithTag(20) as! UIImageView
let selectedImage : UIImage = image
imageView.image=selectedImage
self.dismissViewControllerAnimated(true, completion: nil)
}
The only difference in your code and my code is about visibility of ImagePickerController instance. For your reference, I upload my code at:-
https://www.dropbox.com/s/pe0yikxsab8u848/ImagePicker-Swift.zip?dl=0
My idea is just look at my code once, may be you will get an idea about which section of your code malfunctioning.
It seems like you are using IB and auto layout but have set a right constraint. Try to add some constraint in your storyboard.
for me I was solved, showing mode as popover
#IBAction func photoButton(sender: AnyObject) {
imagePicker.allowsEditing = true
imagePicker.sourceType = .PhotoLibrary
let controller = self.imagePicker
controller.modalPresentationStyle = UIModalPresentationStyle.Popover
controller.modalTransitionStyle = UIModalTransitionStyle.CoverVertical
let popover = controller.popoverPresentationController
popover?.sourceView = self
controller.preferredContentSize = CGSize(
width: self.frame.width * 0.6,
height: self.frame.height * 0.6
)
popover?.sourceRect = CGRectMake(
CGRectGetMidX(self.bounds),
CGRectGetMidY(self.bounds),
0,
0
)
self.presentViewController(self.imagePicker, animated: true, completion: nil)
}
I want the view to load with a default image and let the user tap this image to select another from the photo library. I'm doing this way, but the tap gesture is not working, what am I doing wrong?
class ProfileViewController: UIViewController, UINavigationControllerDelegate, UIImagePickerControllerDelegate {
let imagePicker = UIImagePickerController()
#IBOutlet var profilePicture: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
let tap = UITapGestureRecognizer(target: self, action: Selector("selectProfilePicture"))
profilePicture.addGestureRecognizer(tap)
imagePicker.delegate = self
}
func selectProfilePicture() {
imagePicker.sourceType = UIImagePickerControllerSourceType.PhotoLibrary
imagePicker.allowsEditing = false
self.presentViewController(imagePicker, animated: true, completion: nil)
}
func imagePickerController(picker: UIImagePickerController, didFinishPickingImage image: UIImage, editingInfo: [String : AnyObject]?) {
self.dismissViewControllerAnimated(true, completion: nil)
profilePicture.image = image
}
}
Try to enable userInteractionEnabled on your UIImageView:
profilePicture.userInteractionEnabled = true
I tend to just put an invisible UIButton on top of the image. Easiest / simplest way to do it, without overcomplicating it.
I’m trying to change the image of a button when tapped (it’s for an audio player that I want Play to change to Pause). So far I’ve only managed to change it when the button is held, then it changes back when released.
I’ve tried using Selected instead of Highlighted but that doesn’t work. (The initial image is set in the Attributes Inspector). This is what I have -
#IBAction func buttonTapped(sender: AnyObject) {
ColourTestButton.setImage(UIImage(named: "blank-purple.jpg"), forState:.Highlighted)
}
I realise Highlighted means just that though on another image where I wanted it to change back (in a Cell of a Table View) it stayed, which is why I thought it would work for this one. What do I have to do here to make it stay changed to the new image?
I think you need this code:
#IBAction func buttonTapped(sender: AnyObject) {
let image = UIImage(named: "blank-purple.jpg") as UIImage!
let playButton = UIButton.buttonWithType(UIButtonType.Custom) as! UIButton
ColourTestButton.setImage(image, forState: .Normal)
}
UPDATE:
If you want to change the image like play/pause then you can do it this way:
var pressed = false
#IBAction func pressed(sender: AnyObject) {
if !pressed {
let image = UIImage(named: "pauseImage.png") as UIImage!
let playButton = UIButton.buttonWithType(UIButtonType.Custom) as! UIButton
btn.setImage(image, forState: .Normal)
pressed = true
} else {
let image = UIImage(named: "playImage.png") as UIImage!
let playButton = UIButton.buttonWithType(UIButtonType.Custom) as! UIButton
btn.setImage(image, forState: .Normal)
pressed = false
}
}
I think you are updating the image only at Highlighted state, so when you release the button it goes back to an original image. Try to change it at Normal state so that it will change the image. Take one flag to check the Play and Pause condition.
#IBAction func buttonTapped(sender: AnyObject) {
if (!flag)
{
//for Play
flag = true
ColourTestButton.setImage(UIImage(named:"USE PLAY IMAGE"), forState:. Normal)
}
else
{
//for Pause
flag = false
ColourTestButton.setImage(UIImage(named:"USE PAUSE IMAGE"), forState:. Normal)
}
}
Though Dharmesh Kheni's answer is OK I would recommend to use two buttons. You can set them in separate function or in interface builder and then just show and hide them according to state. Something like this:
func setButtons()
{
playBtn.setImage(UIImage(named: "play.png"), forState:.Normal)
pauseBtn.setImage(UIImage(named: "play.png"), forState:.Normal)
pauseBtn.hidden = true
}
#IBAction func playTapped(sender: AnyObject)
{
playBtn.hidden = true
pauseBtn.hidden = flase
//do other stuff
}
#IBAction func pauseTapped(sender: AnyObject)
{
playBtn.hidden = false
pauseBtn.hidden = true
//do other stuff
}
I have minor problem that whenever I delete the Outlet (imageChosen) shown in the picture below (with the yellow warning), the app crashes when the image supposed to be passed
here how i defined it
var imageChosen: UIImageView!
this is how i saved the image chosen from the gallery to the imageChosen
func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [NSObject : AnyObject]) {
var chosenImage = info[UIImagePickerControllerOriginalImage] as! UIImage
imageChosen.contentMode = .ScaleAspectFit
imageChosen.image = chosenImage
dismissViewControllerAnimated(true, completion: nil)
self.performSegueWithIdentifier("next", sender: self)
}
this where I pass the image to the next activity
override func prepareForSegue(segue: (UIStoryboardSegue!), sender: AnyObject!) {
if segue.identifier == "next" {
var pass:postView = segue.destinationViewController as! postView
pass.currentImage = imageChosen.image
}
}
when I delete the outlet in the picture, the app crashes because the image is nil, even though the code seems to be okay,
can anyone please help me
The reason why you keep getting the error is because you are assign image without allocation of your imageChosen.
The Solution
override func viewDidLoad() {
super.viewDidLoad()
picker.delegate = self
// Add this line
imageChosen = UIImageView()
}
After you have declare that imageChosen = UIImageView(), it will solve your problem
Hope that helps!
You need to delete the outlet from the ViewController(swift file) as well, where you see the outlets in the pic you post click in the (x) to delete the outlet from imageChosen as it does not exist anymore (you can see the warning from Code in form of a yellow triangle).
You should also change your code to:
var imageChosen: UIImageView?
this is how i saved the image chosen from the gallery to the imageChosen
func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [NSObject : AnyObject]) {
if let image = info[UIImagePickerControllerOriginalImage] as UIImage{
imageChosen.contentMode = .ScaleAspectFit
imageChosen.image = image
}
else
{
//something went wrong
}
dismissViewControllerAnimated(true, completion: nil)
self.performSegueWithIdentifier("next", sender: self)
}
this where I pass the image to the next activity
override func prepareForSegue(segue: (UIStoryboardSegue!), sender: AnyObject!) {
if segue.identifier == "next" {
if let pass = segue.destinationViewController as postView
{
if let image = imageChosen.image{
pass.currentImage = imageChosen.image
}
}
}
}
I did it very verbose to make it easier to understand