I searched for this on SO and other sites but didn't find anything useful
What I have is..
One base class say TOPViewController.h/.m
In this class I have created my controls and I am using this class on all my other views, by creating object of this class.
Say,
ViewController1, ViewController2, ViewController3 are my other views and I am using the TOPViewController on all these views.
Now My current view is ViewController2 which is visible. I jumped from ViewController1 to ViewController2.
now in my TOPViewController how I will come to know which is my current viewcontroller visible.
All the view controllers are adding TOPViewController object as [self.view addSubview:topViewObj];
Here is code of adding my TOPViewController in all views,
topBarViewObj = [[TopBarViewController alloc]init];
topBarViewObj.view.backgroundColor = [UIColor whiteColor];
topBarViewObj.view.frame = CGRectMake(0, 0, 320, 50);
topBarViewObj.titleLable.text = #"TEST";
[self.view sendSubviewToBack:topBarViewObj.view];
[self.view addSubview:topBarViewObj.view];
Please guide me for the same.
Thanks in advance..
If you want to know which is the top most UIViewController visible according to your UINavigationController (so the last UIViewController that has been pushed on your UINavigationController stack):
self.navigationController.topViewController;
if you are using UINavigationControoler
you can get baseViewController
baseViewControoer = [self.navigationController.viewControllers firstObject]
But if you did not use the following line
[self.navigationController pushViewController: animated:];
you will not get above result
as you are adding TopBarViewController's view with addSubView to your view so you just try to get the parent class by calling
`id parentClass = [self.view parentViewController]; `
and then try with
`if([parentClass isKindOfClass[ViewController1 Class]]){
// view 1
}
else if ([parentClass isKindOfClass[ViewController2 Class]])
{
// view 2
}
else if ([parentClass isKindOfClass[ViewController3 Class]])
{
// view 3
}
`
It might work for you. Let me know.
Related
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.
I am having problems passing a variable from a view controller to a UIPopoverViewcontroller
is the logic not the same as passing data between two usual viewControllers?
i.e setting a variable in view1 & view2 and synthesising then presenting the PopoverView pass the variable from view1 to view2?
I am really struggling with this I have read a little about protocols but seems overkill if it would work like a usual view controller?
An example
self.optionsPopover.delegate = self;
popOverViewController = [[PopOverViewController alloc]initWithNibName:#"PopOverViewController" bundle:nil];
self.optionsPopover = [[UIPopoverController alloc]initWithContentViewController:popOverViewController];
//dictionary to pass from view1 to view2 which is a popover
self.popOverViewController.statsDict = self.statsDict;
[self.optionsPopover setPopoverContentSize:CGSizeMake(320, 480)];
[self.optionsPopover presentPopoverFromBarButtonItem:optionsButton permittedArrowDirections:UIPopoverArrowDirectionUp animated:YES];
There is nothing wrong in your code except you are setting delegate of popOver before creating popover. I have changed the order. And check your dictionary is nil or not
self.popOverViewController = [[PopOverViewController alloc]initWithNibName:#"PopOverViewController" bundle:nil];
//dictionary to pass from view1 to view2 which is a popover
//self.popOverViewController.statsDict = self.statsDict;
self.popOverViewController.testString = #"testing"; // This is for testing only
self.optionsPopover = [[UIPopoverController alloc]initWithContentViewController:popOverViewController];
self.optionsPopover.delegate = self;
[self.optionsPopover setPopoverContentSize:CGSizeMake(320, 480)];
[self.optionsPopover presentPopoverFromBarButtonItem:optionsButton permittedArrowDirections:UIPopoverArrowDirectionUp animated:YES];
The UIPopOverViewController will allow you to show your own UIViewController (act as a container). Like a UINavigationController that receives a UIViewController. What exactly do you want to do in your UIPopoverViewController?
After initWithContentViewController, your popOverViewController's viewDidLoad method is already called (and that time the statsDict property is nil).
So either set the property before the initializer is called or write a custom setter like:
- (void)seStatsDict(NSDictionary *)statsDict
{
_statsDict = statsDict;
if (self.view) {
// Set UILabel texts, etc...
}
}
I'm trying to add a view controller container library to the remail email client. The idea is that the view controller container presents its child view controllers in two layers. It provides functionality for sliding the top view to reveal the views underneath it.
In the library description this is the suggested way of attaching a child controllerView to it's parent:
if (![self.slidingViewController.underLeftViewController
isKindOfClass:[MenuViewController class]]) {
self.slidingViewController.underLeftViewController =
[self.storyboard instantiateViewControllerWithIdentifier:#"Menu"];
}
where slidingViewController is the top-level instance of the view controller container. With this instance, you can set the view controllers underneath the top view and add panning.
I'm using xib files, not a storeyboard. so my code looked like this instead:
if (![self.slidingViewController.underLeftViewController
isKindOfClass:[MenuViewController class]]) {
self.slidingViewController.underLeftViewController =
[[MenuViewController alloc] initWithNibName:#"Menu" bundle:nil];
}
but using that code.. I'm getting this error:
-[__NSArrayM insertObject:atIndex:]: object cannot be nil
which can be traced to slidingViewController doing the following:
[self.view insertSubview:_underTopViewController.view atIndex:0];
looking at the documentation.. I see there is a difference between instantiateViewControllerWithIdentifier and initWithNibName: the former returns an object at all times.. where as the latter only loads the first time the view controller’s view is accessed.
Question: how do I make an initWithNibName return a loaded viewcontroller object regardless if that view has been visited or not.. similar to instantiateViewControllerWithIdentifier?
You should be able to trigger it by just accessing the view property, something like;
if (![self.slidingViewController.underLeftViewController
isKindOfClass:[MenuViewController class]])
{
MenuViewController *vc =
[[MenuViewController alloc] initWithNibName:#"Menu" bundle:nil];
[vc view]; // <-- access the view and trigger loading
self.slidingViewController.underLeftViewController = vc;
}
I'm trying to pass data to a childviewcontroller.
I have a view controller with two buttons and one view. Pressing the buttons defines the view that is shown.
The specific case is I want to show a list of items. The first way (button) is in a list, the second on a mapview. To show the items i need to pass a category to the child.
In my viewDidLoad I add both viewcontrollers with addchildviewcontroller en set my view to the view of the listviewcontroller.
-(void)viewDidLoad
{
[super viewDidLoad];
UIStoryboard* sb = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
ItemListViewController * itemListViewController = (ItemListViewController *)[sb instantiateViewControllerWithIdentifier:#"ItemList"];
itemListViewController.view.frame = detailView.bounds;
[self addChildViewController:itemListViewController];
[itemListViewController didMoveToParentViewController:self];
itemListViewController.category = category;
ItemListMapViewController * itemListMapViewController = (ItemListMapViewController *)[sb instantiateViewControllerWithIdentifier:#"ItemListMap"];
itemListMapViewController.view.frame = detailView.bounds;
[self addChildViewController:itemListMapViewController];
itemListMapViewController.category = category;
childControllers = [NSArray arrayWithObjects:itemListViewController, itemListMapViewController, nil];
[self.detailView addSubview:itemListViewController.view];
currentPage = 0;
}
Acoording to the button pressed I change the my view
- (IBAction)buttonClicked:(id)sender
{
UIButton * button = sender;
UIViewController *source = (UIViewController *)[childControllers objectAtIndex:currentPage];
[source.view removeFromSuperview];
UIViewController *destination = (UIViewController *)[childControllers objectAtIndex:button.tag - 100];
[self.detailView addSubview:destination.view];
currentPage = button.tag - 100;
button = nil;
}
But passing the category to my childviewcontrollers does nothing. The category in my childcontrollers is always null.
I also tried to get the category by accessing the parentviewcontroller on the childviewcontroller,
NSLog(#"::%#", ((ItemListHeaderViewController *)self.parentViewController).category);
but this also results in null.
I don't know what I'm doing wrong or maybe I'm understanding the whole containment story wrong... I'm new to ios development, so don't shoot me if the question is stupid. :) This is also my first question on stack overflow, so again don't shoot if I did something wrong.
Help would be appreciated. Thanks in advance.
Kind regards...
In the child view controller .h:
#property(nonatomic, strong) NSString *category;
Child VC .m:
#synthesize category;
Then get a reference to that class in the parent class and set the property. I wonder why you are doing this in code. Laying this out in IB is soooo much easier and working with segues more straightforward to me.
Wow, I gave a big thought on the question!
So I have a view Controller called "ContentView" within another view Controller (Main VC). The Main VC has a Navigation Controller which was created using Storyboards. And the contentView loads 3 different view controllers (vc1, vc2 and vc3) depending on the options that a Segmented Control has. So the question now is:
How can I load a new View Controller from the button within one of the subviews (vc2) that will appear once the user selects the option from the segmented control?
Although I have the visible view controller (vc2) in my storyboard, obviously I cannot connect an action to the button to the vc2' file's owner since the Nav Controller is on the Main VC.
I tried to access it with the following code:
AppDelegate *del = (AppDelegate *)[UIApplication sharedApplication].delegate;
UINavigationController *navigationController = (UINavigationController*)del.window.rootViewController;
DetalleMisMarcas *detalleMarcas = [[DetalleMisMarcas alloc] initWithNibName:#"DetalleMarcas" bundle:nil];
[navigationController pushViewController:detalleMarcas animated:YES];
But it does not work.
I have tried to find a solution from this forum, but I had no luck. Most consider the existence of a Main Window Xcode 4.2 does not have.
Finally, the way I load the 3 subviews, is here:
-(IBAction)segmentCtrlChanged:(id)sender {
UISegmentedControl *seg = sender;
if (seg.selectedSegmentIndex == 0)
{
MMViewController *mm= [self.storyboard instantiateViewControllerWithIdentifier:#"MMView"];
mm.view.frame = CGRectMake(0, 0, 320, 240);
[contentView addSubview:mm.view];
}
else if (seg.selectedSegmentIndex == 1) {
MPViewController *mp = [self.storyboard instantiateViewControllerWithIdentifier:#"MPView"];
mp.view.frame = CGRectMake(0, 0, 320, 240);
[contentView addSubview:mp.view];
}
}
-(IBAction)mainSubView:(id)sender
{
MMViewController *mm = [[MMViewController alloc] initWithNibName:#"MTView" bundle:nil];
[contentView addSubview:theMTView];
mm.view.frame = CGRectMake(0, 0, 320, 240);
}
Any ideas?
EDIT: Based on your comment, I think delegates will work better. This is how you may implement it.
Let's say in contentView controller class, add a delegate
// in the header file
#property (weak, nonatomic) id <ContentViewControllerDelegate> delegate;
#protocol ContentViewControllerDelegate <NSObject>
- (void)contentViewDidSelectAView:(UIView *)view;
#end
// in the implementation
- (IBAction)segmentCtrlChanged:(UISegmentedControl *)sender {
case 0:
[self.delegate contentViewDidSelectAView:[[self.storyboard instantiateViewControllerWithIdentifier:#"MMView"] view];
break;
// and the rest of the code
}
// in MainView header file
#interface MainViewController : UIViewController <ContentViewControllerDelegate>
// in MainView implementation
- (void)contentViewDidSelectAView:(UIView *)view {
[self.view addSubView:view];
}
I haven't actually implemented the above code. But I think this should be the way for a controller to talk to its parent. Hope this one helps.
I am not sure what exactly you are doing but let's say you have a UINavigationController in the root, and mainViewController is linked via segue as a relationship. As you have implemented, the segmentedControl in mainVC should be linked to -(IBAction)segmentCtrlChanged:(id)sender with valueChange event.
In this method you can switch the index, rather than using if, although they work the same, and you can also use UISegmentedControl * instead of id :
-(IBAction)segmentCtrlChanged:(UISegmentedControl *)sender {
switch(sender.selectedSegmentIndex) {
case 0: {
MMViewController *mm= [self.storyboard instantiateViewControllerWithIdentifier:#"MMView"];
[self.navigationController pushViewController:mm animated:YES];
break;
}
case 1: {
MPViewController *mp = [self.storyboard instantiateViewControllerWithIdentifier:#"MPView"];
[self.navigationController pushViewController:mp animated:YES];
break;
}
case 2: {
// similar to above
break;
}
default:
break;
}
vc1, vc2 and vc3, -- I assume -- are instances of UIViewController. So make sure that their class is linked, and that their identifiers are set properly. and then, just leave them as they are, because you are not going to make any segue for them individually. They are going to be pushed to the stack via code.
Hope it helps.