I'm having some trouble with the handleOpenURL method in my app delegate. I have a rootviewcontroller that should be shown normally, but when a file is opened in my app, I need the handleOpenURL method to present a new viewcontroller and send the url info. The problem is I can't present a modal view controller from my app delegate. Also, when I try to call a method in my rootviewcontroller to present the modalviewcontroller I get
Warning: Attempt to present ... whose view is not in the window hierarchy!
So, I guess I'm just looking for a solution that will present a new modal view controller and pass the url information to that class. This is the app delegate method.
- (void)handleOpenURL:(NSURL *)url {
}
Thanks for your help
In your RootViewController,Create an instance of appDelegate just like
YourAppDelegate *appDelegate = (YourAppDelegate *)[[UIApplication sharedApplication]delegate];
appDelegate.rootViewControllerInstance = self;
then subject
[appDelegate handleOpenURL:yourURL];
also create a callBackFunction eg:
-(void)callBack:(NSUrl *)url;
Now in appDelegate class create a property of your RootViewController
and in appDelegate your function as specified this way..
- (void)handleOpenURL:(NSURL *)url {
[rootViewControllerInstance callBackUrl:url];
}
In storyboard, add the view controller you want to present by adding a new view controller and setting its class to the one you are presenting.
Control-drag from the root view controller to the new controller, which will create a segue.
Set a name for the identifier of the segue, customize it to be shown modally.
In root view controller, call [self performSegueWithIdentifier:#"MySegueIdentifier"];.
In prepareForSegue of the root view controller set any attributes, including e.g. a URL.
Related
I have a TabBarController which is set up with multiple ViewControllers at launch. When the user clicks a button I want to send them to a different ViewController in the TabBarController, and pass data through a delegate.
I have a protocol and delegate set up. However, when do you set the delegate since all the ViewControllers are in the TabBarController
Is this possible, how can I pass data to another ViewController in the TabBar when the user clicks a button. Any ideas, I'd really like to use a delegate.
- (IBAction)sendData:(id)sender
{
[self.delegate setStringData:strData];
self.tabBarController.selectedIndex = 0;
}
Edit:
So let's say I have a TabBarController with two ViewControllers called ViewControllerOne and ViewControllerTwo.
I have ViewControllerTwo set up as the delegate and protocol. This is the ViewController that will send data to ViewControllerOne after the button is pressed. ViewControllerOne implements protocol and contains the method setStringData which should be called after the button in ViewControllerTwo is pressed.
From a UIViewController you want to change the selected tab bar index and pass data.
I suggest you add a function in you app delegate for this.
That way your UIViewController won't be tied with a UITabBar (if tomorrow you want to use a different UI idiom, you will just have to change the implementation of your function in your app delegate).
To pass data, i you could try to introspection in your function : you take the current UIViewController of the new selected tab index, verify it responds to your selector and call the function.
Edit :
Let's assume your 'just' have to change the selected tabBar index (e.g. your UIViewController will always be the same on the new tab bar index).
In your first View Controller :
- (IBAction)sendData:(id)sender
{
UIApplicationDelegate * appDelegate = [[UIApplication sharedApplication] delegate];
if ([appDelegate respondToSelector:#selector(goToFirstTabBarWithData:)])
{
[appDelegate performSelector:#selector(goToFirstTabBarWithData:) withObject: strData];
}
}
In your Appdelegate :
- (void)goToFirstTabBarWithData:(NSString *)data
{
self.tabBarController.selectedIndex = 0;
UIViewController * vc = [self.tabBarController.viewControllers objectAtIndex:0];
if ([vc respondToSelector:#selector(setStringData:)])
{
[vc performSelector:#selector(setStringData:) withObject:data];
}
}
In your second View controller (the one you will arrive on) :
- (void)setStringData:(NSString *)data
{
// Do something...
}
I found a simpler solution to my problem. Inside of ViewControllerTwo, I just create an instance of ViewControllerOne and pass it that data I need. Then I change the tabBarController index to ViewControllerOne.
For example:
// A method inside of ViewControllerTwo
ViewController *viewcontrollerOne = [ViewcontrollerOne alloc] init];
[viewcontrollerOne setStringData:str];
[viewcontrollerOne release];
self.tabBarController.selectedIndex = 0;
I am trying to create a UITableView using storyboard but I came to something that at the end may be easy but I have no idea how to solve it.
First of all let me point out that I know that one of the limitations of storyboards is that you will have to dig through the storyboard to find information about a view you have and link it to the app delegate.
I have create my mutable array and the information that I will use in the table in the app delegate and now I want to reference that UITableView to the app delegate. The hierarchy goes like that
First I have the root view that once you click on a button it will redirect you to the second view
Inside the second view there is another button that once you press it it will redirect you to the UINavigationController
The UINavigationController contains the UITableView.
Therefore as you can see there are two views before the navigation control and the UITableView.
Here is the code I am trying to use but it does not work
UIViewController *viewController = (UIViewController *)self.window.rootviewController;
// The next line refers to the second view but does not work at all
UIViewController *secondView = [[UIViewController viewController] objectAtIndex:1];
//Then the following line is to redirect from the second view to the navigation controller
UINavigationController *navigationController =[[secondView viewController] objectAtIndex:0];
//Then is the table view
BuildingsViewController *buildingsViewController = [[navigationController viewControllers] objectAtIndex:0];
The above code does not work. Can anyone please help me?
Thanks a lot
If this code is in the app delegate there are a variety of reasons why it will probably not work. Firstly you appear to be mixing up View's, ViewControllers and Navigation controllers with what you are trying to do. Secondly there is no guarantee at the time you are trying to do this that all of the views/viewcontrollers have yet been created yet or are joined in the way they will be when the final building view controller is rendered.
What you could try instead is in your BuildingsViewController (which you say is your table view controller) you can get a handle to the App Delegate by using
MyAppDelegate *myAppDelegate = (MyAppDelegate *)[UIApplication sharedApplication].delegate
Once you have a handle to the delegate you can simply reference your mutable array structure etc. that you created on it from within your BuildingsViewController.
e.g. in the 'numberOfRowsInSection' method:
MyAppDelegate *myAppDelegate = (MyAppDelegate *)[UIApplication sharedApplication].delegate
NSMutableArray *myBuildings = myAppDelegate.buildingArray;
return [myBuildings count];
Or in the cellForRowAtIndexPath method:
// something like this but using your names for app delegate, building array and the accessor for the building name
MyAppDelegate *myAppDelegate = (MyAppDelegate *)[UIApplication sharedApplication].delegate
NSMutableArray *myBuildings = myAppDelegate.buildingArray;
cell.textLabel.text = [myBuildings objectAtIndex indexPath.row].theBuildingName;
I have a tab bar controller on my main window. Each tab is associated with a different view controller and when you click on a tab it sets a different view controller as the selected controller.
My problem is how to pass objects to the view controller e.g. managed context. Any ideas?
- (IBAction)mainMenuButtonTapped:(id)sender {
if (![myTabBarController.view window]) {
[self.window addSubview:myTabBarController.view];
}
UIViewController *selectedViewController = [myTabBarController.viewControllers objectAtIndex:[sender tag]]; //I wanted to pass the self.managedObjectContext to the view controller here, I have tried casting but that doesn't work because some of the vc's are navigation controllers. I have tried using outlets etc.
[myTabBarController setSelectedViewController:selectedViewController];
}
Simply get the managedObjectContext from your app delegate in viewDidLoad of each view controller.
self.managedObjectContext =
[(YourAppDelegate*)[UIApplication sharedApplication].delegate managedObjectContext];
I have a root view controller which contains an outlet for my login view controller. the root view should control the flow to the next view, yet my login view has the button to continue. how would I set the button's touch up inside to the IBAction in my root controller?
One method I have though of was keep a pointer to the root class where I create the login class (new code is commented out):
// RootViewController.m
- (void)viewDidLoad {
LoginViewController *loginController =
[[LoginViewController alloc]initWithNibName:#"LoginView" bundle:nil];
self.loginViewController = loginController;
//loginViewController.parent = self;
[self.view insertSubview:loginController.view atIndex:0];
[loginController release];
[super viewDidLoad];
}
- (IBAction)loginPressed: (id)sender
{
self.loginViewController.loginButton.enabled = NO; //yea... doesnt work
}
So in IB I add a UIViewController to the Nib and give it parent as an outlet, and then assign button's touch up inside event to loginPressed which is defined in the root controller (parent)... this didnt work so well explicitly refering to the controls from self.loginViewController in the RootViewController.
is there a correct way to do this.
-frustrated c++ / c# / java coder
Have you considered presenting the loginView modally? Then you can pop the view and be back at the rootViewController and move on from there.
Create the button pressed method outlet in the same class as that XIB and hook it up. I assume your login view is created by the root controller... create an assign property for the root view on the login view, and then assign the root controller to that when creating the login view. Then, in the login view's method for "button pressed" you can reference the root view controller. Spelled out:
in LoginController's .h file:
#property (nonatomic, assign) RootViewController* rvc;
in LoginController's .m file:
#synthesize rvc;
in Root View Controller's "make the login controller code", after you've initialized it but before you've presented it:
[loginController setRvc:self];
in your LoginController's button touched method:
[[self rvc] whateverMethodThatDoesLoginStuff];
This way you have your path between when the user presses the button and your "do-login" code.
You can have a loginViewController as an IBOutlet, connect its action 'touch up inside' to -loginPressed: of file owner.
Or you can alloc and init your controller in -viewDidLoad programmatically all by yourself, and set the action programmatically as well.
But what you did seems to mix the two ways.
I have a view that is UIViewController (root) that handles ads and a UINavigationController. In that UINavigationController I have typical layers of controllers. In my custom settings table view controller, I have a button to contact me for support. When the user clicks this button I create a MFMailComposeViewController and would like to present it. I can't present it from the settings table view as it will be underneath my ads, so I need to reference the root view and present it from there. I've tried self.parentViewController.parentViewController where self is the settings table view, but that doesn't work. How should I reference this. It seems like a bad design to have to reference the root view directly and pass it to the settings view.
Get the current keyWindow:
UIWindow *window = [UIApplication sharedApplication].keyWindow;
Get its rootViewController:
UIViewController *rootViewController = window.rootViewController;
NOTE: If an UIAlertView is currently being shown, a new window is being created and assigned to be the keyWindow. There might be other exceptional cases as well that will not keep your application window to be the keyWindow.
In my experience, this is the ideal solution to get the rootViewController:
[[[[UIApplication sharedApplication] delegate] window] rootViewController]
Note: In-case of an active UIAlert or UIActionSheet, you get the Alert or Action Sheet as your key window.
Use the app singleton. Something like:
[[[UIApplication sharedApplication] delegate] rootViewController] should get it if your viewController that is the root is named rootViewController
You can always solve this with 1 line of code but I recommend this Swift way to do it, you can call this from anywhere, its also crash and bug safe:
/// EZSwiftExtensions - Gives you the VC on top so you can easily push your popups
var topMostVC: UIViewController? {
var presentedVC = UIApplication.sharedApplication().keyWindow?.rootViewController
while let pVC = presentedVC?.presentedViewController {
presentedVC = pVC
}
if presentedVC == nil {
print("EZSwiftExtensions Error: You don't have any views set. You may be calling them in viewDidLoad. Try viewDidAppear instead.")
}
return presentedVC
}
Its included as a standard function in:
https://github.com/goktugyil/EZSwiftExtensions
Get the UIApplication object.
Cycle through the windows array to find the keyWindow.
And then get the rootViewController property.