I'm trying to have a Terms of Service modal view display when my application launches, when the settings preference indicates the user hasn't accepted the terms of use.
So in my appDelegate in the ApplicationDidFinishLaunchingWithOptions, I have this code:
if (TOSAcceptedPrefValue) { //has not been accepted
// Create the root view controller for the navigation controller
TermsOfServiceController *termsOfServiceController = [[TermsOfServiceController alloc]
initWithNibName:#"TermsOfServiceController" bundle:nil];
// Create the navigation controller and present it modally.
UINavigationController *navigationController = [[UINavigationController alloc]
initWithRootViewController:termsOfServiceController];
termsOfServiceController.delegate = self;
navigationController.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
[self presentModalViewController:navigationController animated:YES];
[navigationController release];
[TermsOfServiceController release];
NSLog(#"1");
}
However, Xcode is indicating that termsOfServiceController.delegate = self is "Assigning to 'id' from imcompatible type 'MyAppAppDelegate *' ".
I think I fully implement the modal protocol in my AppDelegate header:
#protocol TOSModalViewDelegate
- (void)didAcceptTermsOfService:(NSString *)message;
- (void)didRejectTermsOfService:(NSString *)message;
#end
#interface MyAppAppDelegate : NSObject <UIApplicationDelegate, TOSModalViewDelegate> ...
and in the modalview header:
#protocol ModalViewDelegate ;
#interface TermsOfServiceController : UIViewController {
id<ModalViewDelegate> delegate; ...
...
#property (nonatomic, assign) id<ModalViewDelegate> delegate;
and I synthesize it in the modalview implemenation file.
Per this example, I moved my code in the AppDelegate.m file to after the window get instantiated but still have the warning from Xcode.
The warning results in an app crash with this error:
2011-09-05 08:34:12.237 MyApp[4416:207] TOSAcceptedPrefValue = 0
2011-09-05 08:34:13.732 MyApp[4416:207] displayWelcomeScreenPrefValue = 0
2011-09-05 08:34:42.889 MyApp[4416:207] -[MyAppAppDelegate presentModalViewController:animated:]: unrecognized selector sent to instance 0x552b430
2011-09-05 08:34:42.892 MyApp[4416:207] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[MyAppAppDelegate presentModalViewController:animated:]: unrecognized selector sent to instance 0x552b430'
So my question is, is it possible to display a modal view from the appdelegate and if so, what should I change to make it happen.
Thanks for your help
The error is because MyAppAppDelegate isn't a UIViewController subclass, and therefore can't handle presentModalViewController:animated:.
So no, your app delegate can't present a modalViewController, it has to be presented by a real view controller. This isn't hard to do, just create one that shows your terms in viewDidLoad, and responds appropriately when the modal controller exits, to do whatever you need to do next.
Related
I've a TableViewController in which I'm saving the selected cells in an NSMUtableArray. After selecting these cells user clicks on a confirm button and in this button action I'm trying to pass that NSMUtableArray so that I can display it in another viewController tableView
#import <UIKit/UIKit.h>
#protocol SelectedDXDelegate;
#interface AddDXTableViewController : UITableViewController
#property (nonatomic, strong) NSMutableArray *favDXArray;
#property (strong, nonatomic) UISearchController *searchController;
#property (nonatomic, strong) DX *AddEditDX;
#property (weak) id<SelectedDXDelegate> delegate;
- (IBAction)confirmPressed:(id)sender;
#end
#protocol SelectedDXDelegate <NSObject>
#required
-(void)getSelectedDX:(NSMutableArray *)DXselected;
#end
So when confirm button is pressed
- (IBAction)confirmPressed:(id)sender {
[self.delegate getSelectedDX:selectedDX];
[self.navigationController popViewControllerAnimated:YES];
}
it gets me to the
-(void)getSelectedDX:(NSMutableArray *)DXselected
{
myDXSelected = DXselected;
}
But it crashes the app here at reloadData in
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self.DXTableView reloadData];
}
You seem to have mixed up which view controller should be the delegate and which is the delegator. Also, you are just allocating a new instance of the AddDXTableViewController and assigning this as the delegate. This won't work; you need to have the existing instance of your view controller set as the delegate.
From what I can tell from your question, it is actually an instance of DXViewController that is to be the delegate of AddDXTableViewController
Presumably in DXViewController you have some code something like:
AddDXViewController *newViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"AddDXViewController"];
[self.navigationController pushViewController:newViewController animated:YES];
What you need to do is set your delegate at this point:
AddDXViewController *newViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"AddDXViewController"];
newViewController.delegate = self;
[self.navigationController pushViewController:newViewController animated:YES];
Having said all of that, since you are using a storyboard, a delegate is probably an unnecessarily complicated way of achieving your requirement; You can use a segue to move between the first and second view controller and an unwind segue to return back to the first. You can then implement prepareForSegue in the second view controller and use that to provide the array back to the first view controller
You have to make the UIViewController added in storyboard to AddDXTableViewController type in the identity inspector tab in story board.
See here the image
Here you can see the class type is ViewController, click on the dropdown and select the type to AddDXTableViewController
then type cast the viewController to AddDXTableViewController. As per my guess the instantiateViewControllerWithIdentifier: returns UIViewController which does not have delegate may cause the crash
AddDXTableViewController *addDXTVC = (AddDXTableViewController *)[self.storyboard instantiateViewControllerWithIdentifier:#"DXViewController"];
addDXTVC.delegate = self;
Let me know If it works
Short version of question: When I pop a custom UIViewController that is parent to a UIPageViewController off a navigation stack, the app crashes - why and what's the workaround? Longer description below:
I have a login screen that then pushes on top a customer UIViewController which itself is the parent of a UIPageViewController that presents a UIPageView in a section of the parent controller's view. Let's call this custom UIViewController B, which is parent to a UIPageViewController as I said. Here are the parent view controller's properties as declared in the .h file for view controller B:
#interface bjViewController : UIPageViewController <UIPageViewControllerDelegate, UIPageViewControllerDataSource>
#property (strong, nonatomic) UIPageViewController *pageController;
#property (assign, nonatomic) NSInteger index;
#property (strong, nonatomic) UILabel *screenNumber;
#property UIPageControl *childPageControl;
#end
And here is the troublesome code in viewDidLoad for view controller B:
UIViewController *initialViewController = [self viewControllerAtIndex:0];
NSArray *viewControllers = [NSArray arrayWithObject:initialViewController];
[self.pageController setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionForward animated:NO completion:nil];
[self addChildViewController:self.pageController];
[[self view] addSubview:[self.pageController view]];
If I comment this out, I can pop this view controller off the UINavigationController stack without a problem back to UIViewController A, but with these lines in, the app crashes when I navigate back to view controller A. However if I navigate back to A after popping something else on instead of B, there is no problem, so I'm fairly sure the crash has something to do with these lines I've pasted above.
What happens with the crash is that the app does re-present the view from A but then gives an NSException error without indicating where it's crashing (and I have NSLog in all my functions for that view controller - none of those functions are being executed).
Here's the error I get. Yes I do have a breakpoint for all exceptions but it just dumps to the #implementation line in the .m file for B, even as it's already presenting A. Here's the error it gives:
2015-04-21 10:08:14.958 [953:110883] -[UIView invalidatePageViewController]: unrecognized selector sent to instance 0x1655e940
2015-04-21 10:08:49.375 [953:110883] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[UIView invalidatePageViewController]: unrecognized selector sent to instance 0x1655e940'
*** First throw call stack:
(0x26a0845f 0x3489ec8b 0x26a0d879 0x26a0b797 0x2693d008 0x2a4a08fb 0x2690effd 0x2692614d 0x348b8d5f 0x2a091e9d 0x2a4a0981 0x53e95 0x3489dda1 0x348a75f7 0x348a761b 0x2a225435 0x2a0921e9 0x2a4a0981 0x2a181cd7 0x34e45ae1 0x29f58e75 0x348b8d5f 0x2690effd 0x26928df3 0x348b8d5f 0x348b91a9 0x2691a139 0x269ccd1f 0x2691a3b1 0x2691a1c3 0x2df47201 0x29f8443d 0x7379d 0x34e2aaaf)
libc++abi.dylib: termina
Has anyone run into something like this? Can you tell me what I'm doing wrong? I understand the basics of NSException and NSInvalidArgumentException, but I don't even know what's being called/incorrectly called.
My app crashing due to unrecognized selector sent with segue. I know it's a common question in stackoverflow. I tried all the solution but could not get through this. I think i'm missing something. My current project throws this exception, but a new project with this same code does not throw any exception. Why ? I tried like below code snippet to pass data from FirstViewController to DetailViewController using segue.
In my FirstViewController.h file :
#import <UIKit/UIKit.h>
#import "DetailViewController.h"
#interface FirstViewController : UIViewController<UITableViewDataSource, UITableViewDelegate>
#end
In my FirstViewController.m file :
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
//if ([segue.identifier isEqualToString:#"detail"]) {
DetailViewController *vc = (DetailViewController *)[segue destinationViewController];
vc.nameStr = #"Nuibb";
//}
}
In my DetailViewController.h file :
#import <UIKit/UIKit.h>
#interface DetailViewController : UIViewController
#property (nonatomic, strong) NSString *nameStr;
#end
In my DetailViewController.m file :
#import "DetailViewController.h"
#interface DetailViewController ()
#property (weak, nonatomic) IBOutlet UILabel *label;
#end
#implementation DetailViewController
#synthesize nameStr;
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.label.text = nameStr;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
And i'm getting this error message in log -
2015-03-11 10:27:24.145 bdipo[1185:18722] -[UINavigationController setNameStr:]: unrecognized selector sent to instance 0x7fe928dad8c0
2015-03-11 10:27:24.147 bdipo[1185:18722] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[UINavigationController setNameStr:]: unrecognized selector sent to instance 0x7fe928dad8c0'
You only need one Navigation Controller in any app which loads at launch time and will handle navigating the full stack of view controllers for you. It needs to be the root view controller, with the "Is Initial View Controller" box checked (under Attributes Inspector, View Controller) to give it that start-arrow pointing in to the left side of the view.
Assuming your NewsTVC is the first view controller you want to show, delete the navigation view controller in your storyboard. Select NewsTVC, go up to your XCode toolbar and select "Editor --> Embed In --> Navigation Controller".
A navigation controller will be created that segues into your NewsTVC. Make sure "Is Initial View Controller" is selected for the new Navigation Controller, and drag a new segue between you NewsTVC prototype cell and the DetailVC. This should organize your stack so the segue runs properly.
You can access your view as below
DetailViewController *detailVC =
[self.storyboard instantiateViewControllerWithIdentifier:#"DetailViewController"];//modify this identifier name as per your StoryBoardIDentifier for detailview at storyboard..
detailVC.nameStr=#"Nuibb";
[self.navigationController pushViewController:detailVC animated:NO];//if you want to push to detail view from first view
Hope it helps you...
V1(segue name = xyz)-->NavController-->(ROOT)VC2
In your vc1's didselectforRowAtIndexPath or any other Action method write this code.
[self performSegueWithIdentifire:#"xyz"];
Then override the method -
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"xyz"]) {
DetailViewController *vc = (DetailViewController *)[segue destinationViewController];
vc.nameStr = #"Nuibb";
}
}
Inside DetailViewController.h declare a property first.
#property(nonatomic,strong)NSString *nameStr;
On tableView didSelectRowAtIndex a UINavigationController is segued as per your storyboard and your navigationController is not having any property named nameStr
You will have to segue detailViewController direct. As UINavigationController cannot pe pushed in navigation.
I'm currently trying to have a better understanding on how the mechanisms of passing data between controllers work and I'm a little confused especially when passing data back from a second view controller to the main view controller.
This is what I have that works but don't fully understand. I have two view controllers, in the first one I have a button that when clicked it basically goes to the second view controller and a label which shows a message sent from the second view controller. In the second view controller I have a button and a textField, the button basically sends whatever is in the textfield to the label in main view controller.
Here is the code...
// FirstVC.h
#import <UIKit/UIKit.h>
#import "SecondVC.h"
#interface FirstVC : UIViewController <passNames>
#property (nonatomic, strong) NSString* firstNameString;
#property (weak, nonatomic) IBOutlet UILabel *firstNameLabel;
#end
//FirstVC.m
#import "FirstVC.h"
#implementation FirstVC
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier]isEqualToString:#"secondController"])
{
UINavigationController *navController = segue.destinationViewController;
SecondVC *vc2 = (SecondVC*)navController.topViewController;
[vc2 setDelegate:self];
}
}
-(void)viewWillAppear:(BOOL)animated
{
self.firstNameLabel.text = _firstNameString;
}
-(void)setFirstName:(NSString *)firstName
{
_firstNameString = firstName;
}
#end
//SecondVC.h
#import <UIKit/UIKit.h>
#protocol passNames <NSObject>
-(void)setFirstName:(NSString*)firstName;
#end
#interface SecondVC : UIViewController
#property (retain)id <passNames> delegate;
- (IBAction)send:(UIBarButtonItem *)sender;
#property (nonatomic, strong) NSString *firstNameString;
#property (weak, nonatomic) IBOutlet UITextField *firstNameText;
#end
//SecondVC.m
#import "SecondVC.h"
#import "FirstVC.h"
#interface SecondVC ()
#end
#implementation SecondVC
- (IBAction)send:(UIBarButtonItem *)sender
{
_firstNameString = _firstNameText.text;
[[self delegate]setFirstName:_firstNameString];
[self dismissViewControllerAnimated:YES completion:nil];
}
#end
Can someone explain how the prepareForSegue method works in the above code? The reason for this question is because I added an NSLog and it looks like this method is only called in the transition from main view controller to the second controller. Why is this method needed if it is not called when transitioning from second view controller to main view controller which in my case is what I'm doing? It makes sense to use it when passing data from main view controller to a second controller not on the case shown above.
Can some explain the whole mechanism when passing data back to the main view controller?
FYI, I do understand about protocols and delegation.
Thanks a lot.
In your case, you are setting your delegate method of the second view controller to self in mainViewController in you prepareForSegue. This means that apart from navigating to the SecondViewController, you are implementing the callback mechanism in your main view controller, so that your delegate method gets called when the value is passed from the second view controller and this delegate method collects the value as a parameter to handle it in the main View Controller. You might have set the delegate of VC2 as self inn your prepareForSegue because you are creating the instance of VC2 in this method to navigate to the second controller.
Your goal is to hand back the data, like this:
[[self delegate] setFirstName:_firstNameString];
But you can't do that unless you know who to send setFirstName: to, and the compiler won't let you do it unless you guarantee that whoever you are sending setFirstName: to can accept that message.
That is what prepareForSegue prepares. FirstVC has declared that it adopts the passNames protocol, which means that it implements setFirstName:. And now you are saying:
[vc2 setDelegate:self];
...where self is the FirstVC instance. This solves both problems at once. The SecondVC instance (vc2) now has a delegate (the FirstVC instance), it is the right object to send the info back to, and because its delegate is declared as adopting passNames, we know that SecondVC can actually send setFirstName: to that delegate.
Now to the heart of your actual question: The reason for doing this in prepareForSegue is merely that this is the only moment when the FirstVC instance and the SecondVC instance "meet" one another! There is no other moment when the FirstVC instance has a reference to the SecondVC instance so as to be able to call setDelegate on it in the first place. If you weren't using segues and storyboards, the FirstVC would simply create the SecondVC instance directly - and would set itself as its delegate, just as you do:
SecondVC *vc2 = [SecondVC new];
UINavigationController *nav = [
[UINavigationController alloc] initWithRootViewController: vc2];
[vc2 setDelegate:self];
[self presentViewController: nav animated: YES completion: nil];
This is one reason I don't like storyboards: they muddy the story. It's all so simple and obvious when you don't use them and just do everything directly like this.
I am trying to present MFMailComposeViewController from NSObject subclass. Scenario is:
I have UIViewController subclass at what user tap a button to start some process
All logic for this process is taken out of this ViewController to the NSObject subclass
If something wrong with the process I am showing UIAlertView
One of the buttons in this alertView should open mailComposer for user to send feedback to me.
If I am trying present mailComposer from NSObject subclass I am getting "trying to present modal view controller what is not in class hierarchy". So I am setting my viewController as delegate for MFMailComposeViewController. But by touching a button "Send feedback" on alertView it disappears and I am getting the same "trying to present modal view controller what is not in class hierarchy" error. I am trying to log what is the rootViewController this way:
UIWindow *window = [UIApplication sharedApplication].keyWindow;
UIViewController *rootViewController = window.rootViewController;
NSLog(#"rvc - %#", [rootViewController description]);
but log shows to me that rvc is equal to (null). Can I present mailController from NSObject directly, or how it can be done in right way?
The object which presents the MFMailComposeViewController must be part of the view hierarchy, as the error message indicates. In other words, it must be a view controller that has a view currently visible. Your NSObject-derived class that has all your logic should have a delegate back to the UIViewController which it manages that it can call to tell it to display the MFMailComposeViewController. This pattern is integral to iOS development and is covered in the View Controller Programming guide. Here's an example:
#protocol ACFeedback <NSObject>
- (void)showFeedbackMailMessage;
#end
#interface ACDataThing : NSObject
#property (nonatomic, weak) id<ACFeedback> feedbacker;
#end
#interface ACEmptyViewController : UIViewController<ACFeedback>
...
When you instantiate ACDataThing, set the feedbacker property to the ACEmptyViewController reference, then in ACEmptyViewController implement the method showFeedbackMailMessage to create and show the MFMailComposeViewController.