using self.navigationController.view in another class Xcode - ios

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.

Related

Best way to present a ViewController from many different ViewController

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.

Can't get passed data in viewDidLoad method

I'm trying to pass some data to my view controller class like this:
MyViewController *vc = [[MyViewController alloc] init];
vc.myProperty = dataToBePassed;
[self.navigationController pushViewController:vc animated:YES];
I need to make some view configuring in viewDidLoad, but it seems that viewDidload called earlier than property assignment.
Then in MyViewController implementation:
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(#"%#", self.myProperty); // Here i get myProperty = nil
}
- (void)viewWillAppear
{
[super viewWillAppear];
NSLog(#"%#", self.myProperty); // Here i get myProperty = dataToBePassed but it's to late
}
How can i get passed data in viewDidLoad method without implementing singleton or delegate patterns?
Try doing this
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:
#"MainStoryboard" bundle:[NSBundle mainBundle]];
MyViewController *vc = [storyboard instantiateViewControllerWithIdentifier:#"storyboardIdentifier"];
vc.myProperty = dataToBePassed;
[self.navigationController pushViewController:vc animated:YES];
You have to set a storyboard identifier first in the storyboard for the view controller.
While the code sample you provide looks technically correct, I'm with #john-elemans in that you need to show more code.
There is something that is referencing the view which causes it to load and therefore causes viewDidLoad to fire prematurely.
In any case, if something (such as your property) is absolutely essential to the correct building of your view structure, I'd put in its own designated initializer, e.g.,
- (id)initWithPhotoDiameter:(CGFloat)diameter
{
self = [super init...]; // some VC initializer that you should call
if (self) {
_photoDiameter = diameter;
}
return self;
}
Notice the use of the backing instance variable _photoDiameter instead of self.photoDiameter. This is about the only place in a class where you should use the backing ivar, since self is still in the process of being initialized.
Technically there are two approaches that are quite common for lifecycle handling of view controllers related to an application.
Using XIBs
When using XIBs one of the most common if not the most common process to create and setup your view controllers is done programmatically. Following this process, when you initialise the view controller you have the option of either overriding your init method in order for your view controller to have the information prior to loading the view and easing up the process of adjusting drawn content. You can also create a method within your view controller to be called in which you pass the data to be used by the view controller.
Using Storyboard
If you are using storyboards I recommend that you trust segues setup through it. I have found that they make life easier and it will allow you to use certain methods to handle the transition. One of those is prepareForSegue:sender: Within that method I have found that it is easier to setup a view controller after it's initialized accessing the destination controller. You also might consider having all data there before viewDidLoad hence following the segue approach.

Programatically return to previous ViewController in different storyboard

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 to share one storyboard scene between multiple UIViewControllers

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

Call another ViewController without using storyboard

I changed my button from calling another ViewController from Storyboard to download some stuff from a website using JSon. Now I need to call the very same ViewController after the code...
probably it is a simple stuff but I didn't quite get it
thanks
I am not sure that this is what you are looking for, but look at this.
Let's say you have two controllers, ViewController1 and ViewController2. After you are done with the JSON parsing in ViewController 1, let's say from [self finishedGetJSON], you could do this;
- (void) finishedGetJSON(yourargument*) arg {
ViewController1 * screen = [self.storyboard instantiateViewControllerWithIdentifier:#"yourIdentifier"];
[self.navigationController pushViewController:screen animated:YES];
Is that what you were looking for?

Resources