How does presentViewController display the view? - swift

I'm curious as to what the presentViewController does with the first parameter, imagePickerController. Given the way I assigned imagePickerController's properties, will the presentViewController use all that information to display the view?
#IBAction func imageFromPhotoLibrary(sender: UITapGestureRecognizer) {
// Hide the keyboard.
myTextField.resignFirstResponder()
// UIImagePickerController is a view controller that lets a user pick media from their photo library.
let imagePickerController = UIImagePickerController()
// Only allow photos to be picked, not taken.
imagePickerController.sourceType = .PhotoLibrary
// Make sure ViewController is notified when the user picks an image.
imagePickerController.delegate = self
presentViewController(imagePickerController, animated: true, completion: nil)
}

presentViewController won't use any of the options. The imagePickerController you passed in will use those options.
Since the Apple APIs are pretty well designed, presentViewController does one thing (SRP), and does exactly what it says it does... presents the view controller you pass it.
The imagePickerController is in charge controlling the view of the image picker.

Related

Swift Modal ViewController. Is there a easy way to find the result of the ViewController

I have a View(ViewController1) that overlays a modal ViewController2.
ViewController2 has three options
Delete all sessions
Select a place
Cancel
It's fairly easy to get to launch the modal viewController as this in ViewController1
#objc func presentMenu() {
let overlayController = ViewController2()
overlayController.transitioningDelegate = self
overlayController.modalPresentationStyle = .fullScreen
present(overlayController, animated: true)
}
This basically shows ViewController2. However viewController1 code has gone past this and where do I find out that this ViewController2 has been dismissed?
You could implement viewWillAppear and try to figure out that the reason you're appearing is that the presented fullscreen view controller is being dismissed. But it would be even better to use the standard pattern where the presented fullscreen view controller tells you that it is being dismissed. To make that possible, it has a delegate, which you make sure is you:
overlayController.modalPresentationStyle = .fullScreen
overlayController.delegate = self
present(overlayController, animated: true)
Then the overlay controller calls a known method in its delegate when it is about to be dismissed:
self.delegate.dismissalIsHappening()
That method is usually defined by way of a protocol, which is why this is called the protocol and delegate pattern. I have not shown you the entire pattern! If you search on that term, you'll find lots of complete examples.

swift xcode iOS: can I re-use a loaded modal fullscreen view controller?

