I am trying to navigate to a UIViewController using Swift 2.3. To be more precise, I am trying to reload the UIViewController that is currently active. I do not know which view the user currently has active, so this must be defined dynamically.
I have tried several approaches, but they all result in either compile or runtime errors.
Is something like this possible?
let activeViewIdentifier = ??? // Get currently active view identifier as a string
self.performSegueWithIdentifier(activeViewIdentifier, sender:self)
You can get like this :
Objective-C :
self.navigationController.topViewController.restorationIdentifier
Swift :
self.navigationController?.topViewController?.restorationIdentifier
I think you have some issues with your architecture; it's not the best approach to reload just everything on some View Controller you can chose;
Much better way of thinking is to determine, what exactly you want to reload and add methods to reload only thus things
Anyway, if my answer hasn't assure you, consider replacing existing view controller with new and presenting it with some animation, or without it; so your general algorithm may look like this:
Get new VC from storyboard, or creating new instance, if you don't prefer to use it
Push it over your existing controller
Reload stack of navigation controller, in which you are now
you can try this
let activeViewIdentifier = self.navigationController?.childViewControllers[(self.navigationController?.childViewControllers.count)!-1]
You can use the restorationIdentifier, it's right above the Storyboard identifier and it's a UIViewController property.
let activityIdentifierStr = activeViewIdentifier?.restorationIdentifier
self.performSegueWithIdentifier(activityIdentifierStr!, sender:self)
Related
I'm developing an iOS app which is just for debugging some other work I'm involved in. I've ran out of space on the first view controller (lots of buttons and images) and want to expand into a second view controller. I'm implemented a segue between the views and use the following code in the second viewcontroller to trigger functions in the first view controller:
#IBAction func imagePreset_1_clicked(_ sender: Any) {
firstViewController().functionX()
}
This works but if functionX has the following code:
DispatchQueue.main.async { () -> Void in
self.source.text = "hello"
}
which is a label on firstViewController, then I get the following error:
fatal error: unexpectedly found nil while unwrapping an Optional value
I'm new to Swift. Am I correctly creating a reference to the firstViewController or am I creating a new instance of it? It the problem that the label being updated is not in "scope"?
All I want is extra space for icons. Is there a better way to achieve this?
Thanks
update
So I've tried a scrolling view and this works but can anyone advise how to do what I'm trying to achieve with a second view controller accessing the functions in the first view controller? I think that's a nicer ux experience. Is it any easier using a tab bar?
Have you considered using a single ViewController with scrollable view inside ?
Using this, you'll be able to display as many content as you'd like :)
I'm trying to learn how delegates work and wrap my head around the concept. I'm finding I get some of the ideas. I understand you use it to pass data from one view controller to another, however wouldn't it work the same if I just sent data from a segue and every time the 1st view controller would appear, it would use that data?
So for example I have 2 view controllers.
1 is homeViewController and
2 is editViewController.
I have a variable titled "addressOfHome" which is a string in homeViewController(1).
In homeViewController under the method "viewDidAppear"
I also set the addressLabel = addressOfHome.
Then I just pass the data from editViewController(2) to homeViewController(1)
when the segue's destination vc is homeViewController ?
I'm terrible at explaining things so I apologize for that, but I gave it my best shot. Thanks for your time!
Delegates are mainly used to "trigger action" on an object from another one.
An object delegates a way to handle something to someone else, for example when you click on an UIAlertView button, if its delegate is set on a viewController, alertView:clickedButtonAtIndex will be executed on the VC, which can so react as it want
I'm terrible at explaining things
Haha, yes, you are !
A delegate isn't for that - a delegate is a way to over-ride the default behaviour of some feature(s) of a class, without creating your own sub-class. See this post : How does a delegate work in objective-C?
Are you trying to understand how delegates work (in which case, I don't think your example is one that requires a delegate) or are you trying to implement the functionality you describe, and think that a delegate is the way to do it? (I think you actually want a data source).
I have created a custom class for my UIBarButtonItem (refreshIndicator.m). This button will be on many different view controllers, all push-segued from my MainViewController/NavigationController.
Instead of dragging an outlet onto every single ViewController.m file for iPhone storyboard THEN iPad storyboard (ugh, still targeting iOS7), I want to know if there is a way to complete my task simply within my UIBarButtonItem custom class. I've looked around everywhere but I haven't quite found an answer to this,
All I need to do is check which UIViewController is present, check the last time the page was refreshed, and then based on that time, set an image for the UIBarButtonItem. (I've got this part figured out though, unless someone has a better suggestion). How can I check for the current UIViewController within a custom button class? Is this possible?
Does it need to know which view controller its on so it can tell that vc it was pressed? If that's the case, then use your button's inherited target and action properties. On every vc that contains an instance of the button, in view did load:
self.myRefreshIndicator.target = self;
self.myRefreshIndicator.action = #selector(myRefreshIndicatorTapped:);
- (void)myRefreshIndicatorTapped:(id)sender {
// do whatever
}
More generally, its better to have knowledge about the model flow to the views from the vc, and knowledge of user actions flow from the views. Under that principal, your custom button could have a method like:
- (void)timeIntervalSinceLastRefresh:(NSTimeInterval)seconds {
// change how I look based on how many seconds are passed
}
And your vcs:
NSTimeInterval interval = [[NSDate date] timeIntervalSinceDate:self.lastRefreshDate];
[self.myRefreshIndicator timeIntervalSinceLastRefresh:interval];
If you really must go from a subview to a view controller, you could follow the responder chain as suggested in a few of the answers here (but I would go to great lengths to avoid this sort of thing).
It is possible to achieve this, but the solution is everything but elegant. It is one way of getting around the basic principles of iOS and is strongly discouraged.
One of the ways is to walk through the responder chain, posted by Phil M.
Another way is to look through all subviews of view controllers until you find the button.
Both ways are considered a bad practice and should be avoided.
For your particular case, I would rethink the structure of having a separate instance of the bar button. For example, you could rework it into a single UIButton instance that gets displayed over every view controller and it can also act as a singleton.
I'm trying to tell my modal views parent view to update its data before the user returns to the screen, but I'm having some difficulties getting the message through.
I've tried all variants of this statement:
let parent:Oversikt = self.presentingViewController as Oversikt
parent.getShifts()
//OR
let parent:Oversikt = self.navigationController.parentViewController as Oversikt
parent.getShifts()
but I keep getting EXC_BAD_ACCESS in the libswiftCore.dylib swift_dynamicCastClassUnconditional:
Can anyone help me? I apologise if this is a stupid question, I'm like most of you very new to swift. Here's an image of my storyboard. The rightmost one wants to communicate with the second leftmost one.
If this was objective-c I would recommend using delegation, but in this case it may be much easier to use NSNotificationCenter. If all you want to do is send a simple message to another view controller, that is one quite simple method.
Can anyone tell me how I can phrase an if () statement to discover if a segue's destination view controller will appear in the Detail Split or in the Master Split?
I want to put the if() statement inside my prepareForSegue:sender: methods.
EDIT
All my detail views that are relevant to this question (at the moment) conform to a protocol and I am currently performing introspection on the destination controller using:
if ([segue.destinationViewController conformsToProtocol:#protocol(myProtocol)])...
I can see that this would not work if I wanted:
To be able to show the same class in either Master or Detail of the splitView from time to time, and at the same time...
I only want the if() statement to be true when the view is to be presented in the detail split.
Things like segue.destinationViewController.navigationController == ... don't appear to be any use either.
I was hoping that since we need to set "Master Split" or "Detail Split" when we set the segue up... there would be a way to access that information less circuitously.
SECOND EDIT:
The way I have this set up with using introspection does "work". It just doesn't seem very "Object Oriented". I don't think I should be querying the View Controller at all for this information, I can't see why the VC should know anything about which side of the splitView it will be displayed. Surely the object that should hold onto this information is the Segue and, as I say, it appears this is being "set" in the storyboard when we select "Detail" or "Master" split.
Maybe it isn't a property of anything, but I can't see how to get at it.
I suppose I could query the destinationViewController in its viewWillAppear to discover which NavigationController it is in after it appears on screen but, again, it seems a bit "hacky".
There is probably a better more abstract and reusable way to do this that I'm not aware of, but here is a suggestion that could help in your specific project that requires just a bit of special knowledge of your specific project.
If you use introspection in your prepare for segue, you can check to see if methods exist by using the responds to approach.
So for example, in typical implementations of a splitview controller (note - not all) the detail view will implement the methods to handle rotation. So if this is true in your project, you could do something like this:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.destinationViewController respondsToSelector:#selector(splitViewController:shouldHideViewController:inOrientation:)]) {
//do something
}
}
You could use this same approach based upon something that was unique but constant in your project related to either the master or detail view.
hope that helps,
be well
My experience is a little limited, but most times I've seen prepareForSegue used, the if() block checks segue.identifier to do anything that needs to be done specifically to handle building the new page. If you set the identifier for all your segues, you could just have code to handle each segue from that controller, and change what the code is depending on if that segue goes to a masterViewController or a detailViewController. Not really a well automated way, but it'll get the job done.
EDIT: oh geez, that wording is kinda confusing. If you want me to I can put a code example up, but it'll have to wait until Monday, as I don't have access to a Mac until then.
The talk of classes and protocols gave me another idea, but again, not sure if it will work - I wanted to test it before posting, but I'm not going to have the time to test anytime soon.
I think you should be able to create 2 new classes, UIMasterViewController and UIDetailViewController, that are subclasses of just UIViewController. Then, for each of your actual screens, instead of making them subclasses of UIViewController directly, make them either a UIDetailViewController or UIMasterViewController. Then, in your prepareForSegue,
if ([segue.destinationViewController isKindOfClass:UIMasterViewController])
{
//do master view specific stuff
}
else if ([segue.destinationViewController isKindOfClass:UIDetailViewController])
{
//do detail view stuff here
}
This should be a pretty dependable way to tell where your segue is sending you, as long as you can set up the custom view controller classes right. This still won't solve the first issue noted in the question
"To be able to show the same class in either Master or Detail of the
splitView from time to time, and at the same time..."
This could be overcome by making 2 copies of all of the views you want to be able to show as either or both, then make one a UIMasterViewController and the other a UIDetailViewController - copy-paste should be good for most of the rest.
Let me know if this works - I'm not exactly sure how to set up the controllers off the top of my head, but I'm pretty sure it could be done. If it can, I can see this being a very useful thing.