I have a viewController I've built in storyboard. I also have a NSObject Subclass which acts as my model, which sends and listens for API requests and responses. When a method fires in my model, I want to present a modal View of my viewController from whatever view happens to be visible at the time.
An example would be if my API hears "show this view" I want to show viewController regardless of what view is being shown.
Conceptually, how does one do this?
EDIT: I don't know which view controller will be showing when I want to present my modal viewController. Also, I need to pass params from my model to the modalVC when it's presented.
I would send a notification from the model telling "someone" that some view needs be displayed.
NSDictionary *userInfo = #{ #"TheViewKey": viewToDisplay];
[[NSNoticationCenter defaultCenter] postNotificationName:#"NotificationThatThisViewNeedsToBeDisplayed" object:self userInfo:userInfo];
And then on the delegate (or the active view controller) would register to this notification and handle the display.
// self is the delegate and/or the view controller that will receive the notification
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(handleViewToDisplay:) name:#"NotificationThatThisViewNeedsToBeDisplayed" object:nil];
If you put in the view controller remember to remove self from the observers when the view is not visible:
[[NSNotificationCenter defaultCenter] removeObserver:self name:#"NotificationThatThisViewNeedsToBeDisplayed"];
This way your model is decoupled from the presentation.
You have the current viewController (any viewController subclass) present the new view using:
- (void)presentViewController:(UIViewController *)viewControllerToPresent animated:(BOOL)flag completion:(void (^)(void))completion
EDIT: To find the top view controller, you ask the UITabBarController for the selectedViewController (if you use a tabBarController) to get the 'seed', or start with the window.rootViewController.
Once you are past any tabBarControllers, then you should only have UIViewController subclasses and UINavigationControllers. You can use a loop like this:
- (UIViewController *)frontmostController:(UIViewController *)seed
{
UIViewController *ret;
if([seed isKindOfClass:[UINavigationController class]]) {
ret = [(UINavigationController *)seed topViewController];
} else
if([seed isKindOfClass:[UIViewController class]]) {
ret = seed.presentedViewController;
}
return ret ? [self frontmostController:ret] : seed;
}
Related
Hello I am using Page menu in application and i want to push from didSelectRowAtIndexPath in next view in one view which is involed in page menu
i know all way which we use for push and model.if i am using model with custom navigation bar then page menu hide on dismiss model.
Assumed hierarchy:
UINavigation Controller -- UIViewController -- UIPageController
UIPageController (with 2 UIViewController) -- UITableViewController-1 and UITableViewController-2
On didSelectRowAtIndexPath method
UIPageController -- UITableViewController-1 //Selected any one row
Then just push the next view controller onto the current UITableViewController.
Note - Both controller in UIPageController maintain separate life cycle that is independent from each other.
One can not push the viewcontroller in pageView , PageviewController don't have navigation controller .
Solution :
There are two ways to make this possible suggested by Nitin Gohel
set notification for event and Retrieve the notification on main Controller
You can send the information in Notification required in another viewController.
NSDictionary* userInfo = #{#"abc": #"aaa"};
NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
[nc postNotificationName:#"One" object:self userInfo:userInfo];
In MainViewController just set the onserver for that notification in viewDidLoad method
- (void)viewDidLoad {
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(OneNoti:) name:#"One" object:nil];
}
And perform push from MainViewController:
-(void) OneNoti:(NSNotification*)notification
{
NSDictionary* userInfo = notification.userInfo;
NSNumber* total = (NSNumber*)userInfo[#"total"];
NSLog (#"Successfully received test notification! %i", total.intValue);
NSLog(#"%#",notification.userInfo);
[self performSegueWithIdentifier:#"1_" sender:nil];
}
Set the delegate methods in MainView Controller and call the methods while performing action from page view and push from that Action .
Same like above way .
I have a UITabBarController with four tabs. In each of the view controllers presented when a tab is selected I have a reset button. Tapping the button will change the appearance of all the view controllers. In particular, it will change the text of some labels in the different view controllers.
Is there some recommended way to update all the view controllers of a UITabBarController at the same time i.e. to make them reload their views?
My current approach is to make those view controllers conform to a protocol
#protocol XYReloadableViewController
- (void)reloadContents;
#end
and then send the message -reloadContents to all the view controllers when the button is tapped:
- (IBAction)touchUpInsideResetButton {
// ...
NSArray *viewControllers = self.tabBarController.viewControllers;
for (UIViewController<XYReloadableViewController> *viewController in viewControllers) {
[viewController reloadContents];
}
}
Then in each of the view controllers I would have to implement that method:
- (void)reloadContents {
[self.tableView reloadData];
// other updates to UI ...
}
But that seems a little too complicated. So is there an easier way to tell the view controllers to reload their views?
Edit: And what happens if I present a UINavigationController in some of the tabs or a container view controller? I would need to pass the message along the chain of all its child view controllers...
You can create ReloadViewController and all you contrlollers inheritance
from him.
ReloadViewController have property UIButton and methods:
-(void)reloadContents;
-(IBAction)touchUpInsideResetButton:(id)sender;
in .m file:
-(void)viewDidLoad
{
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(reloadContents)
name:#"MyNotification"
object:nil];
}
- (IBAction)touchUpInsideResetButton:(id)sender
{
[[NSNotificationCenter defaultCenter] postNotificationName:#"MyNotification"
object:nil];
}
in your viewControllers need only override method reloadContents
Notifications sound like a better fit for this. When view controllers need to be reset, broadcast an NSNotification and have any view controllers that might need to reset themselves listen for that notification, and trigger what they need to do. That way it doesn't matter how far down a navigation stack they are.
You might want to defer updates until the view actually appears. You could set a BOOL needsUpdate when the VCs receive the notification, but only do the actual update in viewWillAppear:, to save resources and prevent a large number of updates from going off at once (and perhaps blocking the main thread).
If this behaviour is common to all your view controllers, make a UIViewController subclass to prevent repeating code and have them all inherit from that. Alternatively, (if you're using Apple VC subclasses) make a category on UIViewController to add the notification methods.
Scenario:
I have a calendar control as a UIview and I want that when I click on date button of the view I want to navigate to some other UIview controller but when I use:
HomeView *scrhome=[[HomeView alloc]init];
[self presentViewController:scrhome animated:YES completion:nil];
It gives an error:
HomeView is not in memory stack
Thanks in advance.
As schrome is UIView not a view controller,it can not be passed an argument as required type is uiviewcontroller. What you can do is use this uiview inside a controller and that viewcontroller's object can be passed as an argument.
you can use observer Pattern
see you can create an observer using NSNotificationCenter:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(notify) name:#"Event" object:nil]; //1
put this in your view controller.
and a selector in view controller like:
-(void)notify
{
//2
HomeView *scrhome=[[HomeView alloc]init];
[self presentViewController:scrhome animated:YES completion:nil];
}
and in your view write under a condition where you want to throw an event:
[[NSNotificationCenter defaultCenter] postNotificationName:#"Event" object:nil]; //3
and you are ready to go.
you may use object:yourobject in place of nil # //1
and
id=sender;
if you want to get some attributes to work on.
it works fine for me.
presentViewController: expects to have view controller as a parameter, you are passing a view here. Have a controller for your view and pass it in presentViewController: animated:
My question is that I have one main UIViewController that allows three other UIViewControllers to be presented through it, but I am wondering if there is a way that once I dismiss one of those other three controllers, can the main UIViewController be notified or tell that it is now appearing due to the dismissal of said controller?
Thank you in advanced!
If your main view controller implements:
(void)dismissViewControllerAnimated:(BOOL)flag completion:(void (^)(void))completion
and the presented view controllers send it that message, you will know since at that time it can query to see what the "presentedViewController" was.
When you dismiss one of the three ViewControllers, you could signal to the main ViewController that they have been dismissed via a NSNotification:
NSDictionary *viewControllerInfo = #{#"ViewControllerClass" : NSStringFromClass([self class])}
[[NSNotificationCenter defaultCenter] postNotificationName:#"ViewControllerDismissed" object:nil userInfo:viewControllerInfo];
And in your main viewController:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(viewControllerDismissed:) name:#"ViewControllerDismissed" object:nil];
And respond with this method:
- (void)viewControllerDismissed:(NSNotification *)notification {
NSDictionary *viewControllerInfo = [notification userInfo];
// Dictionary should be same as the one passed through the noticiation.
}
Additional note: If you are using a UIStoryboard, then you can use an unwind segue.
EDIT: Updated Dictionary to use NSStringFromClass()
I need to call a delegate method on my main view controller ('showDetails:') from a popover view's pushed view (embedded in navigation controller). This is all from a storyboard setup.
The hierarchy is: Main view -> Popover (menu tableview embedded in navigation controller)->Popover secondary View (pushed onto popover navigation controller)
I know how to setup a delegate on the popover using prepareForSegue, but not on an inner view.
How can I call a delegate method on the main view from an inner (pushed) view of a popover?
Here is how I setup the delegate on a popover main view:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"segueSearchResults"]) {
//Dismiss User Popover
[self dismissUserPopover];
SearchResultsViewController *vc = segue.destinationViewController;
vc.searchDelegate = self;
self.searchPopover = [(UIStoryboardPopoverSegue *)segue popoverController];
self.searchPopover.delegate = self;
}
}
Instead Delegate i prefer "NSNotificationCenter" in your case
Add an observer to your ViewController for some action in uiview
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(receiveActionNotification:)
name:#"someActionNotification"
object:nil];
Post Notification from your pushed View in PopOverController
Post Notification and method in your Viewcontroller will be called
[[NSNotificationCenter defaultCenter] postNotificationName:#"someActionNotification" object:self];
At the end Dont forget to remove Observer.
[[NSNotificationCenter defaultCenter] removeObserver:#"someActionNotification"];
When you need to communicate between two view controllers which are far apart in the VC hierarchy, trying to reference one from the other so you can directly call methods on it doesn't work so well -- there's several levels of indirection in between, and it's very fragile if you change your VC hierarchy later.
Look into notifications (NSNotificationCenter) instead; you can have one VC "broadcast" info for another to respond to, regardless of where they are in your app.