How to dismiss a popover only with a button in swift - ios

My iPad app has several data gathering popovers, and I want to be able to disable the dismissal of the popover by touching outside of it, I then will use a button to quit the popover at the users discretion.
The app looks great, the popovers work fine, and I have a button inside them that quits nicely. Only I can't find a way of disabling dismissal in Swift, lots of posts on obj-c but nothing in Swift.
Does this mean that the functionality is no longer available?
I would greatly appreciate any help to my frustration.

Simply set the view controller's modalInPopover to true and the popover's passthroughViews to nil. But you must do the latter using delayed performance or it won't work. A small delay is all that's needed. Example:
let vc = UIViewController()
vc.modalPresentationStyle = .Popover
self.presentViewController(vc, animated: true, completion: nil)
if let pop = vc.popoverPresentationController {
vc.modalInPopover = true
delay(0.1) {
pop.passthroughViews = nil
}
}
For the delay function, see dispatch_after - GCD in swift?.

Related

InAppSettingsKit Done button does not show up on view controller

I am trying to use InAppSettingsKit from my Swift app (via Swift package dependency to version 3.3.3), and I would like to be able to use the settingsViewControllerDidEnd delegate callback to determine when the user has dismissed the settings dialog, so that I can check for certain conditions that may require additional actions on the user's part.
The Done button was showing up if I pushed the view controller onto a navigation controller, but the code indicates that this method will not fire the Done button delegate callback, so I have been trying to use the present method to show the view controller.
Here is the code that I am using to instantiate and present the settings view controller:
func authenticationSettings(alert: UIAlertAction!) {
let viewController = IASKAppSettingsViewController()
viewController.delegate = self
self.present(viewController, animated: true, completion: nil)
}
And here is what I get, notice no Done button:
I have tried this card method of presenting, and also the full screen method, with no avail.
I tried stepping into the Objective-C code and and from what I could tell, the UIBarButtonItem navigation item was being created and added. Anyone have any ideas on what to try next?
As you may have noticed in the source code, UIBarButtonItem gets added on navigationItem. This item is used only if view controller is part of a navigation controller stack
When you're presenting a new view controller modally it doesn't have a navigation controller in the stack, so to make it work you need to wrap your controller with a UINavigationController:
func authenticationSettings(alert: UIAlertAction!) {
let viewController = IASKAppSettingsViewController()
viewController.delegate = self
let navigationController = UINavigationController(rootViewController: viewController)
self.present(navigationController, animated: true, completion: nil)
}

Window hierarchy ios

I have a question that probably already has been answered but I just can't find it.
I have a view that calls some webservice and when the result comes back I want to open another view to present the result. Right after the call to WS I close that window.
Now my problem is that when I try to open it with presentViewController with
UIApplication.sharedApplication().keyWindow?.visibleViewController!.presentViewController or
UIApplication.sharedApplication().keyWindow?.visibleViewController!.childViewControllers[0].presentViewController
the same is if I access to rootViewControler.
I get the error that the view is not in the hierarchy any more since it already called viewWillDisappear.
So is there any way to show a view over all views no matter what is the current top view?
EDIT:
The solution was really ugly and stupid. I have a static variable which gets changed every time viewDidApear is called to that view. This way I have the last view that is shown. I know this isn't good way to do it or maybe even not safe way, but I don't have any other idea.
Yes, there is a way to show ViewController on top of another, but for this you should not touch your window at all.
In your view controller class right after your call completed call
let viewController = ViewController() //change this to your view controller class that you want to present
self.presentViewController(viewController, animated: true, completion: nil)
Call web service and navigate your view controller in main thread using below code.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// do some background task
dispatch_async(dispatch_get_main_queue(), ^{
// update UI here
// navigate your view controllers
let vc: UIViewController = (self.storyboard?.instantiateViewControllerWithIdentifier("viewContIdentifire"))!
self.presentViewController(vc, animated: true, completion: nil)
// **OR**
self.navigationController?.pushViewController(vc, animated: true)
});
);
Navigate back to VC in more convenient way using
// using this navigate your app to root view of ui stack
self.navigationController?.popToRootViewControllerAnimated(true)
// using this line of code navigate to any view
self.navigationController?.popToViewController(viewContName, animated: true)
// this navigate back to last view
self.navigationController?.popViewControllerAnimated(true)

Trigger viewWillAppear when using a transitioning delegate

