How should we show following ViewController? - ios

I am facing a interrogation, and I hope you could help me to find an answer.
I am selecting X items in a row.
I have already made a custom alert for 1 item selection in a row that look like this.
Here is how I call the alert
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let myAlert = storyboard.instantiateViewController(withIdentifier: "uploadAlert") as! AlertUploadViewController
myAlert.modalPresentationStyle = UIModalPresentationStyle.overCurrentContext
myAlert.modalTransitionStyle = UIModalTransitionStyle.crossDissolve
let image = self.interventionsSelected[0].picture
myAlert.initAlert(image: image!)
request.view.present(myAlert, animated: true, completion: nil)
As you can see, I'm only taking the first image selected. Now, I want to take ALL selected images.
My problematic here is that I want to link all alerts and make smooth transition between them. How can I link the dismissing of an alert and the presenting of the next one ?
Should I make a array of all alerts and catch the dismissing ?
Should I have to use closure and linking all closure ?
I am not sure how I can do that in the best possible smooth way.

you need to maintain a delegate method for the alertcontroller of yours
make a Protocol as
protocol AlertProtocol: class {
func showNextAlert()
}
now assign your main view controller as its delegate
in your alertviewcotntroller:
var delegate: AlertProtocol?
and as you click button in alert controller to do something and close the alert , in its completion handler
self.dismiss(animated: true) {
self.delegate.showNextAlert()
}
this was your current alert closes, and a window heirarcy is maintained and another alert is opened with new data in flow

I would not go with multiple alerts at the same time because it seems visually what will happen is your darker background will be even darker with more alerts. And when dismissing each of them you will have the background brighter...
Supporting multiple images internally seems quite good but it is maybe not scalable if you later choose that some of these internal views will be completely different (one for images the other for whatever you can think of).
The best scenario I can think of is creating this overlay view controller with a container view. So this view is capable of showing view controllers and is able to animate them internally. The interface from how you then use it would look something like:
let container = MyContainerViewController.instantiateFromStoryboard()
container.injectViewControllers([
ShowImageViewController.instantiateFromStoryboard(image: image[0], delegate: self),
ShowImageViewController.instantiateFromStoryboard(image: image[1], delegate: self),
ShowSomethingElseViewController.instantiateFromStoryboard(data: data[0], delegate: self)
])
present(container...
So then I guess you would have a delegate method such as
func embeddedController(_ sender: UIViewController, shouldProceedWithData outputData: Any?) {
if container.canGoForward() {
container.forward(animated: true)
} else {
container.dismiss(...
}
}

Related

Dismiss multiple ViewControllers together when using current context as presentation style

I'm creating an application where I place a view controller ontop of another with
Presentation: current context
But when I'm trying to dismiss the screen by dragging the top towards the bottom the view does not disappear.
I've also tried to add a button to make it dismiss but that doesn't work either.
#IBAction func dis(_ sender: Any) {
navigationController?.popViewController(animated: true)
self.dismiss(animated: true)
}
So, how can I dismiss both views when dragging the top one down when it uses "current context" as presentation style?
I'm using "current context" because the previous screen should never be displayed again. And instead of dragging down both, I would like to drag down just one to make both of them disappear. But it does not seem to work as expected.
Although "current context" is not for this purpose as #matt mentioned,
You should dismiss the controller who presents this one to dismiss both together in this case:
self.presentingViewController!.dismiss(animated: true) // `!` is to makeing sure it's not crashing.
Demo:
Use this simple code to demo:
class ViewController: UIViewController {
#IBAction func present() {
let destination = storyboard!.instantiateInitialViewController()!
if presentingViewController != nil {
// You are not in the A
if presentingViewController?.presentingViewController != nil {
// You are in the C
presentingViewController?.presentingViewController?.dismiss(animated: true)
return
} else {
// You are in the B
destination.modalPresentationStyle = .currentContext
}
}
present(destination, animated: true, completion: nil)
}
}
Usage:
Create a single view application
Drag a button into the view controller
Replace the ViewController class with the provided code
Connect the #IBAction
Run it to see the result.
If you use fullscreen presentation (so that there is no drag-to-dismiss) it's quite simple to do this with essentially no code at all.
Note that the yellow view controller appears as an intermediary when presenting, but not when dismissing.

