Call pushViewController from external class? - cocoa-touch

I'm new to IOS7 and i am struggling to call pushViewController.
First i have UINavigationController as a root view controller i have UICollectionViewController.
In this UICollectionViewController i have also register UICollectionViewCell class
[self.collectionView registerClass:[MYProductCell class]
forCellWithReuseIdentifier:#"product"];
I am trying to pushViewController width detail view based on user action in the cell.
I have NO problems by using
-(void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
NSDictionary *product = self.products[indexPath.row];
TXDetailProductController *detailView = [[TXDetailProductController alloc] init];
detailView.product = product;
[self.navigationController pushViewController:detailView animated:YES];
}
But I want to call pushViewController:detailView from UICollectionViewCell class based on User action.
Can someone give me directions?
Thanks in advance!

You could send a message.
In your viewDidLoad you could set up a message listener:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(pushDetailView:) name:#"pushDetailView" object:nil];
Then add a method:
-(void) pushDetailView:(id)sender
{
// do your pushViewController
}
Then in your UICollectionViewCell when you need to push the view do:
NSNotification* notification = [NSNotification notificationWithName:#"pushDetailView" object:self];
[[NSNotificationCenter defaultCenter] postNotification:notification];
The listener should receive that notification and call the pushDetailView which will push the view. You might need some error checking in there also.
You might need to pass information to the method so you know what to push. You can put the information in an object and send it with the message. Something like:
NSNumber *indexPathRow = [NSNumber numberWithInt: indexPath.row];
NSNotification* notification = [NSNotification notificationWithName:#"pushDetailView" indexPathRow object:indexPathRow];
[[NSNotificationCenter defaultCenter] postNotification:notification];
Then on the receiver class you pull that info out of the notification's object:
-(void) pushDetailView:(NSNotification*)note
{
NSNumber indexPathRow = [note object];
NSDictionary *product = self.products[[indexPathRow intValue]];
// and on with your pushViewController code
}
Again error checking in there also.

Related

nsnotification approach for session inactivity in objective c

In sesssion inactivity implementation for my project. I have created a NSNotification in RootViewController class of project.
- (void)viewDidLoad
{
[super viewDidLoad];
self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithTitle: #"Close"
style: UIBarButtonItemStyleDone
target: self
action: #selector(closeModal)];
UIImage *image = [UIImage imageNamed:#"fidelity_logotype"];
UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, image.size.width, image.size.height)];
[imageView setImage:image];
[self.navigationItem setTitleView:imageView];
self.navigationController.view.backgroundColor = [UIColor fidelityGreen];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(applicationDidTimeout:) name:#"ApplicationTimeout" object:nil];
}
- (void) applicationDidTimeout:(NSNotification *) notif
{
NSLog(#"I m here");
BCDSessionInactivityViewController *sessionView=[[UIStoryboard storyboardWithName:#"Main" bundle:nil] instantiateViewControllerWithIdentifier:#"InactivityViewController"];
sessionView.modalPresentationStyle = UIModalPresentationFormSheet;
sessionView.preferredContentSize = CGSizeMake(838,340);
[[self topViewController] presentViewController:sessionView animated:YES completion:nil];
}
and in logoutviewcontroller, i am removing this observer written below
- (IBAction)logoutbtn:(id)sender
{
NSLog(#"logout is called");
[sessionTimer invalidate];
sessionTimer = nil;
[[BCDTimeManager sharedTimerInstance]stopIdleTimer];
//[self dismissViewControllerAnimated:YES completion:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:#"ApplicationTimeout" object:nil];
[self performSegueWithIdentifier:#"Thankyoupage" sender:self];
}
This is code where i posting the notification.
- (void)idleTimerExceeded {
NSLog(#"idle time exceeded");
[[NSNotificationCenter defaultCenter]
postNotificationName:#"ApplicationTimeout" object:nil];
}
for first time login, it works fine whenever timer exceeds, i post a notification and model view is presesnted perfectly, but once user logs out, after that whenever the notification is posted, selector method is getting called twice
I am pretty sure that notification is getting posted only once.
Should i create notification in every view controller and then remove it when view unloads?
what i am doing wrong here?
You are adding the notification in RootViewController and trying to remove it from LogoutViewController. So that notification observer added to the RootViewController never gets removed. So each time you logout and login, the observer call will get increased by one. For fixing the issue, you need to remove the observer from the RootViewController object.
For fixing the issue you mentioned in your comment,
If I remove the observer in RootViewController , then if timers
exceeds in some other views, and notification observer is not called.
Also, i can't add observer on app delegate because we want timer
notification to be fired only after reaching rootviewController
Write two public methods in AppDelegate
One for adding observer (addObserver)
One for removing observer (removeObserver)
When you reach RootViewController, call the addObserver method for adding the observer
When logout is pressed, call the removeObserver for removing the observer

NSNotification sent once, but is received multiple times

I am communicating between two classes with NSNotificationCenter. My problem is that although I tap a button once (and that button only fires off once) I am unintentionally producing increasing numbers of notifications from only one call to the NSNotificationCenter.
Here is a better explanation of the problem, with code:
My two classes are the mainView class and the Menu class.
When a view in the mainView class is tapped, it launches a view created and governed by the Menu class. This code is called when the mainView is initialized:
menu=[[MyMenu alloc] init];
UITapGestureRecognizer * tap=[[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(onTapped:)];
[tap setNumberOfTapsRequired:1];
[container addGestureRecognizer:tap];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(onChangeItem:) name:#"ItemChange" object:nil];
This Gesture Recognizer fires off this method, also in the mainView class:
- (void) onTapped: (UIGestureRecognizer*) recognizer {
NSLog(#"tap");
[menu displayMenu];
}
This is how the Menu class initializes:
- (MyMenu*) init {
self=[super init];
UICollectionViewFlowLayout * layout=[[UICollectionViewFlowLayout alloc] init];
menuView=[[UICollectionView alloc] initWithFrame:CGRectMake(0, 0, 200, 200) collectionViewLayout:layout];
[menuView setDataSource:self];
[menuView setDelegate:self];
[menuView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:#"cell"];
[menuView setAutoresizesSubviews:YES];
[menuView setAutoresizingMask:UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth];
[menuView setBackgroundColor:[UIColor clearColor]];
[menuView setIndicatorStyle:UIScrollViewIndicatorStyleWhite];
return self;
}
And this is the displayMenu method inside the Menu class:
- (void) displayMenu {
[viewForMenu addSubview:menuView];
}
The Menu class also has a clearMenu method:
- (void) clearMenu {
[menuView removeFromSuperview];
}
This is the code for each cell in the UICollectionView, contained within my Menu class:
- (UICollectionViewCell*) collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
UICollectionViewCell * cell=[collectionView dequeueReusableCellWithReuseIdentifier:#"cell" forIndexPath:indexPath];
[cell setTag:indexPath.row];
UITapGestureRecognizer * tap=[[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(onButtonTapped:)];
[tap setNumberOfTapsRequired:1];
[cell addGestureRecognizer:tap];
NSLog(#"button tapped : %d",indexPath.row);
return cell;
}
This calls the onButtonTapped: method, also within my Menu class:
- (void) onButtonTapped:(UIGestureRecognizer*) recognizer {
NSInteger buttonTapped=[[recognizer view] tag];
[[NSNotificationCenter defaultCenter] postNotificationName:#"ItemChange" object:nil userInfo:#{#"selected":#(buttonTapped)}];
[self clearMenu];
}
This notification is picked up by my mainView class with this code:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(onChangeItem:) name:#"ItemChange" object:nil];
This calls the onChangeItem: method, inside my mainView class:
- (void) onChangeItem: (NSNotification*) notification {
NSLog(#"change item to %d",[[[notification userInfo] objectForKey:#"clock"] intValue]);
}
So that's the code.
OK, here's the problem: the first time the menu displays I get this in my log:
...[43023:11f03] tap
...[43023:11f03] button tapped : 1
...[43023:11f03] change item to 1
And this is fine, this is what I expect. However second time around I get this:
...[43023:11f03] tap
...[43023:11f03] button tapped : 1
...[43023:11f03] change item to 1
...[43023:11f03] change item to 1
Third time around I get this:
...[43023:11f03] tap
...[43023:11f03] button tapped : 1
...[43023:11f03] change item to 1
...[43023:11f03] change item to 1
...[43023:11f03] change item to 1
...[43023:11f03] change item to 1
And so on. Each successive tap on a menu item doubles the amount of notification calls.
To begin with I thought I was adding multiple views, and thus resulting in multiple button taps, and therefore multiple notifications calls.
However as you can see from my logs, this is not the case. The buttons are only receiving 1 tap event - this is firing off only 1 notification - but receiving class gets sent multiple notifications.
Can anyone explain this to me?
Sorry for the lengthy post!
Well, I am assuming that [[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(onChangeItem:) name:#"ItemChange" object:nil]; are being added more than once.
I like to remove any potential observer before adding an observer, like so:
[[NSNotificationCenter defaultCenter] removeObserver:self name:#"ItemChange" object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(onChangeItem:) name:#"ItemChange" object:nil];
That way there will ever only be one observer callback.
Issue: I faced same problem, observer was calling twice, sometimes thrice.
Scenario
A user taps logout button
HomeViewController was dismissed and LoginViewController screen was presented
When user signed in again for second time
Observer was called twice (sometimes thrice)
The issue was [[NSNotificationCenter defaultCenter] removeObserver:self]; in dealloc method of my HomeViewController was not called at all, which actually removes an object from the notification center all together.
ℹ️ dealloc is an Objective-C selector that is sent by the
Objective-C runtime to an object when the object is no longer owned by
any part of the application.
Solution: Made your own method dispose and call it when user tap logout.
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
// ....
}
- (void)dispose {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (void)logoutTapped {
[self dispose];
// ....
}

UINavigationController Title Overlap

I am using a UINavigationController and pushing/popping UIViewControllers onto it. In some instances I am attempting to pop to the root view controller and then push a view controller after a short delay (0.1f).
My push code for the Message View Controller is as follows. My app fires two notifications. The first to select a tab and the second to push the correct view controller onto the stack of that tab.
//user taps a button and the app needs to switch tab and push the correct viewController
//onto the tab. I have tried setting pop == NO to avoid a 'double pop' but I still get
//overlapped titles
-(IBAction)messages:(id)sender {
NSDictionary* dictionary = [NSDictionary dictionaryWithObjects:[NSArray arrayWithObjects:[NSNumber numberWithInt:4], [NSNumber numberWithBool:YES] , nil] forKeys:[NSArray arrayWithObjects:#"tab",#"pop", nil]];
[[NSNotificationCenter defaultCenter] postNotificationName:kAutoSelectTab object:dictionary];
[[NSNotificationCenter defaultCenter] performSelector:#selector(postNotificationName:object:) withObject:kMessages afterDelay:0.1f];
}
//responds to the first notification
-(void)autoSelectTab:(NSNotification*)notification {
NSDictionary* dictionary = (NSDictionary*)[notification object];
int tab = [[dictionary objectForKey:#"tab"] intValue];
BOOL pop = [[dictionary objectForKey:#"pop"] boolValue];
[self.tabBarController setSelectedIndex:tab];
UIViewController* vc = [[self.tabBarController childViewControllers] objectAtIndex:tab];
PSLogDebug(#"Selecting tab:%#",[vc class]);
[self tabBarController:self.tabBarController didSelectViewController:vc];
if (pop == YES) {
if ([vc isKindOfClass:[UINavigationController class]]) {
[(UINavigationController*)vc popToRootViewControllerAnimated:YES];
}
}
}
//responds to the second notification
-(IBAction)messages:(id)sender {
[self.navigationController popToRootViewControllerAnimated:NO];
MessagesViewController* vc = [[MessagesViewController alloc] init];
[self.navigationController pushViewController:vc animated:YES];
[vc release];
}
Functionally the views appear to pop and push correctly BUT the titles do not pop and each new title is overlaid atop the old one.
I set the titles for each of the view controllers in viewDidLoad
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
self.navigationItem.title = #"More";
}
When I don't attempt the pop to root followed by the delay followed by the push - the titles and views behave as expected with no overlapping occurring.
Example Images from screenshots
I've had a good dig around stack overflow but I can't see any questions which describe the same issue as the one I am having.
Qn.1: Is there something fundamentally incorrect with the popToRoot, Delay, push View approach?
Qn.2: If anyone out there has seen this kind of behaviour before, how did you resolve it?
Increasing the delay from 0.1f to 0.5f fixed the problem
Change
[[NSNotificationCenter defaultCenter] performSelector:#selector(postNotificationName:object:) withObject:kMessages afterDelay:0.1f];
to
[[NSNotificationCenter defaultCenter] performSelector:#selector(postNotificationName:object:) withObject:kMessages afterDelay:0.5f];

Delete An NSMutableArray from another Class in C-Objective?

I need help with delete an array with a button from the SecondViewController in the view controller class.
At the moment I have this:
- (IBAction)deleteArrayFromSeconViewController:(id)sender // is the Button which should
// delete the array
{
self.textLabel2.text = #""; // this work fine
ViewController *vc = [[ViewController alloc]init];
[vc.textViewArray removeAllObjects]; // do not remove the objects?
}
What should I do to overtake an order from the SecondViewControllerClass in the ViewControllerClass?
I tried also this in the SecondViewControllerClass:
- (IBAction)deleteArrayFromSeconViewController:(id)sender
{
self.textLabel2.text = #"";
ViewController *vc = [[ViewController alloc]init];
[vc deleteTheArray];
}
to call this function in the ViewControllerClass:
- (void) deleteTheArray
{
[textViewArray removeAllObjects];
}
I'm not at all sure that this is the best way to do this, but you could post a custom NSNotification in your first view controller, and then pick it up in your second. Code Example:
//Post in your first view controller when wanting to delete the array
[[NSNotificationCenter defaultCenter] postNotificationName:#"DeleteArrayNotification" object:nil userInfo:nil /*include any items your reciever might wish to access*/];
and then in your second view controller, you could add yourself as an observer in the -(void)awakeFromNib Method. Put in your awake from nib method:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(deleteArrayNotification:) name:#"DeleteArrayNotification" object:nil];
And then implement the deleteArrayNotification: method:
- (void)deleteArrayNotification:(NSNotification *)notification {
[array removeAllObjects];
[array release]; //Delete this line if your project is using ARC
}
I am highly doubtful that this is good coding practice to implement NSNotification like this, but I thought it might be of use!
More information can be found under the Apple developer documentation, under NSNotification and NSNotificationCenter.
Hope I can help!
Ben

How to reload the tableView after dismissing a NavigationController in uisplitviewcontroller?

I'm using a UISplitviewController as a template.
action for edit button:
newExViewController *editWindow =[[newExViewController alloc]initWithNibName:#"newExViewController" bundle:nil];
UINavigationController *navBar=[[UINavigationController alloc]initWithRootViewController:editWindow];
navBar.modalPresentationStyle = UIModalPresentationFormSheet;
navBar.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentModalViewController:navBar animated:YES];
[navBar release];
[editWindow release];
navBar has a UIBarButton for saveButton. This is called when you press SaveButton
[self dismissModalViewControllerAnimated:YES];
now is the problem:
any idea how to reload the data for both the main NavigationConteroller and the detailViewController when the modalView is dismissed??
I have no clue
thnx
You should look into NSNotificationCenter. In your view with the UITableView, create the notification listener. Then in the view that dismisses, call that notification.
To be more specific, the Notification will call a method that should contain reloadData.
Example
The following should go with the UITableView you want to reload:
This could go along with your [self dismissModalViewControllerAnimated:YES];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(someMethodToReloadTable) name:#"reloadTable" object:nil];
This is how you will call the notification center to reload the table:
[[NSNotificationCenter defaultCenter] postNotificationName:#"reloadTable" object:self];
Example of the notification method:
- (void)someMethodToReloadTable:(NSNotification *)notification
{
[myTableView reloadData];
}
And don't forget to remove the notificaiton observer:
-(void)viewDidUnload
{
[[NSNotificationCenter defaultCenter] removeObserver:self name:#"reloadTable" object:nil];
}
In controllers, that contains view you want to reload, you should decline following method which will be called when the modalView will be dismissed (or when controller's main view will be first time loaded):
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
// here you can reload needful views, for example, tableView:
[tableView reloadData];
}