I did the "Build and analyze" in xCode and get "Dereference of null pointer" . I noted in my code below for which row I get the message. I'm developing for iPhone.
"PDFScrollView.h"
#import "ENsightAppDelegate.h"
#interface PDFScrollView : UIScrollView <UIScrollViewDelegate> {
ENsightAppDelegate *appDelegate;
}
#end
"PDFScrollView.m"
- (id)initWithFrame:(CGRect)frame
{
if ((self = [super initWithFrame:frame])) {
// Set up the UIScrollView
self.showsVerticalScrollIndicator = NO;
self.showsHorizontalScrollIndicator = NO;
self.bouncesZoom = YES;
//self.decelerationRate = UIScrollViewDecelerationRateFast;
self.delegate = self;
[self setBackgroundColor:[UIColor grayColor]];
self.maximumZoomScale = 5;//200
self.minimumZoomScale = 0.5;//.85
self.userInteractionEnabled = YES;
self.delaysContentTouches = YES;
self.canCancelContentTouches = NO;
}
appDelegate =(ENsightAppDelegate *) [[UIApplication sharedApplication] delegate];
pdfView=[appDelegate GetLeaveView];
[self addSubview:pdfView];
return self;
}
It's not the complete code, pasted what I think is useful.
I find this quite strange. How come I get this message?
If self is nil, then you can't access instance variables of 'self'. So you should reference those variables only inside of the if statement. In other words, only if self is not nil.
- (id)initWithFrame:(CGRect)frame {
if ((self = [super initWithFrame:frame])) {
// Set up the UIScrollView
self.showsVerticalScrollIndicator = NO;
// ... bunch of other properties set...
// ... btw, better style here would be to set the ivars directly (in an initializer)
appDelegate =(ENsightAppDelegate *) [[UIApplication sharedApplication] delegate];
pdfView=[appDelegate GetLeaveView];
[self addSubview:pdfView];
}
return self;
}
is appDelegate an instance var of the class? if so, you should be doing it in the if (self =..... same with the [self addSubview...]. basically if that if statement fails, you don't have an object to work with.
appDelegate is actually [self appDelegate], so if you assign null to self, you dereference null pointer.
Anyway - self points to the current object, why would you assign to it? You can use it directly without an assignment.
And last thing - you do use self explicitly in the on-before-last line anyway, and here it is clear that it is null:
[self addSubview:pdfView];
Related
I have this cascade :
Somewhere in the app
- (void) go
{
MyCustomViewController* controller = [[MyCustomViewController alloc] init];
controller.delegate = self;
[controller run];
}
in MyCustomViewController
- (id) init
{
// there is an if statement here in real to choose another XIB if needed
// but I only display here the code that is called in my tests
self = [super initWithNibName:#"MyXIB" bundle:nil];
if (!self) return nil;
self.delegate = nil;
return self;
}
- (void) run
{
// do things with self.view
}
MyCustomViewController inherits GenericWindowController
in GenericWindowController
/*- (id) initWithNibName:(NSString*)nibNameOrNil bundle:(NSBundle*)nibBundleOrNil
{
if (!(self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil])) return nil;
self.view.userInteractionEnabled = NO; // THE APP CRASHES HERE ! self.view is nil
...
return self;
}*/
// METHOD created following first answers : NOT CALLED
- (void) viewDidLoad
{
self.view.userInteractionEnabled = NO;
// and many other things done with self.view
}
MyXIB has its File's owner set to MyCustomViewController and the view is connected.
All files are included and checked into the project.
GenericWindowController is designed to make some standard stuff.
MyCustomViewController extends this stuff to work with a custom View, as designed in MyXIB.
Why self.view is nil in GenericWindowController ?
Why viewDidLoad is not called ?
A view controller should not try to access its view in initWithNibName:bundle:. It should wait until viewDidLoad.
self.view is only valid after viewDidLoad -- in init... it is still nil.
Incredible !!! The problem was because of a missing localization for the XIB. Added the localization, and no problem anymore.
New to objective c, and I am using ArcGIS for the map portion. I have a problem where the method mapViewDidLoad is not called/loaded. Here is some part of the code:
.h file
#interface ViewController : UIViewController<AGSMapViewLayerDelegate, AGSMapViewTouchDelegate, AGSMapViewCalloutDelegate>{
AGSMapView *_mapView;
AppDelegate *appDelegate;
...
}
.m file
- (void)viewDidLoad
{
[super viewDidLoad];
[self.activityView startAnimating];
self.mapView.touchDelegate = self;
self.mapView.calloutDelegate = self;
appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
CLLocationManager *locationManager = [[CLLocationManager alloc] init];
...
}
- (void)mapViewDidLoad:(AGSMapView *)mapView {
AGSEnvelope *envelope = [[AGSEnvelope alloc]initWithXmin:29757.610204117
ymin:40055.0379682464
xmax:29884.6992302249
ymax:40236.6028660071
spatialReference:self.mapView.spatialReference];
[self.mapView zoomToEnvelope:envelope animated:YES];
self.mapView.callout.width = 195.0f;
self.mapView.callout.accessoryButtonHidden = YES;
[self.mapView.gps start];
[self.mapView centerAtPoint:self.mapView.gps.currentPoint animated:YES];
NSLog(#"Location : %#", self.mapView.gps.currentPoint);
[self.activityView stopAnimating];
self.activityView.hidden = YES;
}
What is wrong with my code why i doesn't load the mapViewDidLoad method.
Thanks in advance.
make sure that mapviewdelegate is connected by right click on mapview and then assign delegate..
or add [self.mapview setDelegate:self];
in your case "AGSMapView" mapViewDidLoad: method on AGSMapViewLayerDelegate is invoked after the first layer has been added to the map. At this point, the component is fully functional you can find reference to it in
http://help.arcgis.com/en/arcgismobile/10.0/apis/iphone/reference/interface_a_g_s_map_view.html
make self.mapview.layerDelegate = self;
Just add self.mapView.delegate = self; in viewDidLoad
I am currently doing the following in my code avoid the issue of "obscured" ad. But is it a good practice? One potential problem is that - assume before the viewWillDisappear, there was an ad request send out, and then when the ad come back the adBannerView instance has gone. Would that be a big problem? Should I only do hideAdBanner instead?
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear: animated];
// create the ad banner view
[self createAdBannerView];
if (adBannerView != nil) {
UIInterfaceOrientation orientation = self.interfaceOrientation;
[self changeBannerOrientation:orientation];
}
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
// iAd
if (adBannerView != nil) {
[self hideAdBanner];
adBannerView.delegate = nil;
[adBannerView release];
adBannerView = nil;
}
}
I use a singleton for an ad banner and call it into view on each ViewDidLoad. This automatically removes it from the previous view.
This is for adWhirl, but you should be able to adopt it for just iAD.
adWhirlSingleton.h
#import <Foundation/Foundation.h>
#import "AdWhirlDelegateProtocol.h"
#import "AdWhirlView.h"
#interface adWhirlSingleton : NSObject <AdWhirlDelegate> {
AdWhirlView *awView;
UIViewController *displayVC;
}
#property (strong, nonatomic) AdWhirlView *awView;
#property (strong, nonatomic) UIViewController *displayVC;
+(id)sharedAdSingleton;
-(void)adjustAdSize:(CGFloat)x:(CGFloat)y;
#end
adWhirlSingleton.m
#import "adWhirlSingleton.h"
#implementation adWhirlSingleton
static adWhirlSingleton* _sharedAdSingleton = nil;
#synthesize awView, displayVC;
+(id)sharedAdSingleton
{
#synchronized(self)
{
if(!_sharedAdSingleton)
_sharedAdSingleton = [[self alloc] init];
return _sharedAdSingleton;
}
return nil;
}
+(id)alloc
{
#synchronized([adWhirlSingleton class])
{
NSAssert(_sharedAdSingleton == nil, #"Attempted to allocate a second instance of a singleton.");
_sharedAdSingleton = [super alloc];
return _sharedAdSingleton;
}
return nil;
}
-(id)init
{
self = [super init];
if (self != nil) {
// initialize stuff here
self.awView = [AdWhirlView requestAdWhirlViewWithDelegate:self];
}
return self;
}
-(void)dealloc
{
displayVC = nil;
if (awView) {
[awView removeFromSuperview]; //Remove ad view from superview
[awView replaceBannerViewWith:nil];
[awView ignoreNewAdRequests]; // Tell adwhirl to stop requesting ads
[awView setDelegate:nil];
awView = nil;
}
}
-(void)adjustAdSize:(CGFloat)x :(CGFloat)y
{
[UIView beginAnimations:#"AdResize" context:nil];
[UIView setAnimationDuration:0.7];
awView.frame = CGRectMake(x, y, kAdWhirlViewWidth, kAdWhirlViewHeight);
[UIView commitAnimations];
NSLog(#"Recent Network Name: %#",[awView mostRecentNetworkName]);
}
-(BOOL)adWhirlTestMode
{
return YES;
}
-(NSString *)adWhirlApplicationKey
{
return #"xxxxxxxxxxxxx";
}
-(UIViewController *)viewControllerForPresentingModalView
{
return displayVC;
}
-(void)adWhirlDidReceiveAd:(AdWhirlView *)adWhirlView
{
NSLog(#"%s",__FUNCTION__);
NSLog(#"Recent Network Name: %#",[awView mostRecentNetworkName]);
//[self adjustAdSize];
}
-(void)adWhirlDidFailToReceiveAd:(AdWhirlView *)adWhirlView usingBackup:(BOOL)yesOrNo
{
NSLog(#"%s",__FUNCTION__);
}
#end
Then import adWhirlSingleton into each ViewController and in each viewWillAppear i just implement this:
adWhirlSingleton *adWhirlSingle = [adWhirlSingleton sharedAdSingleton];
adWhirlSingle.displayVC = self;
[adWhirlSingle adjustAdSize:0 :self.view.frame.size.height -50];
[self.view addSubview:adWhirlSingle.awView];
[self.view bringSubviewToFront:adWhirlSingle.awView];
NSLog(#"Ad Banner View");
but the view I have with a UITableView, I use this:
adWhirlSingleton *adWhirlSingle = [adWhirlSingleton sharedAdSingleton];
adWhirlSingle.displayVC = self;
[adWhirlSingle adjustAdSize:0 :self.tabBarController.view.frame.size.height -99];
[self.tabBarController.view addSubview:adWhirlSingle.awView];
NSLog(#"Should have added Ad!");
Hope that helps you a bit
Out of interest, why is that you are removing the ADBannerView?
Apple state that you should share the ADBannerView instances across views.
From the docs: "If your application has multiple tabs or views displaying an iAd banner, be sure to share a single instance of ADBannerView across each view."
i.e. Apple think you should have the ADBannerView presented at the top/front of your view hierarchy and just move it off-screen when there is no ad to show.
So, to answer the question, "is it bad practice to remove and then add it again?"
yes, Apple would say it is.
No answers yet, but i've got an update that seems to have fixed the problem, any ideas why it works now though?!
I've got it to work in the way I intended by doing the following to every class that needs to display an adBanner:
1. In the layoutForCurrentOrientation method I added the following:
adBanner.delegate = self;
[self.view addSubview:adBanner];
2. In the deAlloc method on each class, I removed the following:
[adBanner removeFromSuperview];
Original question:
I'm attempting to use the iAdSuite sample code from Apple to use a single adBanner instance shared across all my views.
The sample implementation is designed to show the adbanner on each view that is called by the rootViewController, however, I would like my app to have ads on the rootViewController view also.
In my amended code:-
When I fire up the app, no banner is shown on the rootView, even though a method is called to request an ad banner. The class is set as the delegate for the ad and the delegate methods are available. These are called and the log for (adBanner.bannerLoaded) is NO.
As it is a shared object, if I switch views to from the rootView the ad is displayed in the other view.
When I return back to the rootView, the delegate method log shows that a banner is loaded, and it is positioned in a visible portion of the view. But the banner isn't visible.
In summary, i'm using the iAdSuite sample code for the AdBannerNavigation project, and trying to use it so that ad banners show on all views, including the rootViewController.
Any help appreciated!
The code i'm using is available here:
http://developer.apple.com/library/ios/#samplecode/iAdSuite/Introduction/Intro.html
My amended rootViewController.h:
#import <UIKit/UIKit.h>
#import <iAd/iAd.h>
#interface RootViewController : UITableViewController <ADBannerViewDelegate>
#end
My amended rootViewController.m
#import "RootViewController.h"
#import "TextViewController.h"
#import "MapViewController.h"
#import "AdBannerNavigationAppDelegate.h"
// for SharedAdBannerView macro
// #define SharedAdBannerView ((AdBannerNavigationAppDelegate *)[[UIApplication sharedApplication] delegate]).adBanner
#import <iAd/iAd.h>
#interface RootViewController()
// Layout the Ad Banner and Content View to match the current orientation.
// The ADBannerView always animates its changes, so generally you should
// pass YES for animated, but it makes sense to pass NO in certain circumstances
// such as inside of -viewDidLoad.
- (void)layoutForCurrentOrientation:(BOOL)animated;
// A simple method that creates an ADBannerView
// Useful if you need to create the banner view in code
// such as when designing a universal binary for iPad
- (void)createADBannerView;
#end
- (void)viewDidLoad
{
[super viewDidLoad];
[self createADBannerView];
[self layoutForCurrentOrientation:NO];
}
- (void)viewDidUnload
{
ADBannerView *adBanner = SharedAdBannerView;
adBanner.delegate = nil;
[adBanner removeFromSuperview];
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self layoutForCurrentOrientation:NO];
}
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
[self layoutForCurrentOrientation:YES];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return YES;
}
- (void)dealloc
{
ADBannerView *adBanner = SharedAdBannerView;
adBanner.delegate = nil;
[adBanner removeFromSuperview];
[super dealloc];
}
- (void)createADBannerView
{
ADBannerView *adBanner = SharedAdBannerView;
NSString *contentSize;
if (&ADBannerContentSizeIdentifierPortrait != nil)
{
contentSize = UIInterfaceOrientationIsPortrait(self.interfaceOrientation) ? ADBannerContentSizeIdentifierPortrait : ADBannerContentSizeIdentifierLandscape;
}
else
{
contentSize = UIInterfaceOrientationIsPortrait(self.interfaceOrientation) ? ADBannerContentSizeIdentifier320x50 : ADBannerContentSizeIdentifier480x32;
}
CGRect frame;
frame.size = [ADBannerView sizeFromBannerContentSizeIdentifier:contentSize];
frame.origin = CGPointMake(0.0f, CGRectGetMaxY(self.view.bounds));
adBanner.frame = frame;
adBanner.delegate = self;
adBanner.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleTopMargin;
adBanner.requiredContentSizeIdentifiers =
(&ADBannerContentSizeIdentifierPortrait != nil) ?
[NSSet setWithObjects:ADBannerContentSizeIdentifierPortrait, ADBannerContentSizeIdentifierLandscape, nil] :
[NSSet setWithObjects:ADBannerContentSizeIdentifier320x50, ADBannerContentSizeIdentifier480x32, nil];
[self.view addSubview:adBanner];
}
- (void)layoutForCurrentOrientation:(BOOL)animated
{
ADBannerView *adBanner = SharedAdBannerView;
CGFloat animationDuration = animated ? 0.2f : 0.0f;
CGRect contentFrame = self.view.bounds;
CGPoint bannerOrigin = CGPointMake(CGRectGetMinX(contentFrame), CGRectGetMaxY(contentFrame));
CGFloat bannerHeight = 0.0f;
if (UIInterfaceOrientationIsLandscape(self.interfaceOrientation))
adBanner.currentContentSizeIdentifier = (&ADBannerContentSizeIdentifierLandscape != nil) ? ADBannerContentSizeIdentifierLandscape : ADBannerContentSizeIdentifier480x32;
else
adBanner.currentContentSizeIdentifier = (&ADBannerContentSizeIdentifierPortrait != nil) ? ADBannerContentSizeIdentifierPortrait : ADBannerContentSizeIdentifier320x50;
bannerHeight = adBanner.bounds.size.height;
if (adBanner.bannerLoaded)
{
contentFrame.size.height -= bannerHeight;
bannerOrigin.y -= bannerHeight;
}
else
{
bannerOrigin.y += bannerHeight;
}
[UIView animateWithDuration:animationDuration
animations:^{
adBanner.frame = CGRectMake(bannerOrigin.x, bannerOrigin.y, adBanner.frame.size.width, adBanner.frame.size.height);
}];
NSLog(#"%f is y pos, height=%f, is it loaded...%#", adBanner.frame.origin.y, adBanner.frame.size.height, adBanner.bannerLoaded?#"YES":#"NO");
}
- (void)bannerViewDidLoadAd:(ADBannerView *)banner
{
[self layoutForCurrentOrientation:YES];
}
- (void)bannerView:(ADBannerView *)banner didFailToReceiveAdWithError:(NSError *)error
{
[self layoutForCurrentOrientation:YES];
}
- (BOOL)bannerViewActionShouldBegin:(ADBannerView *)banner willLeaveApplication:(BOOL)willLeave
{
return YES;
}
- (void)bannerViewActionDidFinish:(ADBannerView *)banner
{
}
#end
I have a UITabbBarController with a UITableView. Under certain circumstances the TabBarControllers dataset requires updating when a user arrives from another view,
e.g. the initial load when the TabBarController is called the first time, or when the settings are changed.
This dataset update takes about 2 seconds and I want to show an UIActivityIndicatorView.
Trouble is that when I enter from another view I don't know which view to attach it to, since the loading of the tabbarController is carried out in the viewWillAppear method.
Any clues how I can go about this?
I've done this sort of thing in the viewDidAppear method. My code kicks off a background task to load the data from a url. It also hands the background task a selector of a method to call on the controller when it is done. That way the controller is notified that the data has been downloaded and can refresh.
I don't know if this is the best way to do this, but so far it's working fine for me :-)
To give some more details, in addition to the selector of the method to call when the background task has loaded the data, I also and it a selector of a method on the controller which does the loading. That way the background task manages whats going on, but the view controller provides the data specific code.
Here's there viewDidAppear code:
- (void) viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
if (reloadData) {
BackgroundTask *task = [[BackgroundTask alloc] initWithMethod:#selector(loadData) onObject:self];
task.superView = self.view.superview;
task.notifyWhenFinishedMethod = #selector(loadFinished);
[task start];
[task release];
}
}
The background task has an optional superView because it will add a new UIView to it containing an activity indicator.
BackgroundTask.m looks like this:
#implementation BackgroundTask
#synthesize superView;
#synthesize longRunningMethod;
#synthesize notifyWhenFinishedMethod;
#synthesize obj;
- (BackgroundTask *) initWithMethod:(SEL)aLongRunningMethod onObject:(id)aObj {
self = [super init];
if (self != nil) {
self.longRunningMethod = aLongRunningMethod;
self.obj = aObj;
}
return self;
}
- (void) start {
// Fire into the background.
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:#selector(execute:)object:nil];
thread.name = #"BackgroundTask thread";
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(taskFinished:) name:NSThreadWillExitNotification object:thread];
[thread start];
[thread release];
}
- (void) execute:(id)anObject {
// New thread = new pool.
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
if (self.superView != nil) {
busyIndicatorView = [[BusyIndicator alloc] initWithSuperview:self.superView];
[busyIndicatorView performSelectorOnMainThread:#selector(addToSuperView)withObject:nil waitUntilDone:YES];
}
// Do the work on this thread.
[self.obj performSelector:self.longRunningMethod];
if (self.superView != nil) {
[busyIndicatorView performSelectorOnMainThread:#selector(removeFromSuperView)withObject:nil waitUntilDone:YES];
}
[pool release];
}
- (void) taskFinished:(NSNotification *)notification {
[[NSNotificationCenter defaultCenter] removeObserver:self name:NSThreadWillExitNotification object:notification.object];
[self performSelectorOnMainThread:#selector(notifyObject)withObject:nil waitUntilDone:NO];
}
- (void) notifyObject {
// Tell the main thread we are done.
if (self.notifyWhenFinishedMethod != nil) {
[self.obj performSelectorOnMainThread:self.notifyWhenFinishedMethod withObject:nil waitUntilDone:NO];
}
}
- (void) dealloc {
self.notifyWhenFinishedMethod = nil;
self.superView = nil;
self.longRunningMethod = nil;
self.obj = nil;
[super dealloc];
}
#end
Finally as I said I put up a activity indicator. I have a xib which contains a 50% transparent blue background with an activity indicator in the middle. There is a controller for it which has this code:
#implementation BusyIndicator
#synthesize superView;
#synthesize busy;
- (BusyIndicator *) initWithSuperview:(UIView *)aSuperView {
self = [super initWithNibName:#"BusyIndicator" bundle:nil];
if (self != nil) {
self.superView = aSuperView;
}
return self;
}
- (void) addToSuperView {
// Adjust view size to match the superview.
[self.superView addSubview:self.view];
self.view.frame = CGRectMake(0,0, self.superView.frame.size.width, self.superView.frame.size.height);
//Set position of the indicator to the middle of the screen.
int top = (int)(self.view.frame.size.height - self.busy.frame.size.height) / 2;
self.busy.frame = CGRectMake(self.busy.frame.origin.x, top, self.busy.frame.size.width, self.busy.frame.size.height);
[self.busy startAnimating];
}
- (void) removeFromSuperView {
[self.busy stopAnimating];
[self.view removeFromSuperview];
}
- (void) dealloc {
self.superView = nil;
[super dealloc];
}
#end
Hoep this helps.