How come my IBAction isn't connecting?

So in my attached image I show my subclass I'm creating for my HOME button. I have many VCs with a HOME button and I want to connect them to this class to make them all send the user HOME
So far, I made my button to take this class, as shown in the image. My issue is I'm not able to connect my IBAction here and not sure why.... would appreciate any tips anyone can tell me about why I'm not able to connect my IBAction function to the button right now....
Another confusing thing is that although I haven't given any of the other buttons in this stackview of buttons a class, I'm actually able to connect any of my other buttons in this stackview to my IBAction... which I find odd.
class HomeButton: UIButton {
#IBAction func showHomeVC(sender: AnyObject) {
var sb: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
var vc: UINavigationController = sb.instantiateViewController(withIdentifier: "HomeNC-ID") as! UINavigationController
self.present(vc, animated: false, completion: nil)
}
}
I'm aware that I can simply go to each VC where there's a HOME button and create an IBAction from each and just paste this code in there and it will work, but I would like to do this another way where I don't have to have so much of the same code being repeated.... I would like to know what changes need to be made to make this happen.
The error that you are are getting is because you are using the class of UIButton which doesn't have the property to display a view controller. You should present it either on a UIViewController, or one of the other types.
It is linked up but you just can't present a view controller using self (UIButton)

Trigger events when ViewController covered by a presented ViewController

I would like to process code when a ViewController is no longer visible due to presenting a new ViewController.
I cannot use ViewWillDisappear etc since the controller is not technically ever dismissed from the stack - you just can't see it.
What process can I use so that code runs when the controller is no longer visible (i.e. topmost) and when it becomes visible again?
EDIT:
Seems some confusion here - not sure why.
I have a viewcontroller.
I use the following code to present another controller
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let navController = storyboard.instantiateViewControllerWithIdentifier("NavController") as! UINavigationController
let thisController = navController.viewControllers[0] as! MyController
self.presentViewController(navController, animated: true, completion: nil)
This controller does not trigger a viewWillDisappear on the previous controller since the previous view is not removed - just hidden.
I need to process code when this view is hidden (i.e. not visible) and, more importantly, process code when it becomes visible again.
When presenting a UIViewController if the presentation style has been set to UIModalPresentationOverCurrentContext it doesn't call the viewWillDisappear and related methods as the view never disappears or gets hidden.
A simple test to check if thats the case would be to set the NavController that you are using to have a clear background color. If you do this and present the NavController and you can still view the first UIViewController below your NavController content. Then you are using UIModalPresentationOverCurrentContext and that is why the viewDidDisappear isn't called.
Have a look at the answer referenced by Serghei Catraniuc (https://stackoverflow.com/a/30787112/4539192).
EDIT: This is in Swift 3, you can adjust your method accordingly if you're using an older version of Swift
If you won't be able to figure out why viewDidAppear and viewDidDisappear are not called, here's a workaround
protocol MyControllerDelegate {
func myControllerWillDismiss()
}
class MyController: UIViewController {
var delegate: MyControllerDelegate?
// your controller logic here
func dismiss() { // call this method when you want to dismiss your view controller
// inform delegate on dismiss that you're about to dismiss
delegate?.myControllerWillDismiss()
dismiss(animated: true, completion: nil)
}
}
class PresentingController: UIViewController, MyControllerDelegate {
func functionInWhichYouPresentMyController() {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let navController = storyboard.instantiateViewController(withIdentifier: "NavController") as! UINavigationController
let thisController = navController.viewControllers[0] as! MyController
thisController.delegate = self // assign self as delegate
present(navController, animated: true, completion: {
// place your code that you want executed when it disappears here
})
}
func myControllerWillDismiss() {
// this method will be called now when MyController will dismiss
// place your code that you want executed when it re-appears here
}
}
Firstly, thanks to Serghei for his time in helping work through this.
To clarify, both my potential presented controllers were set to Full Screen presentation style in the storyboard, however one was being set to Custom via a piece of pasted code dealing with the presentation. I can't find the error with the other.
However, if I force a presentation style of Full Screen as part of the presenting process then all is ok.
Hopefully my frustrating afternoon can help to save someone else's - always try to understand the implications and processes involved in pasted snippets.

Load data with Core Data in UICollectionView before animation

I am pretty new to iOS development, but I could not find an answer to this question (maybe I could not find the right keywords to search..?).
Anyway, I have this "problem":
I have 2 storyboards, A and B, both with some views inside.
I go from view A-2 to view B-1 via #IBAction - a simple button click - with the following code:
#IBAction func onExercisesButtonTapped(_ sender: AnyObject) {
let storyboard = UIStoryboard(name: "Exercises", bundle: nil)
let controller = storyboard.instantiateViewController(withIdentifier: "ExercisesVC") as UIViewController
controller.modalTransitionStyle = UIModalTransitionStyle.crossDissolve
present(controller, animated: true, completion: nil)
}
On the other hand, in the B-1 view I have a UICollectionView that is populated with data coming from a CoreData model.
The cells within the UICollectionView appear only after the animation is completed. If I set animated: false in the perform function and thus no animation is performed from A-2 view to B-1 view the cells are immediately visible.
The cells are the only element that appear after the completion of the animation. What is the cause of this behaviour? Can I avoid it with some sort of preloading/prefetching or other methods?
Is there a way to display the cells before the animation starts, just like the UICollectionView background and all the other elements within the view?
Thank you in advance :)
Update your view in viewWillAppear:.

