'NSObject' may not respond to -navigationController - iphone

Hi there I currently I have a warning on a line of code where I am trying to push a new view onto the screen.
Outline // my NSObject receives a code=1 from my server I have set up. Everything works fine the code comes through which then initializes an AlertView where I have set up an if statement to catch the button click of my AlertView message. When that button is pressed my application falls over.
I have declared my ViewController of the view I am trying to push in its header file and there are no errors just the warning when compiled.
this is my NSObject I have made
/////.h
#import <Foundation/Foundation.h>
#interface alerts : NSObject {
}
- (void)pleaseRegisterDevice;
#end
/////.m
#import "alerts.h"
#import "instaCode1_3AppDelegate.h"
#import "RegisterDeviceViewController.h"
#implementation alerts
//use this alert when phone falls out of sync
- (void)pleaseRegisterDevice {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Please Register Device"
message:#"click OK to register"
delegate:self
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert autorelease];
[alert show];
}
//Catch pleaseRegisterDevice method
- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex {
NSString *buttonTitle=[alertView buttonTitleAtIndex:buttonIndex];
if ([buttonTitle isEqualToString:#"OK"]) {
NSLog(#"msg from alertView method");
//open new wndow
RegisterDeviceViewController *regViewController = [[RegisterDeviceViewController alloc] init];
//Push it onto the top pf the navigation controller's stack
**[[self navigationController] pushViewController:regViewController animated:YES];**
}
else {
NSLog(#"was not able to push view");
}
}
- (void)dealloc {
[super dealloc];
}
#end
I have bolded the line of code where I get the warning 'alerts' may not respond to -navigationController
any help would be greatly appreciated.

I dont think an NSObject subclass has a UINavigationController...
You need to get a pointer to your app delegate's navigation controller like so
MyAppDelegate *appDelegate = (MyAppDelegate *)[[UIApplication sharedApplication] delegate];
[appDelegate.navigationController pushViewController:regViewController animated:YES];

navigationController is a property defined on a UIViewController. A NSObject does not have this method.

You don't have any instance member or method called navigationController, hence the warning.

Related

UIActionSheet code crashes when moved from UIViewController file to separate class file

I have searched and searched the board(s) and am not able to figure this out. It has got to be something simple and right in front of me.
I am trying clean up my code and make it more reusable. I was taking some UIActionSheet code that works from a UIViewController and making its own object file. Works fine, until I add UIActionSheetDelegate methods.
When a button is pressed, instead of firing the actionSheetCancel method, it crashes with no stack trace. Every time.
My code is below. Any help would be appreciated. My guess has been it is because I am not using the xcode storyboard tool to connect things together, but I would think this is legal.
egcTestSheet.h:
#import <UIKit/UIKit.h>
#interface egcTestSheet : NSObject <UIActionSheetDelegate> {
}
- (void) showSheet:(UITabBar *) tabBar
displayTitle:(NSString *) name;
#end
egcTestSheet.m
#import "egcTestSheet.h"
#implementation egcTestSheet
-(void) showSheet:(UITabBar *)tabBar displayTitle:(NSString *)name{
UIActionSheet *menu = [[UIActionSheet alloc] initWithTitle:name
delegate:self
cancelButtonTitle:#"Done"
destructiveButtonTitle:#"Cancel"otherButtonTitles:nil];
[menu showFromTabBar:tabBar];
[menu setBounds:CGRectMake(0,0,320, 700)];
}
// actionsheet delegate protocol item
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex: (NSInteger)buttonIndex{
NSLog(#"button index = %d", buttonIndex);
}
- (void)actionSheetCancel:(UIActionSheet *)actionSheet{
NSLog(#"in action canceled method");
}
#end
call code from a UIViewController object:
egcTestSheet *sheet = [[egcTestSheet alloc] init];
[sheet showSheet:self.tabBarController.tabBar displayTitle:#"new test"];
Your action sheet is probably being released as it is dismissed (are you using ARC?). This means when it tries to call it's delegate to inform said delegate of its dismissal/selection, it is trying to call self. Self is a dangling pointer by this time, because it has been released.
In the view controller that is presenting/calling this action sheet, set a property to keep a reference to the action sheet. Set the property to nil on dismissal of the action sheet.

Display a UIAlertView in NSObject Class

I'm building a login system within my app that will be called several times. So instead of copying and pasting the code into several spots, I'm of course making an NSObject class so I can call the class when needed, instead.
The login system will display a UIAlertView, and when "OK" is tapped, the system will attempt to log in. I can call the class and the UIAlertView will show, but I cannot tell which buttons are tapped. Here is my code:
//Calling the login system
Login *login = [[Login alloc] init];
Login.h:
#import <Foundation/Foundation.h>
#interface Login : NSObject <UIAlertViewDelegate> {
}
#end
Login.m:
#import "Login.h"
#implementation Login
+(void)initialize {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Login" message:nil delegate:self cancelButtonTitle:#"Cancel" otherButtonTitles:#"OK", nil];
[alert show];
[alert release];
NSLog(#"Testing");
}
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
NSLog(#"Here");
NSString *title = [alertView buttonTitleAtIndex:buttonIndex];
if([title isEqualToString:#"OK"]) {
NSLog(#"Tapped");
}
}
#end
For now, before I put UITextFields in the view, I just want to get the app to know which button was tapped. Testing appears in the log, but neither Here nor Tapped appear. Thanks!
Your alert view should not be called by the class method +(void)initialize but by the instance -(id)init method that's why your instance doesn't get the notifications.
the class method "+(void)initialize" is called when the class first load.
the instance method "-(id)init" has its name beginning by init, and is called when you create (instantiate) your object.
-(id)init {
//alert view
self = [super init];
return self;
}
Just
switch(buttonIndex){
case 0:
NSLog(#"Tapped First Button");
break;
case 1:
break;
default:
break;
}
When you use self in a class method you're referring to the class itself, rather than an instance of the class. However, your delegate method is an instance method. You probably want the caller to create a Login instance and have the instance create the alert, plus be its delegate.
It's Simple :
Create a property for your NSObject class in your view controller class :
in h file :
#property (nonatomic , retain) LoginCheckNSObject *LoginCheckerObject;
in m file :
self.LoginCheckerObject=[[LoginCheckNSObject alloc] init];
[self.LoginCheckerObject setDelegate:self];
[self.LoginCheckerObject TrackNowLogin];

Frustrating UIWebView Delegate Crash issue

I've created an ARC Application that run's perfect. It's got a UINavigationController that I use to push through the views and everything runs fine.
I'm converting the Application to iPad and i've decided to show one of the views as a popover. (I don't like UIPopoverController so i've created my own basic popup). It's added to the view as follows..
MyViewController *hotelinf = [[MyViewController alloc]
initWithNibName:#"MyViewController_iPad" bundle:nil];
[self.view addSubview:hotelinf.view];
The view is added as a subview fine. The view i'm adding contains a UIWebView that has the usual delegate methods in it, but when the view tries to access one of the delegates it simply crashes.
*** -[MyViewController respondsToSelector:]: message sent to deallocated instance 0x7917b90
Specifically, it crashes on this line..
[self.webView loadHTMLString:stringResponse baseURL:nil];
I've displayed views (and UINavigationControllers) as subViews many of times without any issues, although none of them included a UIWebView delegate. I'm guessing I have to set the delegate of my UIViewController istance but i'm not sure how. It's also worth noting that if I push the view in my existing UINavigationController it calls and loads the HTML fine, which surely means it has nothing to do with the code of the view itself.
Any help would be greatly appreciated! Here is the code (in addition to above that shows the controller)..
.h
#interface MyViewController : UIViewController <UIWebViewDelegate, UIActionSheetDelegate> {
//Unrelated IBOutlets
}
#property (nonatomic, strong) UIWebView *webView;
#end
.m
#implementation MyViewController
#synthesize webView;
- (void)viewDidLoad
{
[super viewDidLoad];
self.webView = [[UIWebView alloc]initWithFrame:CGRectMake(317,283,393,354)];
self.webView.delegate = self;
[self.view addSubview:self.webView];
[NSThread detachNewThreadSelector:#selector(getHTMLString) toTarget:self withObject:nil];
}
-(void)getHTMLString {
#autoreleasepool {
//Download a valid HTML String
[self performSelectorOnMainThread:#selector(loadHTML) withObject:nil waitUntilDone:NO];
}
}
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
self.webView = nil;
}
-(void)loadHTML {
self.webView.opaque = NO;
self.webView.backgroundColor = [UIColor clearColor];
if ([stringResponse isEqualToString:#""]) {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Error" message:#"Could not connect to XXXXX.com. Please verify you are connected to a working 3G/WIFI Network." delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
} else {
//it crashes here only when loaded as a subview - the first access to the delegate
[self.webView loadHTMLString:stringResponse baseURL:nil];
}
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
}
- (void)webViewDidFinishLoad:(UIWebView *)webView {
[self stopIndicator];
}
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error
{
if (error.code == NSURLErrorCancelled) return; // this is Error -999
[self stopIndicator];
// report the error inside the webview
NSString* errorString = [NSString stringWithFormat:
#"<html><center><font size=+10 color='black' face='Helvetica'>An error occurred:<br>%#</font></center></html>",
error.localizedDescription];
[self.webView loadHTMLString:errorString baseURL:nil];
UIAlertView *alert = [[UIAlertView alloc]
initWithTitle:#"Cannot load URL."
message:#"You have a connection failure. Please verify you are connected to a WIFI or 3G enabled Network."
delegate:self cancelButtonTitle:#"Ok" otherButtonTitles:nil];
[alert show];
}
#end
The issue has nothing to do with the UIWebView, rather with your controller class. Indeed,
MyViewController *hotelinf = [[MyViewController alloc]
initWithNibName:#"MyViewController_iPad" bundle:nil];
[self.view addSubview:hotelinf.view];
You are allocating the controller and assigning it to a local variable; then you add the controller's view as subview to your current view. Doing that, that view is retained, but what happens to the controller object itself? Are you releasing it? Or it leaks (since it is assigned to a local variable)?
This possibly explains why when later the respondsToSelector method is called, the controller has already been deallocated...
A way to fix this is creating a new property or an ivar in your main controller class and store MyViewController in there. Don't forget to release it in dealloc.
I would also suggest another thing. In:
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
self.webView = nil;
}
set the webView delegate to nil before releasing the view:
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
self.webView.delegate = nil;
self.webView = nil;
}
And I would also possibly review the reason why you release the webView in viewDidDisappear. On the other hand you allocate it in viewDidLoad. This asymmetry is dangerous, since whenever the main view disappears (for any reason) the webView will be removed and when the view reappears, it is not there anymore.
Better add all the delegate methods. You havent added the first two. Most probably, your code is crashing when message webViewDidStartLoad is sent
– webView:shouldStartLoadWithRequest:navigationType:
– webViewDidStartLoad:
– webViewDidFinishLoad:
– webView:didFailLoadWithError:

Using UIAlertView in an NSObject

I'm having a terrible time getting a UIAlertView to work within my custom NSObject class. In the research I've done it appears it should be possible but here's what I've run into.
First, here's my code:
-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
NSLog(#"clickedButtonAtIndex: %d", buttonIndex);
}
-(void)testAlertView {
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"List Contains Items"
message:#"List contains items. Remove all items & delete?"
delegate:self
cancelButtonTitle:#"No"
otherButtonTitles:#"Yes", nil];
[alertView show];
}
If I set the delegate to self this code crashes as soon as I tap a button. If I set it to nil clickedButtonAtIndex is never called. I've tried with and without using the <UIAlertViewDelegate>.
I know someone will ask 'why are you doing this in NSObject instead of in your UIViewController?'. Primarily because I want to separate this code out so I can use it from multiple places in my app. But also because this is a small piece of a larger block of logic that makes sense to be on it's own.
Any ideas what I'm doing wrong?
Thanks,
Rich
I had the same problem using ARC. The root of the problem was the same. I solved it by putting my custom NSObject into a "strong" property to make sure the object exists as long as the calling object (an UIVIewCOntroller in my case) exists, so when the delegate of my alert view is called I still have my custom object around and the delegate method works fine.
Add the NSObject as strong property:
#import "Logout.h" // is NSObject
.
.
.
#property (nonatomic, strong) Logout *logout;
Then you will get the delegatemethods called in your NSObject.
Don´t forget to register the delegate for the UIAlertView:
#interface Logout () <UIAlertViewDelegate>
and in your method:
UIAlertView *a = [[UIAlertView alloc] initWithTitle:#"title"
message:#"message" delegate:self cancelButtonTitle:#"cancel"
otherButtonTitles:#"ok", nil];
[a show];
How To Present An Alert View Using UIAlertController When You Don't Have A View Controller. Detail description.
Yes, you can only use UIAlertController only in UIViewController classes. So how can we do it in NSObject classes. If you see the description link given above you will get to the answer. To summarise in a line for the above description: Create a new window above the the current window. This new window will be our viewController where we display alert. So using this viewController you can call the method [presentViewController: animated: completion:].
Answer:
dispatch_async(dispatch_get_main_queue(), ^{
UIWindow* window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
window.rootViewController = [UIViewController new];
window.windowLevel = UIWindowLevelAlert + 1;
NSString *msg=#“Your mssg";
UIAlertController* alertCtrl = [UIAlertController alertControllerWithTitle:#“Title" message:msg preferredStyle:UIAlertControllerStyleAlert];
[alertCtrl addAction:[UIAlertAction actionWithTitle:NSLocalizedString(#"Yes",#"Generic confirm") style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
// do your stuff
// very important to hide the window afterwards.
window.hidden = YES;
}]];
UIAlertAction *cancelAction= [UIAlertAction actionWithTitle:#"cancel" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
window.hidden = YES;
}];
[alertCtrl addAction:cancelAction];
//http://stackoverflow.com/questions/25260290/makekeywindow-vs-makekeyandvisible
[window makeKeyAndVisible]; //The makeKeyAndVisible message makes a window key, and moves it to be in front of any other windows on its level
[window.rootViewController presentViewController:alertCtrl animated:YES completion:nil];
});

iphone navigationController : wait for uialertview response before to quit the current view

I have a view with a back button managed with a navigation controller and I want to check if a file has been saved when the user click on the back button.
If the file has been saved you go back in the previous view, else a uialertview ask you if you want to save the file or not.
So I did that but the view disapear and the alertview appear after.
-(void)viewWillDisappear:(BOOL)animated {
if(!self.fileSaved){
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"" message:#"Save the file?" delegate:self cancelButtonTitle:#"No" otherButtonTitles:#"Yes",nil];
[alert show];
[alert release];
}
}
- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex
{
switch (buttonIndex) {
case 0:
NSLog(#"NO");
break;
case 1:
NSLog(#"yes");
break;
default:
break;
}
}
When viewWillDisappear is called, it's already too late. You should intercept the back button earlier on. I have never done it, but my suggestion is to set the delegate on the navigationBar property in your viewDidAppear method:
// save the previous delegate (create an ivar for that)
prevNavigationBarDelegate = self.navigationController.navigationBar.delegate;
self.navigationController.navigationBar.delegate = self;
Don't forget to set it back in viewWillDisappear:
self.navigationController.navigationBar.delegate = prevNavigationBarDelegate;
Then intercept the shouldPopItem method:
- (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item {
if(!self.fileSaved) {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"" message:#"Save the file?" delegate:self cancelButtonTitle:#"No" otherButtonTitles:#"Yes",nil];
[alert show];
[alert release];
return NO;
}
if ([prevNavigationBarDelegate respondsToSelector:#selector(navigationBar:shouldPopItem:)])
return [prevNavigationBarDelegate navigationBar:navigationBar shouldPopItem:item];
return YES;
}
And in the YES handler for the dialog, manually pop the controller:
[self.navigationController popViewController:YES];
You must subclass UINavigationController for this to work. Then override - (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item .
You should set up a custom Delegate protocol that your view controllers adopt and, if you allow it to pop, call your [super navigationBar shouldPopItem:], else, return NO to the above method.
Wouldn't it be easier just to add a left button item as in the following:
UIBarButtonItem *backButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemSave target:self action:#selector(saveThisDate)];
self.navigationItem.leftBarButtonItem = backButton;
[backButton release];
To follow up on nobre response and as Jon mentionned it, the best way is to subclass UINavigationController.
The easiest way and fastest way to acheive this :
Modify the class of your navigation controller in Interface Builder to inherit from CustomNavigationControllerDelegate
Implement the CustomNavigationControllerDelegate protocol in your UIViewController
#interface YourViewController <CustomNavigationControllerDelegate>
#pragma mark - UINavigationBar Delegate Methods
- (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item {
UIAlertView* alert = [[UIAlertView alloc] initWithTitle:title message:message delegate:self cancelButtonTitle:cancel otherButtonTitles:ok, nil];
alert.tag = kpopup_back;
[alert show];
return NO;
}
Register your controller as the delegate
#pragma mark - viewWillAppear
- (void) viewWillAppear:(BOOL)animated
{
((CustomNavigationController*)self.navigationController).customDelegate = self;
}
Finally and important part, REMOVE the delegate (to avoid to re-trigger yourself on the pop) and pop the controller yourself in the the UIAlertViewDelegate
case kpopup_back :
{
if(buttonIndex != 0) //OK
{
((CustomNavigationController*)self.navigationController).customDelegate = nil;
[self.navigationController popViewControllerAnimated:YES];
}
}
break;
It works flawlessly on my side, hope it can help.
Here are the sources :
CustomNavigationControllerDelegate.h
#import <UIKit/UIKit.h>
#protocol CustomNavigationControllerDelegate <NSObject>
#optional
- (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item;
#end
#interface CustomNavigationController : UINavigationController
#property (nonatomic, retain) id<CustomNavigationControllerDelegate> customDelegate;
#end
CustomNavigationControllerDelegate.m
#import "CustomNavigationController.h"
#interface CustomNavigationController ()
#end
#implementation CustomNavigationController
- (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item {
if (_customDelegate && [_customDelegate respondsToSelector:#selector(navigationBar:shouldPopItem:)]) {
return [_customDelegate navigationBar:navigationBar shouldPopItem:item];
}
return YES;
}
#end