I have n number of different ViewController's, so I want to present a PresentViewController from any other ViewControllers in Objective-C
How I am doing Currently:
So currently i am doing like this import "PresentViewController.h" in all ViewControllers then calling following code to present it:
NSString * storyboardName = #"MainStoryboard";
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:storyboardName bundle: nil];
PresentViewController * vc = [storyboard instantiateViewControllerWithIdentifier:#"IDENTIFIER_OF_YOUR_VIEWCONTROLLER"];
[self presentViewController:vc animated:YES completion:nil];
But by this way I have to import "PresentViewController.h" file into all other ViewControllers which seems very irritating job.
What I am looking for:
Is there any other way to present PresentViewController from a particular place and it can be presented whenever it needed from any ViewControllers?
If that is really all the code you've got, you are not doing anything with PresentViewController unique to it, so just don't mention it by name in your code:
UIViewController * vc = [storyboard instantiateViewControllerWithIdentifier:#"IDENTIFIER_OF_YOUR_VIEWCONTROLLER"];
[self presentViewController:vc animated:YES completion:nil];
If you don't mention it in your code, you don't need to import it.
But obviously if you do something with PresentViewController, like call a method of it, then things are more complicated. You could cast to id, in which case you can call any method, provided the compiler knows about it (also known as an informal protocol). But in general it would be better just to bite the bullet and do the import.
The problem isn’t that you have to import that header all over the place, but rather that:
you’re repeating that code all over the place;
you’re exposing internal implementation details about PresentViewController (such as its storyboard name and storyboard identifier) to other classes; and
if you ever want to change this to include some additional parameter, you’re going to have to manually hunt through your code for where changes are necessary rather than just changing some public interface that the compiler will then be able to warn you of issues.
Personally, I’d define an extension to UIViewController:
// UIViewController+Foo.h
#interface UIViewController(Foo)
- (void)presentFoo;
- (void)presentFooWithCompletion:(void (^)(void))completion;
#end
And
// UIViewController+Foo.m
#implementation UIViewController(Foo)
- (void)presentFoo {
[self presentFooWithCompletion:nil];
}
- (void)presentFooWithCompletion:(void (^)(void))completion {
NSString *storyboardName = #"MainStoryboard";
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:storyboardName bundle: nil];
UIViewController *vc = [storyboard instantiateViewControllerWithIdentifier:#"IDENTIFIER_OF_YOUR_VIEWCONTROLLER"];
[self presentViewController:vc animated:YES completion:completion];
}
#end
Then any view controller that needs to present this scene can have
#import “UIViewController+Foo.h”
And
[self presentFoo];
Or if you don’t want it as an extension to UIViewController, you could define these as class methods (using the + instead of -) of your PresentViewController. But the idea is the same: Define some simple interface that all your view controllers can call without getting lost in the implementation details of PresentViewController.
Now, obviously, I have no idea what this view controller does, so I used Foo as a placeholder. You’d obviously replace that with whatever the functional purpose of this view controller is. E.g. if it was to present a “network error” message, then I’d call the methods and filenames with presentNetworkError, UIViewController+NetworkError.h, etc. But hopefully this illustrates the idea.
Related
I'm currently using MRProgress in my project. Before time, I've put these two libraries in every files of my project. At that time, MRProgress working well and show correctly display in this project. Now I'm creating common class and put all of functions into that class to make reusable and optimize my project like that.
common.m
- (void) myFuntion:(UIViewController *)myvc {
[MRProgressOverlayView showOverlayAddedTo:myvc.navigationController.view animated:YES];
..
..
..
..
[MRProgressOverlayView dismissOverlayForView:myvc.navigationController.view animated:YES];
}
myusage.m
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"stb" bundle:nil];
ViewController *searchVC = [storyboard instantiateViewControllerWithIdentifier:#"ViewController"];
[common myFuntion:searchVC];
At that time, my problem is MRProgress cannot display anymore. Please let me know what I did wrong.
First, verify if ViewController is embedded into a navigation controller
You can put a breakpoint and then 'po searchVC.navigationController' in the console
Btw, I don't know if you did it wrong or not, but it doesn't make sense to add MRProgress on the navigationController and then dismiss it with 'self.view'. You should dismiss the same view you added the overlay, in your example it should be : myvc.navigationController.view
From above shared code it appears like you have declared instance level method:
- (void) myFuntion:(UIViewController *)myvc
And you are calling it like class level method, try replacing above prototype with following
+ (void) myFuntion:(UIViewController *)mivc
Actually methods that are declared with "-" are treated as instance level and functions with "+" are class level.
Reference: http://rypress.com/tutorials/objective-c/classes
Use this method after pushing view like :
SceondViewController *controller = [self.storyboard instantiateViewControllerWithIdentifier:#"SceondViewController"];
[self.navigationController pushViewController:controller animated:YES];
[self myFuntion:controller];
Hope it will useful for you.
I need to launch different view controllers from an action sheet.
Is it best practice to create the view controllers in storyboard and then using the identifier, launch them from the appropriate point in the action sheet?
I am trying to get following to work without success. It is saying I need to define window property...
-(void)launchVC {
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
UIViewController *ivc = [storyboard instantiateViewControllerWithIdentifier:#"reportProblem"];
[(UINavigationController*)self.window.rootViewController presentViewController:ivc animated:NO completion:nil];
NSLog(#"should have launched by now");
}
Alternatively, is it better to create the whole view controller in code?
FYI, I am leaning toward storyboard as this view controller has its own class and I know how to set class in storyboard but not in code but cannot get it to work.
Thanks in advance for any suggestions.
Where have you implemented launchVC method ?
If you have implemented launchVC method in your AppDelegate, then it should work.
Otherwise If you have implemented it in your ViewController Class then
[self presentViewController:ivc animated:NO completion:nil];
I tried many answers here but none of them had the same exact scenario.
I'm trying to navigate to a UIViewController that's within a separated storyboard in a different bundle, so far I was able to navigate to it but am unable to return to the previous UIViewController. The method that invokes the external view controller (TabBarController) is implemented as follows:
+(void) launchExternalUI: (UIViewController *) previousViewController {
UIStoryboard* sb = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle: [self frameworkBundle]];
TabBarController *vc = (TabBarController*) [sb instantiateInitialViewController];
[previousViewController.navigationController pushViewController:vc animated:YES];
[vc release];
}
Now the method within TabBarController that should return to the previous view controller:
- (void) navigateToPreviousViewController: (UIGestureRecognizer *)gesture {
[self.navigationController popViewControllerAnimated:YES];
}
In TabBarController, if I print all the viewcontrollers within self.navigationController, all I see is the TabBarController, shouldn't I see the previous view controller that pushed this on launchExternalUI ? The [self.navigationController popViewControllerAnimated:YES]; has no effect at all. I'm a bit lost on this.
It's also important to notice that previousViewController is defined in a local storyboard and TabBarController is implemented in a different .framework, would that cause the issue?
Thanks in advance for all the help!
**Edit: The navigation flow I need is storyboard1:VC1->storyboard2:VC2->storyboard1:VC1, I can get storyboard1:VC1->storyboard2:VC2 part to work but not storyboard2:VC2->storyboard1:VC1
I often split projects up into various Storyboards, and have created a dynamic view controller that handles the task of loading the appropriate controller from a secondary storyboard, whilst maintaining the navigation tree.
I've created a sample project and uploaded to github as it's easier than explaining all the steps here. The key part to note is the User Defined Runtime Attributes for each of the DynamicStoryboardViewControllers in the Main.stoyboard. Note also that each of the secondary storyboards need the "is initial View Controller" checked for one of your viewControllers. Not included in the example is loading a specific scene from a storyboard. This is no more than adding the "sceneName" dynamic runtime attribute much in the same way as the storyboardName attribute is added.
it's a quick sample so a little rough, but you'll get the idea of how it all works. Feel free to ask questions if you get stuck.
Cheers!
EDIT:
It dawned on me that perhaps you don't have a navigationController in the view hierarchy (as i do in my sample). And in any event, seemingly, you won't have much control over where your tab bar is introduces. So without a navigationController the [self.navigationController popViewControllerAnimated:YES] won't work;
You should test for this and either call the popViewControllerAnimated as you do, or call dismissViewControllerAnimated ;
if(self.navigationController){
[self.navigationController popViewControllerAnimated:YES];
}else{
[self.presentingViewController dismissViewControllerAnimated:Yes completion:nil];
}
Hope this helps, if not, perhaps you can supply some sample code.
Create an unwind segue within your original view controller, and use segues instead of pushing/popping view controller views onto the view array.
To create an unwind segue you should create an unwind method in the ORIGINAL viewcontroller:
- (IBAction)unwindToOriginalViewControllerSegue:(UIStoryboardSegue*)sender {
; //TODO: anything special you need, reference via sender
}
Then, in your NEW viewcontroller, in the storyboard, drag from the controller icon to the Exit icon. Link that to the Unwind segue you named above.
UIViewController * YourViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"Storyboard Identifier"];
[self.navigationController pushViewController:YourViewController animated:TRUE];
How can to go back to previous view programmatically without UINavigationController, because all examples that I have found use UINavigationController ?
I am making view for sending feedback and I would like to use this view in many more apps.
If I use UINavigationController than app need to have UINavigationController to use my view for sending feedback.
Is this possible to do and how ?
This is code how I show my view for sending feedback:
- (IBAction)showFeedbackView:(id)sender {
WOC_FeedbackViewController *feedbackView = [[WOC_FeedbackViewController alloc] init];
[self presentViewController:feedbackView animated:YES completion:nil];
}
Use this to go back
[self dismissViewControllerAnimated:YES completion:nil];
Depending on what your project works (Storyboards or XIB) you can use presentViewController. But thats when you keep a reference on your last viewController's identifier.
If you know the ID of your viewController NSString *yourID;
1) Init your viewController
For storyboard
UIViewController *vc = [self.storyboard instantiateViewControllerWithIdentifier:yourID];
For XIB
UIViewController *vc = [UIViewController alloc]initWithNibName:yourID bundle:nil];
2) Presenting your ViewController
[self presentViewController:vc animated:YES completion:nil];
P.S This is a way to do this, but you should use navigationController because you can keep memory of a viewController on stack so when you need to access it again, you can access it faster. In other words, UINavigationController is applied in every iOS Application you see on App Store.
In Swift, you can use following line of code to go back to previous view controller without navigation controller:
self.dismissViewControllerAnimated(true, completion: nil)
I'm completely re-formulating this question having learned that I was originally off track but that having me no closer to solving the problem. With reference to this image...
I am wanting to either create or manipulate the segue (highlighted in yellow) in code such that the Master view is any one of a number of subclasses of MFMasterViewController (highlighted in red).
When doing this using Nibs I could create a Nib, SharedNib.xib & set the class as MFMasterViewController, then create my subclasses, say MFMasterViewControllerSubclassA, MFMasterViewControllerSubclassB etc. & then instantiate whichever subclass I wanted using...
MFMasterViewControllerSubclassA *controller = [[MFMasterViewControllerSubclassA alloc] initWithNibName:#"SharedNib" bundle:nil];
or...
MFMasterViewControllerSubclassB *controller = [[MFMasterViewControllerSubclassB alloc] initWithNibName:#"SharedNib" bundle:nil];
etc.
Any clues as to how I can get this right using storyboards?
In my case the reason for wanting to do this is that all my subclasses are the same tableview & data but sorted differently & having some difference in what's written to the detail text of the cels. I suspect that it is a not uncommon pattern.
Cheers & TIA,
Pedro :)
It's not a direct answer but this is how I would accomplish what you want based on your explanation of the reason.
Basically you need to separate the UITableViewDataSource (and maybe the delegate too) from the MFMasterViewController so when the segue is executed you can set the correct dataSource and delegate in the view controller.
So in the Navigation Controller you need to implement the prepareForSegue:sender: method. This is where you can customize the segue before it is executed:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
// you can set the segue identifier using Interface Builder
// also it is a good thing to make sure which segue you're using
if (([segue identifier] isEqualToString:#"TheId"]) {
id<UITableViewDataSource> dataSource = [[TableViewDataSourceImplementationA alloc] init];
[[[segue destinationViewController] tableView] setDataSource:dataSource];
}
}
This way you can get the customization you want without the need to create subclasses of your view controller.
And if you have access to WWDC videos, check the session #407 Adopting Storyboards in Your App.
For anyone stumbling upon this question, you should also consider more generally using a "Strategy" pattern as an alternative to subclassing your controller. The accepted answer is a form of that, where the strategy implemented comes from whatever the data source/delegate is, and can be switched out at runtime. Another example of this is https://stackoverflow.com/a/17381927/954643
If your .m file is not associated with any storyboard, wouldn't self.storyboard be Nil?
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:
#"MainStoryboard" bundle:[NSBundle mainBundle]];
ViewController *viewController = [storyboard instantiateViewControllerWithIdentifier:#"HauptMenu"];
Make sure to change the storyboardWithName: to whatever your storyboard is named.
NSString * storyBoardName;
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
storyBoardName = #"MainStoryboard_iPad";
} else {
storyBoardName = #"MainStoryboard_iPhone";
}
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:
storyBoardName bundle:[NSBundle mainBundle]];
ViewController *viewController = [storyboard instantiateViewControllerWithIdentifier:#"HauptMenu"];
I believe i finally found the answer. We want to use storyboard ViewController with another class name. There are many workarounds like using delegates but i think this is the best one. I already answered it in another topic. Hope it helps!
https://stackoverflow.com/a/32103618/1943053