I want to present a view controller:
Modally
Using custom transitions
At the same time, I want to make sure that when it is dismissed, the view controller behind it is aware of being pushed to the foreground.
My basic idea is to try some different configurations and see which one will lead to viewWillAppear being called on the view controller behind.
Attempt 1
presentedViewController.modalPresentationStyle = .Custom
presentedViewController.transitioningDelegate = someTransitioningDelegate
The results of this approach:
The custom transition works perfectly well
viewWillAppear does not get called on the view controller behind presentedViewController when I call presentedViewController.dismissViewControllerAnimated(true)
I do want viewWillAppear to be called on the view controller below the one being dismissed, so I did this:
Attempt 2
presentedViewController.modalPresentationStyle = .FullScreen
presentedViewController.transitioningDelegate = someTransitioningDelegate
or
presentedViewController.modalPresentationStyle = .FullScreen
presentedViewController.modalTransitionStyle = .CoverVertical
presentedViewController.transitioningDelegate = someTransitioningDelegate
The results of this approach:
viewWillAppear gets called on the view controller behind presentedViewController when dismissing presentedViewController
The transition occurs as expected when presenting the view controller.
When dismissing the view controller, the background during the transition is black, which is undesirable.
Seems that .FullScreen causes the view controllers behind presentedViewController to be removed from the display hierarchy - which is good because presumably that's what triggers the viewWillAppear call.
Attempt 3
presentedViewController.modalPresentationStyle = .FullScreen
presentedViewController.modalTransitionStyle = .CoverVertical
The results of this are:
viewWillAppear gets called on the view controller behind presentedViewController.
The background during the transition is the view controller located behind presentedViewController, which is desired.
No custom transition.
The project I'm working on is structured in a way that makes it difficult to use delegation (which seems to be the suggested answer here). Making use of NSNotificationCenter is another alternative which lets me call the code that is supposed to be called by viewWillAppear, but from attempt 3, I'm hoping there is a more elegant approach to achieve all these:
Trigger viewWillAppear
Use a custom transition
See the view controller being presented in the background during the transition animation
Seems Apple considers it foul play to invoke viewWillAppear etc., but it's okay to invoke beginAppearanceTransition and endAppearanceTransition, which in turn will invoke viewWillAppear. I'm going with this.
The way I have achieved this in the past is by calling viewWillAppear and viewDidAppear from the transition animator. Here's a simplified example:
public func animateTransition(transitionContext: UIViewControllerContextTransitioning) {
let fromVC = transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey)
let toVC = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey)
fromVC.viewWillDisappear(true)
toVC.viewWillAppear(true)
UIView.animateWithDuration(0.3, animations: {
//Animations here
}, completion: { (success) in
toVC.viewDidAppear(success)
fromVC.viewDidDisappear(success)
transitionContext.completeTransition(true)
})
}
I call the "will" methods before I do animations, and the "did" methods after completion

Issue with UIImagePickerController - How to switch to new controllers without a segue?

I'm trying to get taking photos working on an iOS app. To do so, I do the following...
imagePicker = UIImagePickerController()
imagePicker.delegate = self
imagePicker.sourceType = .Camera
imagePicker.allowsEditing = true
self.presentViewController(imagePicker, animated: true, completion: nil)
The issue I have is that I keep getting:
2016-02-28 13:12:17.767 Instagram[60379:2605934] Presenting viewcontrollers on detached view controllers is discouraged <Instagram.ViewController: 0x7937ed50>.
2016-02-28 13:12:18.282 Instagram[60379:2605934] Unbalanced calls to begin/end appearance transitions for <UINavigationController: 0x7a402a00>.
Whenever I try to do this. I assumed this had something to do with not transitioning to the ViweController correctly (there are two ViewControllers before this one - ViewController (where users login), and PhotoTableController (where users view a feed, and can press to add their own photo).
The first way I tried to transition between the two was the way I've always done it programmatically:
self.performSegueWithIdentifier("loginSegue", sender: nil)
But that caused the error above. So I thought I should use the "presentViewController" like I did for the imagePicker:
self.presentViewController(PhotoTableController(), animated: true, completion: nil)
I did something similar for the transition from PhotoTableController to PostPhoto as well, but it didn't matter because when the user would login and do that first transition, the screen would go completely black.
I'm not sure what exactly is causing this issue, or if I'm going about solving the issue correctly. And a lot of what I google is Objective C code, which I cannot understand.

Change view controller when user exits app

Hey I am trying to make it so whenever the user presses the home button and then try to returns to my app, they are returned to the initial view controller. Is there anything I can write into the app delegate to get this to work?
I have tried to call this function in the applicationDidBecomeActive function:
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let VC = storyboard.instantiateViewControllerWithIdentifier("homeVC") as! UIViewController
self.window?.rootViewController?.presentViewController(VC, animated: true, completion: nil)
But i get an error saying the view is not in the hierarchy.
Any help will be appreciated!
You want to implement the applicationDidBecomeActive method of your App's delegate, and programmatically perform a segue or dismiss the current view controller, depending on the navigation setup.
Your users might not like forced navigation though. The general assumption is that they can resume where they left off.
Well, every time the app is closed applicationDidEnterBackground in your AppDelegate is called. You can use that to present your view controller.

Resources