Using viewDidAppear to present a View Controller, re-opening it when it's closed

In my App, I've created a new storyboard that serves as a very basic tutorial for how to use certain features. (Instructions.storyboard). This storyboard has it's own class - InstructionsVC.swift
I want to present InstructionsVC when MainVC loads within viewDidAppear.
It works great. Fires up on App load just like it's supposed to. The problem occurs when I press the [Close] button on the Instructions interface. It closes the VC, fades to the main screen, and then immediately fires the Instructions VC back up.
How can I prevent the Instructions VC from loading back up once it's closed?
func openInstructions() {
let storyboard = UIStoryboard(name: "Instructions", bundle: nil)
let instructionsView = storyboard.instantiateViewController(withIdentifier: "instructionsStoryboardID")
instructionsView.modalPresentationStyle = .fullScreen
instructionsView.modalTransitionStyle = .crossDissolve
self.present(instructionsView, animated: true, completion:nil)
}
override func viewDidAppear(_ animated: Bool) {
openInstructions()
}
And within my instructions class, I have the following action on the close button:
#IBAction func closeButtonPressed(_ sender: UIButton) {
let presentingViewController: UIViewController! = self.presentingViewController
presentingViewController.dismiss(animated: true, completion: nil)
}
Note - I'd rather not use UserDefaults to resolve this, because I'm going to be incorporating something similar in other parts of the App and don't want to resort to UserDefaults to achieve the desirable behavior.
Thanks in advance buddies!
viewWillAppear and viewDidAppear are called every time a view controller's content view becomes visible. That includes the first time it's rendered and when it's shown again after being covered by a modal or by another view controller being pushed on top of it in a navigation stack.
viewDidLoad is only called once when a view controller's content view has been loaded, but before it is displayed. Thus when viewDidLoad is called it may be too soon to invoke your second view controller.
You might want to add an instance variable hasBeenDisplayed to your view controller. In viewDidAppear, check hasBeenDisplayed. If it's false, display your second view controller and set hasBeenDisplayed to true.

Resources