my app is nav based. in which i have a main tableView which shows feed items in cells. when a cell is clicked, a detailview is created which shows details of that feed item. i am working with push notifications now. when action button in notification is clicked,
(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo:
is called. how can i implement that method that if action button in notification is clicked. it should parse the feed again, reload the tableview, creates the latest feed items detailview and push it in navigational stack. i tried some code but it didn't work. here is the code i wrote.
in AppDelegate:
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
RootViewController *controller = [[RootViewController alloc] init];
[controller newFeedItem];
}
in RootViewController:
- (void)newFeedItem
{
spinner = [[UIActivityIndicatorView alloc]initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
spinner.frame=CGRectMake(130, 170, 50, 50);
[self.view addSubview:spinner];
[spinner startAnimating];
[self performSelector:#selector(doStuff) withObject:nil afterDelay:0.01];
}
-(void)doStuff
{
[[self stories] removeAllObjects];
[self startParsing];
[self.tableView reloadData];
// create detailview and push it in navigational stack
[spinner stopAnimating];
[spinner release];
}
but activity indicator is not appearing and tableView is also not reloading. Why is it happening so? Thanx in advance
Not quite sure about your program flow but I assume you show the rootViewController at program start and at a later time you receive a remote notification.
In your code (in didReceiveRemoteNotification) you are instantiating a new rootViewController and that new one will be different from the one already on the screen. Following your approach you might want alloc the controller once and keep it around for later when the remote notification arrives.
Personally I would suggest to use local notifications and to fire a local notification in didReceiveRemoteNotification and catch it in the rootViewController. That would ensure the currently active instance of the controller responds.
Also not sure why the spinner not shows, for a try call it from the viewDidAppear, just it see it it works at all and if the problem is with the reaction to the remote notification. And use some breakpoints.
Edit with respect to your comment:
in the interface define
RootViewController *controller
in the implementation you alloc the controller (for example in appDidFininshLaunching)
if (controller == nil) controller = [[RootViewController alloc] init]
in didReceiveRemoteNotification you can then do
[controller newFeedItem];
without alloc'ing it again and you can refer to the same controller. Don't forget to release it in -(void)dealloc
Related
Hi I have three views and I would like to achieve something that doesn't work. I have a main view if user presses a certain button the code checks if he is logged or not:
if yes he is sent directly to view B if not first he goes to login view.
After successfull login I have this code to go to view b:
incidencias =[[MisIncidencias alloc]
initWithNibName:#"MisIncidencias"
bundle:nil];
[self.view addSubview:incidencias.view];
the thing is I would like to get rid of the login view because it shows there underneath plus if user clicks back it goes back to login but if I add:
[self.view removeFromSuperview];
either before or after [self.view addSubview:incidencias.view], I just get redirected to the main view;
I don't know if I explained myself clearly but for example in Android you can just call finish and then call next activity and the login activity disappears but here in iphone I don't know what to do.
I have found another solution is to add both views one after another but it doesn't really work well:
incidencias=[[MisIncidencias alloc]
initWithNibName:#"MisIncidencias"
bundle:nil
];
[self.view addSubview:incidencias.view];
login=[[LoginViewController alloc]
initWithNibName:#"LoginViewController"
bundle:nil];
[self.view addSubview:login.view];
it doesn't work well because incidencias starts and doesn't wait for login to finish.
thanks
EDIT: thanks to beOn I have modified my code adding the protocol:
LoginViewControllerDelegate
and this method inside viewController:
- (void)loginSucceededFromController:(LoginViewController*)viewController {
[viewController.view removeFromSuperview];
incidencias =[[MisIncidencias alloc]
initWithNibName:#"MisIncidencias"
bundle:nil];
[self.view addSubview:incidencias.view];
}
in LoginViewController I have
- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex: (NSInteger)buttonIndex{
if(self.delegate)
[self.delegate loginSucceededFromController:self]
}
it gets an error:
Semantic Issue: Property 'delegate' not found on object of type 'LoginViewController *'
if login is successful the user sees an alert and once he clicks on ok is when the method above gets called.
what else should I add? I am beginning with iphone and I don't understand very well what is delegate (I come from java)
Ah, okay, this ain't so bad. Here's the first solution that comes to mind:
Step 1. Create a delegate protocol for your login view.
#protocol LoginViewControllerDelegate <NSObject>
#required
- (void)loginSucceededFromController:(LoginViewController*)viewController;
#end
Step 2. Implement the protocol in your main view controller
- (void)loginSucceededFromController:(LoginViewController*)viewController {
// TODO: we'll put something here in a second
}
Step 3. Call the delegate method from your login view on successful login
if (loginSuccess && self.delegate) {
[self.delegate loginSucceededFromController:self]
}
Step 4. Dismiss the login view and present the new view from the main view controller using the code you already have:
- (void)loginSucceededFromController:(LoginViewController*)viewController {
[viewController.view removeFromSuperview];
incidencias =[[MisIncidencias alloc]
initWithNibName:#"MisIncidencias"
bundle:nil];
[self.view addSubview:incidencias.view];
}
Hopefully that clears things up some. The reason you were having trouble is that you were either adding a subview to a view, then immediately removing the view, or removing the view, then adding a subview to it. In the code above, you call the view's controller's delegate, and the delegate, which happens to own the superview of the view, first removes the view, then adds a newView (for lack of a better term) to the superview. Since the superview was never removed, it's able to show your newView.
You have to take BOOL which one can access through out application like global
like extern BOOL login; now once you login set to YES. now check when
if(login == YES){
incidencias=[[MisIncidencias alloc]
initWithNibName:#"MisIncidencias"
bundle:nil
];
[self.view addSubview:incidencias.view];
}
else{
login=[[LoginViewController alloc]
initWithNibName:#"LoginViewController"
bundle:nil];
[self.view addSubview:login.view];
}
If you want something working right away, and you are using uinavigationcontroller... then u can possibly make use of
- (void)setViewControllers:(NSArray *)viewControllers animated:(BOOL)animated
just get a mutable copy of the self.navigationController.viewcontrollers array, pop out the last element, which will be the login screen and push in the new screen where you are planning to move screen b.. and pass the array to this function.. and you are now safe!
I'm using the following code to display the previous view when a user is clicking on a button
[self.navigationController popViewControllerAnimated:YES];
In the previous view, I overwrite viewWillAppear to initialized few things. However, it seems like viewWillAppear is not being called. I put NSLog in viewDidload, viewWillAppear, viewDidAppear and only viewDidAppear is being called. Is this normal behavior? If yes, what event should I override so I can do my initialization? Thank you.
As requested -viewWillAppear for the previous view
- (void)viewWillAppear:(BOOL)animated{
NSLog(#"ViewWillAppear");
//[[GameStore defaultStore] resetGame];
[self setHangmanImage];
NSLog([[[GameStore defaultStore] selectedList] label]);
[labelListName setText:[NSString stringWithFormat:#"List Name: %#", [[[GameStore defaultStore] selectedList] label]]];
[labelCurrentIndex setHidden:YES];
[labelCurrentWord setHidden:YES];
[[self navigationController] setNavigationBarHidden:NO];
[FlurryAnalytics logEvent:#"GameViewController - viewWillAppear"];
[self getNewQuestion];
NSLog(#"ViewWillAppear finish");
[super viewWillAppear:YES];
}
I setup the UINavigationalController in the app delegate using the following code
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
HomeViewController *hv = [[HomeViewController alloc] init];
UINavigationController *navController = [[UINavigationController alloc]
initWithRootViewController:hv];
// You can now release the itemsViewController here,
// UINavigationController will retain it
[hv release];
// Place navigation controller's view in the window hierarchy
[[self window] setRootViewController:navController];
[navController release];
// Override point for customization after application launch.
[self.window makeKeyAndVisible];
return YES;
}
UPDATE
I don't know what happened but last night after trying to run the app one more time in the simulator and its still having this issue, I decided to save everything and shut my computer down since it was getting late.
This morning I turned my computer back on opened up xcode, clean the project and build and run it and I the problem is fixed and -viewWillAppear is called. I didn't change anything and its working. I added NSLog in -willShowView and its not getting called. I don't know why all of a sudden viewWillAppear is being called.
Make sure your navigation controller's delegate is set and then use this function to call viewWillAppear in the class whose viewWillAppear you want to call:
- (void)navigationController:(UINavigationController *)navigationController
willShowViewController:(UIViewController *)viewController animated:(BOOL)animated
{
[self viewWillAppear:animated];
}
I've just hit a problem very much the same and after some testing discovered that calling popViewControllerAnimated: in a block (from a network response in AFNetworking) viewDidAppear isn't called in the parent view.
The solution that worked for me here was to call it in the main thread instead.
dispatch_async(dispatch_get_main_queue(), ^{
// If not called on the main thread then the UI doesn't invoke the parent view's viewDidAppear
[self.navigationController popViewControllerAnimated:YES];
});
I have just hit this problem as well the root cause of this problem is because I put self.navigationController.delegate = self on viewDidLoad. My solution is to put the delegation on viewWillAppear :
- (void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
self.navigationController.delegate = self;
}
After that popViewController will hit viewWillAppear.
I have a UIViewController called LoginViewController. The UIViewController has two UITextField, username and password. This is displayed as a modalViewController when the first time the app is launched and there isn't any credentials in the keychain. When the user clicks on the login button the keyboard that is presented is dismissed. The code is:
- (IBAction) loginClick: (UIButton *) sender
{
if ([username isFirstResponder]){
[username resignFirstResponder];
}
if ([password isFirstResponder]){
[password resignFirstResponder];
}
[RKObjectManager sharedManager].client.username = username.text;
[RKObjectManager sharedManager].client.password = password.text;
[progressLock lock];
[progressLock unlockWithCondition:0];
[HUD showWhileExecuting:#selector(myTask) onTarget:self withObject:nil animated:YES];
[[RKObjectManager sharedManager] loadObjectsAtResourcePath:#"/account/verify.json" objectClass:[Login class] delegate: self];
}
Now inside the app there is an options button. When this is click it will present a modalViewController which has a logout button in it. Clicking on this logout button will present the LoginViewController again. The code is:
- (IBAction) logout:(id)sender
{
NSUserDefaults *standardDefaults = [NSUserDefaults standardUserDefaults];
NSString * username = [standardDefaults stringForKey:#"kApplicationUserNameKey"];
NSError * error = nil;
[standardDefaults removeObjectForKey:#"kApplicationUserNameKey"];
[SFHFKeychainUtils deleteItemForUsername:username andServiceName:#"convore" error:&error];
LoginViewController* lvc = [[LoginViewController alloc] init];
lvc.delegate = self;
[self.mgvc.groups removeAllObjects];
[self.mgvc.table reloadData];
Topic * topic = [Topic object];
topic.tid = [NSNumber numberWithInt:-2];
self.mgvc.detailViewController.topic = topic;
self.mgvc.detailViewController.detailItem = topic.tid;
[self presentModalViewController:lvc animated:YES];
[lvc release];
}
What's weird is that I can't seem to dismiss the keyboard when I click on the login button this time. Why is this? Is it because as of now I am displaying the LoginViewController from a modalviewcontroller? How do I solve this weird issue?
I don't know whether the fact that you can't dismiss the keyboard is due to presenting the login in a modal view, nor do I see anything evidently wrong with your code.
I would suggest you to change your flow and make:
the logout button dismiss your first modal view and,
your login view be displayed as a normal view, once the modal view disappears.
Explicitly, you can do 2 either by one of several means:
trying and pushing the login view controller on your navigation controller (if you have one);
selecting it in a tab bar controller (if you use one);
your modal view is shown above a view; when the modal is dismissed, that view appears again, and so the viewWillAppear selector of its view controller is called; that's the place where you can check if you are logged in or out and show the login, if necessary; if you don't know how to detect if your are in or out, you can always set a flag in the view controller when you dismiss the logout modal view.
(after reading your comment: modal view is not full screen, so viewWillAppear is not called: you can either send viewWillAppear yourself, or send a different, custom message to your superordinate view controller so that it knows that the login view should be displayed (and possibly refresh the view).
EDIT: try something like this (this requires that you call your own selector, not viewWillAppear):
[baseNonModalViewController performSelector:#selector(yourSelector) withObject:nil afterDelay:0.0];
Executing performSelector with a 0.0 delay, has only the effect of scheduling yourSelector for execution on the main loop. This way, first the modal will be completely dismissed, then the login view will be displayed.
I have been figuring out this since yesterday but have not got that correct yet.
I have added the modalviewcontroller for my loading view controller on top of my tab bar controller and it works fine.
Added the code in app Delegate:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions: (NSDictionary *)launchOptions {
[navController.navigationBar setTintColor:[UIColor blackColor]];
[window addSubview:rootController.view];
[window makeKeyAndVisible];
LoadingViewController *lvc = [[LoadingViewController alloc] initWithNibName:#"LoadingView" bundle:nil];
// Delegate added here
lvc.loadingDelegate = self;
[rootController presentModalViewController:lvc animated:YES];
[self URL];
[lvc release];
return TRUE;
}
Now I do my parsing and when its done I call the following code in different view name XMLParsingView.m where the parsing got over.
- (void)handleLoadedApps
{
LoadingViewController *loading = [[[LoadingViewController alloc] init] autorelease];
//delegating to let the load view controller know to dimiss itself by defining disappear method in protocol
[loading.loadingDelegate disappear];
}
and in loading view controller I have method which calls dismissModalViewControlAnimated:
-(void)disappear{
[activity stopAnimating];
[activity removeFromSuperview];
[self removeFromSuperview];
[self dismissModalViewControllerAnimated:YES];
}
But for some reason it will never remove the view and not load it back to my tab bar controller.
Really need help here if any one have come across such issues.
Sagos
In your code you seem to create, without a nib, a new LoadingViewController and immediately go and dismiss it. In your app delegate you create your first loadingViewController with a nib, present it modally on rootController and then release it. Since you want to dismiss it outside your app delegate you have
3 choices, (hardest to fastest and most sane)
a) Key-Value-Observing on a property of XMLParsingView from LoadingViewController to remove itself when the task finishes.
b) Use delegation to inform the LoadingViewController when the task finishes to dismiss itself.
c) Fetch your rootController from your [[UIApplication sharedApplication] delegate], which means you must expose rootController as a property or through a method, and make rootController dismiss your modal.
You need to call dismissModalViewControllerAnimated on the rootViewController, not the loading view controller.
I have a button inside the content of a UIPopoverController. This button runs a method called myAction.
MyAction has the form
- (void) myAction:(id)sender
so, myAction receives the id of the caller button.
Now, inside this method I would like to dismiss the UIPopoverController, but the only thing I have is the ID of the caller button. Remember that the button is inside the UIPopoverController.
Is there a way to discover the ID of the UIPopoverController, given the button ID I already have?
thanks.
Unfortunately no. At least, not within the standard practices. You might be able to travel up the responder stack to find it, but it's a hack, it's buggy, and it's really, really messy.
If you want to dismiss a popover by pushing a button, some place relevant should keep a reference to the popover. Usually that would be the owner of the popover (not the controller showed within the popover). When the button is pressed, it can send a message to the owner controller, which can then dismiss the popover.
You might be tempted to have the controller displayed inside of the popover be the owner of its own popover, but coding this way is brittle, can get messy (again), and may result in retain loops so that neither ever gets released.
You can access the presenting popoverController by accessing "popoverController" with KVC.
[[self valueForKey:#"popoverController"] dismissPopoverAnimated:YES]
I have this working, and I do not think it is a hack. I have a standard split view iPad app. I then added a method on my detail controller (the owner of the pop over) to handle the dismissal.
On the standard split view architechture, both the root and detail view controllers are available via the app delegate. So I bound a button click inside the pop over to call a method which gets the app delegate. From there I call the method on the detail controller to dismiss the pop over.
This is the code for the method on the View Controller that is displayed inside the popover:
- (void) exitView: (id)sender {
MyAppDelegate *appDelegate = (MyAppDelegate *)[[UIApplication sharedApplication] delegate];
[appDelegate.detailViewController exitDrill];
}
Then the simple method to dismiss on the Detail View Controller:
- (void) exitDrill {
if(dtController != nil){
[dtController dismissPopoverAnimated: YES];
[dtController release];
}
}
I like the ability to do this because it give me a way to show a user how they can exit a pop over. This may not be necessary in future versions of the app; for right now, while this paradigm is still new to the platform, I prefer to let the users gexit a display in a couple fo different ways to make sure I minimize frustration.
As Ed Marty already wrote
If you want to dismiss a popover by pushing a button, some place relevant should keep a reference to the popover
This is very true; however, when showing a UIPopoverController, the class opening the popovercontroller keeps this resource already. So, what you could do is to use this class as the delegate class for your Popover Controller.
To do so, you could do the following, which I use in my code.
In the class opening the popover, this is my code:
- (void)showInformationForView:(Booking*)booking frame:(CGRect)rect
{
BookingDetailsViewController *bookingView = [[BookingDetailsViewController alloc] initWithStyle:UITableViewStyleGrouped booking:booking];
[bookingView setDelegate:self];
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:bookingView];
self.popController = [[UIPopoverController alloc] initWithContentViewController:navController];
[self.popController setDelegate:self];
[self.popController setPopoverContentSize:CGSizeMake(320, 320)];
rect.size.width = 0;
[self.popController presentPopoverFromRect:rect inView:self.view permittedArrowDirections:UIPopoverArrowDirectionLeft animated:YES];
}
- (void)dismissPopoverAnimated:(BOOL)animated
{
[self.popController dismissPopoverAnimated:animated];
}
So what I am doing here is creating a UINavigationController and setting a BookingDetailsViewController as its rootViewController. Then I am also adding the current class as delegate to this BookingDetailsViewController.
The second thing I added is a dismissal method called dismissPopoverAnimated:animated.
In my BookingDetailsViewController.h I added the following code:
[...]
#property (nonatomic, strong) id delegate;
[...]
And in my BookingDetailsViewController.m I added this code:
[...]
#synthesize delegate = _delegate;
- (void)viewDidLoad
{
UIBarButtonItem *closeButton = [[UIBarButtonItem alloc] initWithTitle:#"Close" style:UIBarButtonItemStylePlain target:self action:#selector(closeView)];
[self.navigationItem setRightBarButtonItem:closeButton];
[super viewDidLoad];
}
- (void)closeView
{
if ([self.delegate respondsToSelector:#selector(dismissPopoverAnimated:)]) {
[self.delegate dismissPopoverAnimated:YES];
}
else {
NSLog(#"Cannot close the view, nu such dismiss method");
}
}
[...]
What happens is that when the "Close" button in the UINavigationController is pressed, the method closeView is called. This method check if the delegate responds to dismissPopoverAnimated:animated and if so, it calls it. If it does not respond to this method it will show a log message and do nothing more (so it wont crash).
I have written my code using ARC, hence there is no memory management.
I hope this helped you.