I have an impression that ViewController usage are similar: on story board, we drag an UIViewController onto scene, then change its class type, e.g. to UIImagePickerController. (I want to make a dedicated scene for picking images)
But later I find that UIImagePickerController won't work if I directly subclass:
class TestUIImagePickerController: UIImagePickerController, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
self.sourceType = .photoLibrary
self.delegate = self
// self.present(self, animated: true) // either comment it out or not, both way won't work.
}
But it works only if I put an UIViewController on storyboard, then construct an UIImagePickerController programmatically:
class SecondTestUIImagePickerController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
let picker = UIImagePickerController()
picker.sourceType = .photoLibrary
picker.delegate = self
self.present(picker, animated: true)
}
May I know whether I missed anything in the first usage example? And is it a must to create UIImagePickerController programmatically then present it via an agent view controller (UIVIewController)?
self.present(self, animated: true)
you can not self present self, use another ViewController to present UIImagePickerController
UIImagePickerController can be used on Story board, in your code, for example
class SecondTestUIImagePickerController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
let sb = UIStoryboard(name: "ImagePickerStoryboard", bundle: nil)
let picker = sb.instantiateViewControllerWithIdentifier("ImagePicker") as! TestUIImagePickerController
self.present(picker, animated: true)
}
keep in mind that UIImagePickerController is subclass of UINavigationController
Related
Is there a way to present an UIImagePickerController from inside a modal view?
In my case I have a class called MakeWishView which is a UIView and the user should be able to pick an image if he taps on a button inside the MakeWishView. My problem right now is that I can not call present because MakeWishView is not a ViewController but a UIView.
This is what I would like to call:
#objc private func wishImageButtonTapped(){
showImagePickerController()
print("wishImageButtonTapped")
}
// image picker
extension MakeWishView: UIImagePickerControllerDelegate, UINavigationControllerDelegate {
#objc func showImagePickerController(){
let imagePickerController = UIImagePickerController()
imagePickerController.delegate = self
present(imagePickerController, animated: true, completion: nil) // -> error
}
}
Add a delegate to the presenting vc like
class MakeWishView:UIView {
weak var delegate:VCName?
or
let root = (UIApplication.shared.delegate as! AppDelegate).window!.rootViewController
root.present(imagePickerController, animated: true, completion: nil)
i want import image in ios from library but i have error
value of type 'viewcontroller' has no member 'present'
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
#IBAction func Alert(sender: AnyObject) {
let nextController = UIImagePickerController()
self.present(nextController, animated: true, completion: nil)
}}
Here's my stripped working code. I use two pickers - one for camera, one for library - because of some odd iOS bug with tint color. To avoid "massive view controller" issues, I tend to move everything possible related to a a UIImagePickerController into a separate file and extension.
class OpenViewController: UIViewController {
let pickerLibrary = UIImagePickerController()
override func viewDidLoad() {
super.viewDidLoad()
pickerLibrary.delegate = self
}
}
extension OpenViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate {
#objc func showImagePicker() {
pickerLibrary.allowsEditing = false
pickerLibrary.sourceType = .photoLibrary
present(pickerLibrary,
animated: true,
completion: nil)
pickerLibrary.popoverPresentationController?.sourceView = self.view
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
let selectedImage = info[UIImagePickerControllerOriginalImage] as! UIImage
dismiss(animated: true, completion: nil)
}
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
dismiss(animated: false, completion: nil)
}
}
While the present code looks correct, I notice several things you didn't post in your code. Maybe because you just didn't post it, but I would definitely check these things:
Are you setting ViewController to be a delegate?
Are you setting ViewController.view to be the source view? This may not be needed for iPhone, but definite helps with iPad.
Have you includes everything needed to be working for UIImagePickerControllerDelegate, UINavigationControllerDelegate?
Now as your error suggests, something else is likely the issue - a UIViewController can present another view controller. In this case you've given us no way to duplicate that error. In other words, I'm assuming that (1) You are seeing a UIButton attached to Alert() - BTW, Swift conventions use lower case for that - and (2) You've proven that when that button is tapped, `Alert() is actually executed.
I am very new to iOS development and Swift, so sorry if this is a trivial question. I have the following code
import UIKit
class ViewController: UIViewController, UINavigationControllerDelegate, UIImagePickerControllerDelegate {
var imagePicker: UIImagePickerController!
#IBOutlet weak var imageView: UIImageView!
#IBAction func openGalery(_ sender: UIButton) {
if UIImagePickerController.isSourceTypeAvailable(.photoLibrary) {
imagePicker = UIImagePickerController()
imagePicker.delegate = self
imagePicker.sourceType = .photoLibrary;
imagePicker.allowsEditing = true
present(imagePicker, animated: true, completion: nil)
}
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
imagePicker.dismiss(animated: true, completion: nil)
imageView.image = info[UIImagePickerControllerOriginalImage] as? UIImage
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
In the code I create the func imagePickerController. imagePickerController is responsible for setting the selected image in the imageView. It works. I open the galery, I select an image and there is a transition back to the view with the image set. However, why is it working? I never call the function imagePickerController. Comparing this with the languages I worked before, I should call this function on tap of the select button in the PhotoLibrary. Like an event or something. But here it is somehow called automatically, only by defining the function. Why is that?
imagePicker.delegate = self
Translates to: "Hey imagePicker, when something happens, let me know by calling my UIImagePickerControllerDelegate methods!"
In particular, your implementation of imagePickerController(_:didFinishPickingMediaWithInfo:) is being called, which is part of the UIImagePickerControllerDelegate protocol.
It's delegation pattern.
When you set imagePicker's delegate to self, you're starting receiving events from picker.
Events defined in protocol. In this case it's UIImagePickerControllerDelegate
This pattern frequently using in iOS developing.
More about delegation pattern you can read in Apple's documentation
In my screenshot below, the structure of my storyboard are UITabBarController -> UINavigationController -> UIViewController. In my UIViewController, I call .Camera using UIImagePickerController in my viewDidLoad. However, when users hit Cancel inside the camera, I dismiss the ViewController, and my tab bar dissapears!
This is my codes for dismiss:
func imagePickerControllerDidCancel(picker: UIImagePickerController) {
dismissViewControllerAnimated(true, completion: nil)
}
EDIT: Added calling of UIImagePickerController
override func viewDidLoad()
{
super.viewDidLoad()
let picker = UIImagePickerController()
picker.delegate = self
picker.sourceType = .Camera
presentViewController(picker, animated: true, completion: nil)
}
try to present the imagePicker Controller 'OverCurrentContext'
imagePicker.modalPresentationStyle = UIModalPresentationStyle.OverCurrentContext
or set it in the Storyboard in your UIViewController, hope it can help you.
I'm trying to create effect similar to Snapchat in Swift - swiping between UIImagePicker with custom controls and other VCs.
The problem is:
when CameraVC is presented for the first time background is black and swipe between VCs works only on controls (on empty space where should be image from camera it isn't) and warning shows up "Attempt to present UIImagePickerController on CameraVC whose view is not in the window hierarchy!"
when I swipe to another VC and then back to CameraVC UIImagePicker is presented properly and everything works great instead of swiping between VCs which is not working at all. There's also no "window hierarchy" warning
So I think the reason why it's not working is that UIImagePicker is presenting over PageViewController not "in" it, but I have no idea how to fix this.
I'm presenting PageViewController like this:
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
let storyboard = UIStoryboard(name: "Main", bundle: nil)
var vcPageView = storyboard.instantiateViewControllerWithIdentifier("PageViewID") as! UIViewController
self.presentViewController(vcPageView, animated: false, completion: nil)
}
Loading VCs to table in PageViewController:
override func viewDidLoad() {
super.viewDidLoad()
self.delegate = self
self.dataSource = self
let storyboard = UIStoryboard(name: "Main", bundle: nil)
var vc0 = storyboard.instantiateViewControllerWithIdentifier("CameraID") as! UIViewController
var vc1 = storyboard.instantiateViewControllerWithIdentifier("vc2ID") as! UIViewController
self.myViewControllers = [vc0, vc1]
self.setViewControllers([myViewControllers[0]], direction: UIPageViewControllerNavigationDirection.Forward, animated: false, completion: nil)
}
And finally CameraVC:
#IBOutlet var cameraOverlay: UIView!
var camera = UIImagePickerController()
override func viewDidLoad() {
super.viewDidLoad()
if UIImagePickerController.isSourceTypeAvailable(UIImagePickerControllerSourceType.Camera){
self.camera.delegate = self
self.camera.sourceType = UIImagePickerControllerSourceType.Camera;
self.camera.mediaTypes = [kUTTypeImage]
self.camera.allowsEditing = false
self.camera.showsCameraControls = false
self.cameraOverlay.frame = self.camera.cameraOverlayView!.frame
self.cameraOverlay.bringSubviewToFront(self.cameraOverlay)
self.camera.cameraOverlayView = self.cameraOverlay
self.cameraOverlay = nil
}
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
self.topMostViewController().presentViewController(self.camera, animated: false, completion: nil)
}
topMostViewController code:
extension UIViewController {
func topMostViewController() -> UIViewController {
// Handling Modal views
if let presentedViewController = self.presentedViewController {
return presentedViewController.topMostViewController()
}
// Handling UIViewController's added as subviews to some other views.
else {
for view in self.view.subviews
{
// Key property which most of us are unaware of / rarely use.
if let subViewController = view.nextResponder() {
if subViewController is UIViewController {
let viewController = subViewController as! UIViewController
return viewController.topMostViewController()
}
}
}
return self
}
}
Try this, In the CameraVC move the camera code in the viewDidLoad() to the viewDidAppear()