This is a kinda conceptual question (not trying to solve a particular problem but to understand things better) that I encountered.
IBOutlets in view controllers are "forcedly unwrapped" variables by default and I don't know exactly why but it tells me that iOS expects each one of them to be initialized when the VC is called.
I have a view controller A and two other ones B and C. I have a click listener to a custom button of mine in A that depending a certain variable, decides after the click if B or C are going to be called.
I tried implementing this with presentViewController but for some reason my program crashed in runtime because of non-initialized outlets in either B or C.
However, I named by segues and tried the same thing with performSegueWithIdentifier and it just worked.
I wanna know, what are all the differences between them and is everything I told I know correct?
Thanks in advance
Related
I still don't have an answer after viewing many answers to a similar question eg. https://stackoverflow.com/questions/12509422/how-to-perform-unwind-segue-programmatically
I would like to try to restate what "programmatically" means to the question I'm posing.
All the solutions show using a storyboard and connecting the view controllers "exit" with an unwind method defined in some view controller. This works but isn't programmatically, but rather directly connecting through use by the Xcode tool. Sure the prepare method is programmatically involved in the final steps.
My challenge is that I'm using Cocoapods as a reusable library, and the storyboard has no way to know about users of this library. So they cannot connect to an unwind method, as there isn't one yet.
I want to use the language generic capabilities to specify the unwind method, programmatically (Swift or Objective-C).
The unwind method might also be in another bundle, further complicating things.
Note: Creating a placeholder unwind would be ok, assuming the value of the view controller name can be changed programmatically through the UIStoryboardSegue object (which has an unwindAction).
Thanks for any insight.
UPDATE ANSWER (it won't let me answer)
First, my answer involves clarifying the original question, in particular the "without storboard" statement. This was meant to "unwind" to a ViewController that wasn't specified via the storyboard, in particular a new user of this library. My answer shows I can do this, but a storyboard is still required (just not for this new ViewController).
My question did state that a placeholder unwind could be created via a storyboard.
Note: Creating a placeholder unwind would be ok, assuming the value of the view controller name can be changed programmatically through the UIStoryboardSegue object (which has an unwindAction).
So my answer can call code that wasn't specified via the Xcode user interface (which is the UI process of connecting to a named unwind though dragging to the 'exit').
I was under the (wrong) assumption this was connecting to my unwind method (eg. unwindFromHelp), much like connecting a button to an IBAction in code. Instead it connects dynamically to anything matching that name in the runtime hierarchy.
The key insight to the answer is from What are Unwind segues for and how do you use them? where #shawkinaw states:
In other words, think of the unwind action as describing where the segue is coming from, rather than where it is going to.
This also means there can be many implementations of the same unwind method name! At runtime, the ViewController hierarchy keeps track of the unwind methods it's encountered along the way, and calling the Segue Unwind will unwind to the first one it finds.
Based on that new understanding, my quest to call an unwind method in some future ViewController is possible by creating a placeholder unwind that is manually added through the Xcode exit approach. That operation shows all the unwind methods available at that time.
So the solution involves using the storyboard, and a (potentially) unused ViewController, where an unwind method is defined. Then the exit of the desired ViewController connects through Xcode to that unwind.
A future user can also define this same unwind method in new code (eg. unwindFromHelp) and if the ViewHierarchy runtime stack is such that no other unwind is matched, their code is executed. Thus this delegation approach achieves my original question, where the future user must provide an implementation for the delegation (which here is an unwind method name, eg. unwindFromHelp).
I wrote a sample that shows how this can be accomplished and the unwind returns to different ViewControllers based on how it got there: github UnwindExample
An example flow diagram is based on the
A or B calling Help idea.
The app I am currently working on requires that I do not use ANY storyboards. Therefore I need to do everything programmatically. One thing I seem to be struggling with is switching between two UIViewControllers.
The issue is that every time I call the self.present() method; it creates a brand new instance of the class I would like to show. So when I go into Xcode's visual debugger, I see over 15 different views that are all stacked and are merely instantiations of one another. Ex: View1, View2, View1, View2, View1, View2, View1... This constant repetition of the views is significantly hurting the performance of my app. So my question:
Is there a way that I can switch between my two UIViewControllers without constantly creating a new instance of each one?
Again, I am doing all of this with ALL storyboards DELETED. So the solution I necessary needs to be implemented using ONLY code.
I think you should create main ViewController.
if you keep your two instance view controller, create two controller in mainViewController. Then keep that in main viewController.
And push view controller you want to present in navigation of main view controller
if you want to change second view controller pop navigation controller and push another, or just push other controller.
if you want keep your instance view controller i think it's best option for you
I think that it is really important to realize the fact that the view controller will show up multiple times in the debugger because that is something that can throw off the performance of the app. I really like the way you mention that in your comment. This is one of the main differences that can outstand you from another programmer in the same field. One of the most easiest and simple ways to fix this problem would be using the self.dismiss() method. Another way to dismiss this view controller would be to use a navigation controller to fix this problem. A navigation controller will push the main view controller out of the way and it will not create multiple instances of it. This will be the most efficient as it doesn't require a lot of code and a mere initialization of the UINavigationController class implemented in the UIKit. This is one of the most important tools and resources that you must make use of while coding in xcode and developing your skills in the swift ios field. Since this problem is not one of the most common to find on the internet, it is very beneficial for you to post it on this forum page and will really be helpful for some other programmers unaware of such methods and ways to code. One of the questions that I have for you is the fact that you don't want to use storyboards. Why don't you want to use storyboards and only make it proGrAMitcally? This is one of the very questions that manages to astound me. The storyboard is an implementation that makes it very easy for xcode and swift users to work around the tedious work that has to be done while working in the coding aspect. It only takes a few lines of code and you can get a seGu done very easily. The switch is very easily done and you can find this method on some youtube channels. For this type of work, I recommend VigneshSriniswami Patel and ShaniLakshmiVishnuJiSwami, these content creators will help guide you to becoming an xcode master.
Hope I helped!
I am working on a Udacity course for building iOS apps. I have come across this problem in the connection/segue between the two screens of the course app and can't seem to find answers on the Udacity forum. I've gone back and checked my steps so really don't know why its happening. It is shown in the attached screen capture.
The problem is baffling and I'd like to get through this before proceeding with the rest of the course.
when creating the segue you connected it from the button to the destination view controller, so it will crash because the sender is a UIButton. To fix this issue, first remove your segue. Then create a new one from the view controller to the destination view controller (with the same name you had previously).
I have a series of three view controllers, the first two of which are wrapped in a UINavigationController, and are managed by the default Main.storyboard file. Let's call them A and B, respectively. I have more view controllers throughout the file, each instantiated and managed separately, though I am working on moving them into all-code solutions. The aforementioned third view controller I have already in code (C) is one that at various times is instantiated and presented by B.
A and B have a normal segue relationship setup in Interface Builder and called via code. I do the same with C from B, via code (instantiate and presentViewController(_:)). There are a few cases where an action in C invalidates B's raison d'ĂȘtre, and thus I must dismiss both.
So far, I have been calling dismissViewControllerAnimated(_:) from C, and then checking in B's viewDidAppear(_:) whether it should be dismissed, and dismissing it the same way if so. During this process, the user is thrown back though the VC hierarchy, watching as empty views fly back to whence they came, leaving time for them to experiment with controls that no longer work, and may crash the app. This is a rather disconcerting user experience that I wish to do away with. I could simply disable everything, but that's a lot of controls that I'd rather not mess with if I can avoid it...
I understand that IB supports "unwind segues," which can dismiss an entire VC hierarchy simultaneously, though those only seem to handle view controllers in the storyboard. Is there any way to get this behavior (dismiss everything to A) programmatically, without having to revert much of the work I've already done, considering that part of my hierarchy is contained in a UINavigationController?
UPDATE:
So, I got the dismissal to work properly, by passing a reference to the presenting view, and calling its dismiss segue before leaving. That approach came with its own issues and navigation controller weirdness, but with some tweaking it might be usable.
Honestly, it would be much easier to remove the feature entirely at this point, as it's mostly a convenience.
For the sake of science, I'm going to keep at it until I decide ether way, and answer back here for anyone googling this way.
UPDATE:
Ew... It seems this code was older than I thought. I actually have two navigation controllers, to support a custom modal animation into and out of B, with a custom unwind segue there (there you go). In order to get the animation I want, I may as well toss C into the storyboard and make a custom unwind segue.
If I don't care about animation, simply disabling animation on the custom unwind got both B and C to vanish promptly, and together. Trouble is, it's a bit jolting for my taste...
vacawama's suggestion actually makes a lot of sense, and serves me right for not checking the documentation! UIViewController already keeps a reference to its presentingViewController. Going back by two is a pinch, simply climbing back that hierarchy and dismissing the one I want. Works like a charm, without animation doesn't happen at all...
Gonna post an answer here soon with what I've found thus far.
So, science prevails, and a solution is found.
It's really a pain to do if you don't like writing custom segues and animators. Gonna do that eventually, but for now it's more profitable to just not enable the feature at all. (Thank goodness it's easy to toggle on my end).
I found that running my dismiss segue (I use a custom one) on B, then dismissing C did the trick, so long as I didn't use animations for either. Nothing unexpected there at all!
Further, I could get the same effect in one line (animation doesn't matter, so neither does custom segue) by running:
presentingViewController?.presentingViewController?.dismissViewControllerAnimated(true, completion: nil)
from C's equivalent of goAllTheWayBack().
Gross, but it got the job done. A bit more tweaking, like actually using the storyboard (ugh) for its unwind segue and writing a custom animator, it'd look fancier than a pig in a blanket!
I'd about declare this horse good and dead. Thanks all!
Have you tried Atomic Object's approach
TL;DR
use IB and ctrl-drag 'C' view controller (yellow) to 'Exit',
select 'prepareForUnwind',
give the unwind segue an identifier,
then you can perform the unwind programmatically and it will skip 'B' view controller.
I'm trying to make a custom view controller for use in my projects, that has possible 3 segues from it. But those are not mandatory.
When view controller loads i'm calling a method "trySegues()" that should try to perform all 3 of those segues. Segues are custom, so each successful call will be handled and registered in my code.
In Objective-C i would do that using "#try - #catch" so all the successful calls will go forward, while if one of those segues is not set, it'll raise an exception, but it will be handled by me manually so it won't break the execution of the program.
Is there a way to do the same in Swift?
Using Optionals to simulate an exception won't work, since the performSegueWithIdentifier function returns no value.
In no imaginable way is #try-#catch-ing a performSegueWithIdentifier: the appropriate approach to this challenge. Not in Objective-C. Not in Swift.
Segues can't (or shouldn't) exist independently of storyboards. After all, if you were try to instantiate a segue, the class name is UIStoryboardSegue. The fact that this class even exists publicly is not so that you can instantiate it and add it at run time. It exists so that you can subclass the segue, choose your subclass on the storyboard, and set up a segue with custom behavior:
In Objective-C and Swift, exceptions are reserved for truly exceptional behavior. Behavior which can be prevented at development time. We should not be relying on #try-#catch blocks during run-time by the time we've released our app. We should have fixed our app so that there's nothing to #catch.
So with this said, the only way that performSegueWithIdentifier: can throw an exception is if the segue identifier we gave it does not exist. The only reason the segue shouldn't exist is because we made a typo somewhere.
The segues are hooked up and defined at compile time. There is no public mechanism for checking whether or not a particular segue exists because we just shouldn't be doing this at all.
Now, with all that said, if we want to dynamically push or modally present a particular view controller at run time, and we can't know at compile time what view controller it will be (or from what view controller we're getting to it from), then we shouldn't be presenting it via a segue.
Instead, we should be using one of the presentation options the UIViewController class defines.
Your options are:
presentViewController(_:animated:completion:)
showViewController(_:sender:)
showDetailViewController(_:sender:)
And in place of an unwind segue, you'd want something like this:
dismissViewControllerAnimated(_:completion:)