I have a storyboard with two view controllers. First one, VC_1, has one button that opens 2nd one - VC_2.
VC_2 also has a button that opens VC_1.
Both controllers have almost identical code:
class VC_1: UIViewController
{
override func viewDidLoad()
{
super.viewDidLoad()
print(“VC_1 loaded")
}
override func viewDidAppear(_ animated: Bool){ print(“VC_1 appeared") }
override func viewDidDisappear(_ animated: Bool){ print(“VC_1 disappeared") }
#IBAction func btnShowVC_2(_ sender: UIButton)
{
let storyboard = UIStoryboard(name: "Main", bundle: nil)
secondVC = storyboard.instantiateViewController(identifier: “VC_2”)
secondVC.modalPresentationStyle = .fullScreen
show(secondVC, sender: self)
}
}
The difference is only in "VC_2" instead of "VC_1" in the 2nd controller code.
I have seen this View Controller creation code in Apple documentation and many other examples around the Internet.
When I press the button on the VC_1, I see in the debug window, that VC_2 is loaded and appeared, and VC_1 is disappeared. And same, of course, happens when I press the button on VC_2 - it disappears, and VC_1 is loaded again.
My questions are:
what happens with View Controller object after "viewDidDisappear" has been called? Does it really disappear from memory, or "disappear" only means "you cannot see it on the screen?". I do not see "viewDidUnload" in the documentation...
I suppose that "viewDidLoad" means that new View Controller object was created in memory. Is there any way to load the View Controller object only once, and then hide and show it without causing "viewDidLoad" to be called? I tried to do it with global variable "secondVC" but got "Application tried to present modally an active controller" error.
viewDidDisappear: called after the view is removed from the windows’
view hierarchy. No, View controller object just left the view property. By the way the amount of memory used by view controllers is negligible. So dont think about too much. If you want to catch when Your View controller object release from the memory put
deinit { print("vc deallocated") }
viewDidUnload, it has been deprecated since the iOS
6, which used to do some final cleaning.
Partly true. Keep in mind ViewDidload called one time for the life cycle of view controller. There is a method called before viewdidload but this is not related with your question.
In addition to "There is a method before viewdidload" -> loadView( ) is a method managed by the viewController. The viewController calls it when its current view is nil. loadView( ) basically takes a view (that you create) and sets it to the viewController’s view (superview).

UIBarButtonItem not triggering IBAction

I'm trying to create a UIBarButtonItem that opens the camera once the button has been tapped. For some reason, my takePicture(_sender:) function doesn't seem to be getting called.
I originally tried to create my UIBarButtonItem using the Interface Builder. Here was the interface and a screenshot of the actions connected to the UIBarButtonItem:
And here is the code for my takePicture(_sender:) function:
#IBAction func takePicture(_ sender: UIBarButtonItem) {
print("Taking picture...")
let imagePicker = UIImagePickerController()
// If the device has a camera, take a picture; otherwise,
// just pick from photo library
if UIImagePickerController.isSourceTypeAvailable(.camera) {
imagePicker.sourceType = .camera
} else {
imagePicker.sourceType = .photoLibrary
}
imagePicker.delegate = self
// Place image picker on the screen
present(imagePicker, animated: true, completion: nil)
}
The little circle next to my function declaration is filled in and connected properly:
However, when I load the simulator and press the button, the UIImagePickerController never appears and my print() function is never called in the code.
So, I then tried to declare the UIBarButtonItem programmatically to see if perhaps it was an issue under-the-hood of Xcode's Interface Builder. Here was my code:
(Note: I deleted the UIBarButtonItem from the Interface Builder and then I connected the UIToolbar to my code using an #IBOutlet.)
override func viewDidLoad() {
super.viewDidLoad()
let takePictureBarButton = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.camera, target: self, action: #selector(DetailViewController.takePicture))
toolBar.setItems([takePictureBarButton], animated: false)
}
#objc func takePicture() {
print("Taking picture...")
let imagePicker = UIImagePickerController()
// If the device has a camera, take a picture; otherwise,
// just pick from photo library
if UIImagePickerController.isSourceTypeAvailable(.camera) {
imagePicker.sourceType = .camera
} else {
imagePicker.sourceType = .photoLibrary
}
imagePicker.delegate = self
// Place image picker on the screen
present(imagePicker, animated: true, completion: nil)
}
As a last ditch attempt at debugging this program and trying to find where the problem lies, I created a temporary button in my UI and connected that to my takePicture(_sender:) function instead (I changed the signature from a UIBarButtonItem to a UIButton). That worked perfectly. This tells me that the problem is not with the function itself, but has something to do with the connection.
I was seconds away from posting this question when I found a similar question. The user stated that the root of their problem lied in a UITapGestureRecognizer on their View Controller.
To solve my problem, I simply set my UITapGestureRecognizer's target to be my StackView instead of the the entire view.
Original:
Resolved:
I'd be curious to learn why the UITapGestureRecognizer stopped my UIBarButtonItem from being tapped, but not the normal UIButton. Seems a little strange...
Swift 4
I went through this problem and referenced above answers. The project I worked on had UIButton as navigation bar item and hence it comes under navigation item and I found out that the outlet for the IBAction should be connected to the button in the bar button item rather than connecting it to the bar button item itself. In mattkx4's question, the IBAction has 'UIBarButtonItem' as its sender. I fixed the issue by connecting the button inside the bar button item to the action.
As you can see there is a button inside the bar button item. Connect the action to this button and the action will be triggered.
make sure you connect your camera button with action for touchUpInside event

Warning: Attempt to present view controller on another view controller whose view is not in the window hierarchy

I have a working simple single player game, where the initial view controller has a button to start the game. This button performs a segue and all game logic in the GameViewController is working as expected.
I've followed this tutorial to add multi player functionality to my game.
On the initial view controller, a button now calls
GameKitHelper.sharedGameKitHelper.findMatchWithMinPlayers(2, maxPlayers: 2, viewController: self, delegate: MultiPlayerNetworking)
}
which has the following implementation in GameKitHelper.swift:
func findMatchWithMinPlayers (minPlayers: Int, maxPlayers: Int, viewController: UIViewController, delegate: GameKitHelperDelegate) {
matchStarted = false
let request = GKMatchRequest()
self.delegate = delegate
request.minPlayers = 2
request.maxPlayers = 2
presentingViewController = viewController
presentingViewController.dismissViewControllerAnimated(false, completion: nil)
let mmvc = GKMatchmakerViewController(matchRequest: request)
mmvc?.matchmakerDelegate = self
presentingViewController.presentViewController(mmvc!, animated: true, completion: nil)
self.delegate?.matchStarted()
}
The Class MultiPlayerNetworking implements the GameKitHelper protocol, and gets called on the matchStarted function.
The MultiPlayerNetworking class in essence takes over here, and starts sending out messages to hosts and remote players.
Note that some time later, When auto-matching finishes, the following function gets called in GameKitHelper:
func matchmakerViewController(viewController: GKMatchmakerViewController, didFindMatch match: GKMatch) {
viewcontroller.dismissViewControllerAnimated(true, completion: {})
self.match = match
match.delegate = self
}
Now, I think this says that the GKMatchmakerViewController is dismissed, thereby showing me the initial view controller again (and this is what happens on screen).
Now my issue! After the GKMatchmakerViewController is dismissed, I'm back at the initial view controller and want to 'simulate' an automatic segue to my gameView (which has logic to deal with a multi player game as well).
I've made the initial view controller conform to the MultiPlayerNetworking protocol, which has a function to simulate a segue:
func segueToGVC() {
self.performSegueWithIdentifier("game", sender: nil) // self = initial view controller
}
However, xCode complains with:
Warning: Attempt to present <GameViewController: 0x7d440050> on <GKMatchmakerViewController: 0x7c8fbc00> whose view is not in the window hierarchy!
I'm stuck here, and have tried so many different methods of dismissing the view controller, to making sure I'm calling the performSegue function on the topViewController via this link, but nothing works.
My question: why is the GKMatchmakerViewController visually dismissed, but still present in the view hierarchy, such that calling a performSegue function on the initial view controller give the above error/warning?
Views are greatly appreciated!
why is the GKMatchmakerViewController visually dismissed, but still present in the view hierarchy
Here are two suggestions:
Perhaps it's because dismissal takes time. You are saying:
viewcontroller.dismissViewControllerAnimated(true, completion: {})
So there's an animation. Don't attempt to perform the next segue until the animation is over.
Perhaps you are just wrong about who self is. You are saying:
self.performSegueWithIdentifier("game", sender: nil)
// self = initial view controller
We have only your word, in that comment, for who self is. Meanwhile, the runtime seems to think differently about the matter:
Attempt to present <GameViewController: 0x7d440050> on <GKMatchmakerViewController: 0x7c8fbc00>
It might be good to believe the runtime; after all, it knows more than you do.

Pick image from custom image library [Swift]

I am new to Swift/app development. I have successfully developed an app that uses an UIImagePickerController to let the user select an image from the device's photo library. However, I would like the user to select images from a custom set of images, and not from the device's photo library.
What would be the easiest way to implement this? Any help appreciated!
Edit
As per requested here is some of the code I am using.
I have a view controller that adheres to the following protocols:
class StockViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate
On my main storyboard, I have an UIImageView with a tap gesturer which triggers an action on selectImageFromPhotoLibrary.
#IBAction func selectImageFromPhotoLibrary(sender: UITapGestureRecognizer) {
// UIImagePickerController is a view controller that lets a user pick media from their photo library.
// Hide the keyboard.
let imagePickerController = UIImagePickerController()
// Only allow photos to be picked, not taken.
imagePickerController.sourceType = .PhotoLibrary
imagePickerController.allowsEditing = false
// Make sure ViewController is notified when the user picks an image.
imagePickerController.delegate = self
presentViewController(imagePickerController, animated: true, completion: nil)
}
The only source types I can select for the imagePickerController are SavedPhotoAlbums, Camera or PhotoLibrary. I cannot select any app image folder.