How to call NavigationController within class, not derived from UIViewController? - ios

My service class hasn't derived from UIViewController, but I'd like to call NavigationController to move to another controller.
class AppoxeeService
{
public void ShowInbox(){
..
//this code doesn't work (of course),
//because class isn't derived from UIViewController
NavigationController.PushViewController(new AppoxeeInboxController(), true)
//this code is mostly possible,
//but the NavigationController is null here
UIApplication.SharedApplication.KeyWindow.RootViewController.NavigationController.PushViewController(new AppoxeeInboxController(), true)
}
}
The Xamarin documentation is described well only for storyboards, but I'd like dynamically find current RootViewController and bind it to NavigationController.
In other simple (aka "HelloWord") samples from Xamarin or their documentation it is possible to instantiate from FinishLaunching method:
var window = new UIWindow (UIScreen.MainScreen.Bounds) {
BackgroundColor = UIColor.White
};
rootViewController = new RootViewController ();
navigationViewController = new UINavigationController (rootViewController);
window.RootViewController = navigationViewController;
window.MakeKeyAndVisible ();
I'm fully stucked with NavigationController and its usage from code.

You don't need to write complex code.
just call your instance of Navigation Controller that you declared in AppDelegate class
In AppDelegate.h
#property(nonatomic,strong) UINavigationController *navigationController;
In AppDelegate.m
#synthesize navigationController
then your code in didLaunch Method
in your class
just call the instance anywhere
[(((AppDelegate *)[[UIApplication sharedApplication] delegate]).navigationController pushViewController:yourViewController animated:YES];

Related

DecisionNavigationController + TabBarController = 2 navigationsBar

I have a problem with my personal project.
This is my architecture :
Then, my MainViewController can push to TabBarController or another single Controller.
The problem is when I push to the TabBarController, it displays me 2 navigationBar in my 1st controller (from the tabbar)
I can hide the MainController navigation bar to display only the navigation bar from the tabbar but I dont think this is the best practice.
How can I do this ?
Thanks for help.
I think the best solution is that change the Window.RootViewController in AppDelegate , so that you don't need to deal with multiple navigation bars.
Set NavigationController with MainViewController first in AppDelegate
public partial class AppDelegate : UIApplicationDelegate
{
UIWindow window;
public override bool FinishedLaunching (UIApplication app, NSDictionary options)
{
window = new UIWindow (UIScreen.MainScreen.Bounds);
window.RootViewController = new UINavigationController(new MainViewController());
window.MakeKeyAndVisible();
return true;
}
public void changeRootVC()
{
window.RootViewController = new TabController();
}
}
Change RootViewController instead of pushing to TabbarController in MainViewController
AppDelegate app = UIApplication.SharedApplication.Delegate as AppDelegate;
app.changeRootVC();

how to call function from other VC?

I have some variable lets say isMenuVisible = false; I want to set some function when this var is changed:
isMenuVisible: Bool!{
didSet{
callFunctionFromOtherViewController()
}
}
How is that possible? Do I need to create instance of VC in order to access that function? Or I need to make that function public?
To call a method/function from another VC (or from any other class), you have 2 choices:
Create a class method in this other view controller:
In your MyViewController.h
+(void)myClassMethod;
Wherever you need to use it
[MyViewController myClassMethod];
Create an instance method in this other view controller:
In your MyViewController.h
-(void)myClassMethod;
Wherever you need to use it
MyViewController *myViewControllerInstance = [[MyViewController alloc] init];
[myViewControllerInstance myClassMethod];
definitely no need to create instance for the viewController,
if it is a rootViewController you can get it like that :
var appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
var vc = (appDelegate.window?.rootViewController as? MyViewController)!
if your view controller is parent, it is like that:
var myViewfromParent = self.parentViewController as? MyViewController
if you use storyboard with id, you can get it like that:
let storyboard = UIStoryboard(name: "MyStoryboardName", bundle: nil)
let vc = storyboard.instantiateViewControllerWithIdentifier("someViewController") as! UIViewController
hope it helps
If you're using viewController containment and you can easily reference the view controller you can simply create a public method in your .h file and call that method... however, with viewControllers it's often the case that a. you don't have / want universal access to the viewController instance in other classes and b. the viewController may not even exist when this method is changed. For these reasons I'd go with NSNotifications when triggering actions in viewControllers.
NSNotificationCenter addObserver in Swift
If this isn't acceptable and you can guarantee that this class will be linked in an architecturally sound way, I would create a delegate. That way you can set the delegate when you create the viewController and there's no mess when trying to get back to your viewController... I'd only recommend this if there's a genuine connection between the viewController and the class that contains the action (in this case the bool)
In objective c you'd do
MyViewController *myViewController = [[MyViewController alloc] init];
myViewController.delegate = theClassContainingTheBool; // could be self.. might not be... If you can't do this line of code, then you should use notifications
In swift
Delegates in swift?
How to define optional methods in Swift protocol?

iOS how present Modal ViewController from Uiview

i have subclass a UIView and now i need to show a view controller but UIView not have method to present view controller.
this is my problem
thank's
this is a piece of code inside my uiview subclass
-(void)tableView:(UITableView*)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
if ([tabella isEqualToString:#"Articoli"]) {
NSDictionary *itm=(NSDictionary*)[comanda objectAtIndex:indexPath.row];
Articoli *aboutViewController = [[Articoli alloc] initWithNibName:#"Articoli" bundle:nil];
aboutViewController.modalPresentationStyle = UIModalPresentationFormSheet;
aboutViewController.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
aboutViewController.idarticolo=[itm objectForKey:#"idarticolo"];
CGRect aboutSheetFrame = aboutViewController.view.frame;
UIViewController *viewController = [UIViewController new];
viewController.view = self;
//here xcode give me a red error
[self presentViewController:viewController animated:YES completion:nil] ;
aboutSheetFrame =CGRectZero;
aboutViewController.view.superview.bounds = aboutSheetFrame;
}
}
When you need a communication between UIView instance and UIViewController, there are a few known iOS concepts, which you should adhere to. As you have figured out that UIView cannot really present a new controller (missing either presetViewController:animated:completion methods or navigationController property, which are both present in UIViewController).
Views are supposed to be the most reusable parts of your code, so you must think of a way to design your views to be completely blind to where they are at. They usually only know about user interaction.
So first, what you must do is refactor your view.
If your UIView is supposed to be a UIControl (has some kind of target selectors), you need to use add target in your controller to get callback from view interaction.
You can use delegate pattern as used in UITableView or UICollectionView, which is designed as a protocol.
You can use gesture recognizers added to a view (UITapGestureRecognizer for example), so the controller knows about user interaction.
You can even mix and match those architectural patterns.
But you should really look into iOS programming basics, to understand this better.
In addition the first error I see in your code is that you create a generic UIViewController, when you should really be creating custom subclasses of it, defined in Storyboard and separate subclass of UIViewController.
The second error I see is that your UIView responds do tableView:didSelectRowAtIndexPath: method, which should in fact never happen. All this code must be moved back to one UIViewController subclass.
You can do this without any view hierarchy issues using the below code.
ObjectiveC
UIViewController *currentTopVC = [self currentTopViewController];
currentTopVC.presentViewController.........
- (UIViewController *)currentTopViewController
{
UIViewController *topVC = [[[[UIApplication sharedApplication] delegate] window] rootViewController];
while (topVC.presentedViewController)
{
topVC = topVC.presentedViewController;
}
return topVC;
}
Swift
var topVC = UIApplication.sharedApplication().keyWindow?.rootViewController
while((topVC!.presentedViewController) != nil){
topVC = topVC!.presentedViewController
}
topVC?.presentViewController........
PresentViewController is method of UIViewController class not of UIView, you can do one thing, create UIViewController instance and set its view to the view you have and then present it.
Something like below
YourCustomView *customView = [[YourCustomView alloc]initWithFrame:someFrame];
UIViewController *viewController = [UIViewController new];
viewController.view = customView;
//From currentViewController present this
[self presentViewController:viewController animated:YES completion:nil] ;
Customize this code as per your requirement
But as you are in view you need to pass this event to viewController, so better implement delegate method and at place where you calling present viewController call delegate which is implemented in ViewController and in side that presentViewController with customView set to its view property
You can also present your viewcontroller from the navigation controller object
Create Global Navigation Object in App Delegate or anywhere, you can access navigationcontroller object from view
#property (strong, nonatomic) UINavigationController *gblNavigation;
//Present viewcontroller from NavigationController object
[gblNavigation presentViewController:YOUR_VC_Object animated:YES completion:nil];
You can't present a view controller from a view. You can only present a view controller from a view controller.
Apple wants views to be dumb. That is views should only know how to display content. View should not respond to user interaction: that should be passed to a view controller.
You may want to consider using a delegate pattern, target action, or something similar to allow a view controller to control the interaction.
iOS 15, compatible down to iOS 13
Based on Shamsudheen TK solution for anyone who comes across this question in the future.
let presentedWindow = UIApplication.shared.connectedScenes.flatMap { ($0 as? UIWindowScene)?.windows ?? [] }.first { $0.isKeyWindow }
guard let currentViewController = presentedWindow?.rootViewController else {
return
}
currentViewController.present(UIViewController(nibName: nil, bundle: nil), animated: true)
Note that connectedScenes is available only since iOS 13. If you need to support earlier versions of iOS, you have to place this in an if #available(iOS 13, *) statement.

Get param from ViewController in AppDelegate.

I need to get in AppDelegate one parameter of some ViewController.
It not root for AppDelegate.
What is faster way to do it? Delegation?
Make it a property on your VC and then your AppDelegate can access it as needed.
First something is terribly wrong with your design otherwise there shouldn't be any need for you to do something like this.
Second, you haven't provided any relevant information about your VC hierarchy and there is no general solution for this.
However , here are few workarounds / patches:
1) If you are using storyboard you can use :
UIStoryboard* sb = [UIStoryboard storyboardWithName:#"mystoryboard"
bundle:nil];
UIViewController* vc = [sb instantiateViewControllerWithIdentifier:#"ExampleViewController"];
2) You can make make view controller singleton and access it directly from AppDelegate
3) Hacky Method: In AppDelegate have a #property (nonatomic, retain) UIVIewController *hackyViewController;
In hackyViewController.m do this
-(void)viewDidLoad{
// call super
YourAppDelegate *appDelegate = (YourAppDelegate *)[[UIApplication sharedApplication] delegate];
YourAppDelegate.hackyViewController =self;
}
Ideally , you should navigate through viewcontroller hierarchy using parentViewController and childViewcontroller property of UIVIewcontroller to get the instance. You can also make a recursive function which navigates through all childViewcontroller and check instance using iSKindOf to identify the viewcontroller you are looking for but this method does not work with all iOS configurations.

Why is an override on navigationController:willShowViewController:animated necessary for tab bars

I have an application that creates a tabbarcontroller from the AppDelegate. I wanted to have a button added to the nav bar but was unable to. Eventually I managed to get hold of some working code, but I don't really understand it.
The steps were:
Confirm the AppDelegate to UINavigationControllerDelegate
Set the rootNavigationController.delegate = self
Override navigationController:willShowViewController:animated and tabBarController:didSelectViewController
I think I follow the tabBarController:didSelectViewController code, but am lost in what is happening with navigationController:willShowViewController:animated.
- (void) tabBarController: (UITabBarController*) tabBarController didSelectViewController: (UIViewController*) viewController
{
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone)
{
self.tabBarController.navigationItem.title = viewController.navigationItem.title;
self.tabBarController.navigationItem.rightBarButtonItems = viewController.navigationItem.rightBarButtonItems;
self.tabBarController.navigationItem.leftBarButtonItems = viewController.navigationItem.leftBarButtonItems;
}
}
- (void) navigationController: (UINavigationController*) navigationController
willShowViewController: (UIViewController*) viewController
animated: (BOOL) animated
{
if (viewController == tabBarController)
{
UIViewController* tabViewController = tabBarController.selectedViewController;
SEL willShowSel = #selector(navigationController:willShowViewController:animated:);
if ([tabViewController respondsToSelector: willShowSel])
{
UIViewController<UINavigationControllerDelegate>* vc =
(UIViewController<UINavigationControllerDelegate>*) tabViewController;
[vc navigationController: navigationController willShowViewController: vc animated: animated];
}
}
This code is likely dealing with problems that occur using a UITabBarController within a UINavigationController. The UITabBarController documentation states that it needs to be the root view controller (i.e. NOT within a UINavigationController) and using it in other ways can cause problems.
What the code appears to be doing is capturing the event normally passed to viewController, checking if it is a UITabBarController and if it is, then it checks whether the visible view in the UITabBarController responds to this method, and if it does then it passes the method (selector) call on to that view.
If it is possible, I'd recommend pulling the UITabBarController out from being embedded in the UINavigationController. Might take a bit of work, but will make your code compliant. (And remove need for navigationController:willShowViewController:animated:

Resources