this is my code:
let swswipe:SWRevealViewController=UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("swswipe") as! SWRevealViewController
if(indexPath.row==0)
{
let swipe=swipeView()
swipe.collectionClick="one"
self.presentViewController(swswipe, animated: true, completion: nil)
}
front
|
1st view <->swreveal<->2ndview(swipe)
i am trying to sent values from 1st to 2nd but intermediate there is a swrevealview controller is there ,so that value didn't receive
but i didn't receive anything
print("coming to collection view", self.collectionClick)
You can do this in any of following ways:
Send a notification when the second controller is going away and have the parent listen for it.
Create a delegate protocol that lets the second controller reference a parent method directly.
(If that needs to implement over the back button action)Use the parent's viewWillAppear: and check the isMovingToParentViewController property for whether it's just re-appearing.
and apply what is best for your value passing conditions and availability of values.
Related
What I want is to close a viewController after performing a segue so that the back button of the navigation controller on the new view doesn't go back to the view that I just closed, but it goes to the view that precedes it in the storyboard like it is the first time that it is loaded.
I already tried stuff like dismiss and so but it doesn't really work for me as it only closes the view in which the button that I pressed for performing the function is located :
#objc func goToList(){
self.dismiss(animated: true, completion: nil)
performSegue(withIdentifier: "goToList", sender: nil)
}
The navigation controller maintains a stack (array) of ViewControllers that have been opened (pushed). It also has the ability to pop these ViewControllers off the stack until it gets to a specific one.
For example, if you wished to return to a previous view controller of type MyInitialVC then you'd want to search through the stack until you found that type of VC, and then pop to it:
let targetVC = navigationController?.viewControllers.first(where: {$0 is MyInitialVC})
if let targetVC = targetVC {
navigationController?.popToViewController(targetVC, animated: true)
}
NB. written from memory without XCode, so you may need to correct minor typos
You can use unwind segue to get back to each viewController that you want.
Read more here:
Unwind Segues Step-by-Step (and 4 Reasons to Use Them)
I'm building an app and I've decided to build my user interfaces in multiple Storyboard files. Each Storyboard is a group of similar actions. For example, LogInStoryboard contains views for logging in and for registering a user.
I've run into a bit of problem with navigating to a new Storyboard and dismissing all the previous view controllers to clean up after myself.
This is my dataflow:
1) App launches into a log in view. Root view controller is LogInViewController, which lives in LogInStoryboard.
2) User taps the register button to summon the modal RegistrationViewController, also lives in LogInStoryboard.
3) User completes registration and is automatically signed in.
At this point, the view controller stack is [LogInViewController, RegistrationViewController]. After registering, the user is automatically signed in so I want to navigate to their home screen, HomeViewController. However, HomeViewController lives inside another Storyboard HomeStoryboard.
I want to dismiss both LogInViewController and RegistrationViewController, and then I want to instantiate HomeViewController from HomeStoryboard and present it. This way I have a simple view controller stack and the LogInStoryboard view controllers can all be deallocated.
What would be the best way to achieve this sort of flow? Or should I even be so worried about those old view controllers?
Its very often the case that your login flow acts almost as an entirely separate app who's only function is to authenticate the user and write the user info to your database. In this case when you are done with the login instead of performing a segue what you want to do is replace the entire view hierarchy with HomeViewController. To do this you can simply call UIApplication.shared.keyWindow! = UIStoryboard(name: "HomeStoryboard", bundle: nil).instantiateInitialViewController() (assuming you made HomeViewController the initial view controller). Of course this has no animation and is kind of ugly so you way want to use UIView.transition(with:duration:options:animations:completion:) to animate the change instead. Example:
UIView.transition(with: UIApplication.shared.keyWindow!, duration: 0.25, options: .transitionCrossDissolve,
animations: {
UIApplication.shared.keyWindow!.rootViewController = UIStoryboard(name: "HomeStoryboard", bundle: nil).instantiateInitialViewController()
})
Swift 4.0
I would start with the HomeViewController as the Initial View Controller.
And from there create the LogInViewController.
if shouldLogin {
let storyboard = UIStoryboard(name: "LogInStoryboard", bundle: nil)
let loginVCtrl = storyboard.instantiateViewController(withIdentifier: "yourLogInViewControllerID")
present(loginVCtrl, animated: true)
}
And after your user finished the Login/Registering process you can call:
dismiss(animated: true)
So your ViewController stack looks like:
[HomeViewController]
[HomeViewController, LogInViewController]
[HomeViewController]
set your HomeViewController in appdelegate as navigationcontroller and set navigationcontroller as rootviewcontroller
NOTE: Both actions perform in appdelegate
Does anybody know if there is a certain pattern for handling segues programmatically in a MVC way?
I would think the best way would be to work with an event system within a controller.
I want that all the view controllers connect to this navigationController instead of handling all the logic within the viewController logic itself. I want to out source this logic
In most of your view controllers, you will have access to a prepareForSegue function, with one parameter called sender.
If you kick off a segue programatically with performSegue(withIdentifier: "mySegueID", sender: yourVC) then this function will be called, and you'll be able to pass information from the sender to the new view controller.
In this function, to get a handle on the next VC, use segue.destinationViewController.
I don't know about a particular pattern but a simple way to programmatically handle transitions between 2 UIViewController could be to have a separated manager whose job is just to push/present/whatever new controllers over current, and to pop/dismiss/whatever current controllers to old ones.
The way I usually do this is by having a class we can name WorkflowManager, which will handle all transitions. Associated with this manager, you declare a WorkflowManagerComponent protocol and implement it :
protocol WorkflowManagerComponent {
var completionHandler: (hasCompleted:Bool,data:Any)->() {get set}
}
Make each UIViewController implement this, for example by calling completionHandler(true,someData) when the user taps a "next" button, or completionHandler(false,nil) when the user taps a "back" button.
Then in your workflow manager, you perform transitions to the next or previous UIViewController according to parameters sent in the completionHandler:
//init viewController1 ...
viewController1.completionHandler = onViewController1Completes
// ...
func onViewController1Completes(_ completed: Bool, data: Any) {
if hasCompleted {
//init viewController2 ...
viewController2.data = data
viewController2.completionHandler = onViewController2Completes
//Push the new vc
viewController1.navigationController.push(viewController2, animated: true)
} else {
//The vc1 was presented as a modal, dismiss it
viewController1.dismiss()
}
}
This way each UIViewController is separated from others, free off any transition logic.
The issue I'm having is this.
I have a navigation controller with 3 viewController. In the 1st controller, I have the user select an image. This image is passed to 2nd and 3rd controller via prepareForSegue.
At the 3rd controller, I have a button that takes the user back to the 1st view controller. I explored 2 ways in doing this:
1) use performSegue, but I don't like this because it just push the 1st controller to my navigation stack. So I have this weird "Back" button at the 1st Viewcontroller now, which is not what I want. I want the app to take user directly to 1st viewcontroller without the back button.
2) I tried Poptorootviewcontroller. This solves the issue of the "back" button. But, when I pop back to the 1st viewcontroller, the user's selected image is still on screen. I want to clear this image when the user goes from the 3rd viewcontroller back to the 1st viewcontroller.
So with approach 2), how do I make sure all memory is refreshed and the image becomes nil in the 1st viewcontroller? Since I'm not using performSegue, 3rd viewcontroller does not have access to the 1st Viewcontroller.
For refresh, you'd have to clear it in viewWillAppear but I find this rather dangerous. Best you can do there is to create a new copy of the view controller everytime and Swift will take care of the rest. I don't know if you are using the storyboard but I would recommend using the class UIStoryboard and the function instiantiateViewControllerWithIdentifier("something") as! YourCustomVC
As long as you stay in the navigation stack, you'll not lose any of the current configurations of previous View Controllers.
As for passing data back to the first controller. You can either just throw it in the global scope which is the easiest way but might be difficult to know when it was updated or if the data is fresh. But you can always just:
var something: String = ""
class someView: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
something = "foo"
}
}
Something will be availabe everywhere then.
You could make a protocol and pass the delegate along the 3 view controllers. So when you are starting it you could do:
func someAction() {
let v = SomeViewController()
v.delegate = self
self.navigationController?.pushViewController(v, animated: true)
}
And then with each following view:
func someOtherAction() {
let v = SomeOtherViewController()
v.delegate = self.delegate
self.navigationController?.pushViewController(v, animated: true)
}
Although personally I find it hard to keep track of this.
Lastly you could use the NSNotificationCenter to pass an object along with all the data and catch it in a function on your first controller.
To do this you first register your VC for the action in viewDidLoad() or something:
NSNotificationCenter.defaultCenter().addObserver(self, selector: "someAction:", name: "someNotification", object: nil)
Then when you are done in the 3rd view make some object or a collection of string and send it back as follows:
NSNotificationCenter.defaultCenter().postNotificationName("someNotification", object: CustomObject())
And then lastly you'll catch it in the function "someAction"
func someAction(note: NSNotification) {
if let object = note.object as? CustomObject {
//Do something with it
}
}
Hope this helps!
Use an unwind segue which provides the functionality to unwind from the 3rd to the 1st (root) view controller.
The unwind segue is tied to an action in the root view controller. Within this action, you simply nil the image:
#IBAction func unwindToRootViewController(sender: UIStoryboardSegue)
{
let sourceViewController = sender.sourceViewController
// Pull any data from the view controller which initiated the unwind segue.
// Nil the selected image
myImageView.image = nil
}
As you can see in the action, segues also let you pass data back from the source view controller. This is a much simpler approach than needing to resort to using delegates, notifications, or global variables.
It also helps keep things encapsulated, as the third view controller should never need to know specifics about a parent view controller, or try to nil any image that belongs to another view controller.
In general, you pass details to a controller, which then acts on it itself, instead of trying to manipulate another controller's internals.
I'm new to swift and am trying to write an app with it.
I have a UIViewController that I am transitioning to. I have designed the UI in interface builder and I intend to use segues to manage the transition. However, the view controller relies on data that is passed into the view controller from the previous view controller.
If I have properties on my view controller then I will need to redefine my init method. But I wouldn't normally call the init method; it would be called for me before prepareForSegue. So I see a few possible solutions:
Make my variables optional (so I can pass them in prepareForSegue
and update the view then).
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let viewController: EventViewController = segue.destinationViewController as! EventViewController
viewController.event = self.event
}
Manually init my view controller and present it programmatically instead.
???
Is there a third option? If not, which of the previously mentioned 2 is better practice?
There is two possible options as you mentioned:
The first one is the easiest which is to pass the data in prepareForSegue. which you don't have to care about dismissing the controller or keeping a track of inner view controllers,because storyboard will take care of it.
The second way is to set a Storyboard ID in storyboard,for the controller you need to present programmatically, which need more things to handle, like to dismiss the controller or keep track of inner presented controllers.
let nextViewControllerName = storyboard?.instantiateViewControllerWithIdentifier("Storyboard ID") as! nextViewControllerName
nextViewControllerName.event = self.event
self.presentViewController(nextViewControllerName, animated: true, completion: nil).
At the end they does the same purpose.
Note: You should always pass the data before presenting the controller.