I have a tab based application. I have created an iAd object in app delegate class and using it in my three view controller class. It's working good on second tab's screen and third tab's screen. On second tab there is a table view, when clicking the row of that table view i navigate to the new view where i have used the same code for iAd. On clicking the iAd, iAd screen opens in landscape mode and when closing the screen becomes black and log the following.
[ADHostWindowController supportsOrientation:]: message sent to deallocated instance 0x100bc740
I created the object in app delegate like this:
self.bannerView = [[ADBannerView alloc]init];
[self.bannerView setDelegate:self];
I'm adding banner in view controllers like this:
[[[self appdelegate] bannerView] setFrame:CGRectMake(0, hightofView-180, 768, 66)]
All my view controllers are in portrait but iAds always open in landscape mode.
This is working in iOs 6, but not with iOS 5 on iPad. How do I fix this?
My guess your problem is not related to iAds but rather to memory issues.
It seems that an object of class kind ADHostWindowController is being deallocated prematurely.
My advice would be to make sure that ADHostWindowController is not released i.e (retainCount>=1) before supportsOrientation: is called to it. (which is definitely after the iAd opens).
For diagnosis: Try logging the retain count of that ADHostWindowController (then maybe retaining it one more time) before opening an iAd and see what happens.
Take a look at TabbleBanner code in iAd sample from Apple: https://developer.apple.com/library/ios/#samplecode/iAdSuite/Introduction/Intro.html
I don't investigate it in detail, but you need to follow the Apple iAd guide:https://developer.apple.com/library/ios/#documentation/UserExperience/Conceptual/iAd_Guide/BannerAdvertisements/BannerAdvertisements.html
To create a ADBanner, in each UIViewController, add one to self.view
#property (strong,nonatomic) ADBannerView *bannerView;
- (void)viewDidLoad
{
[super viewDidLoad];
[self createADBanner];
}
- (void)createADBanner{
self.bannerView = [[ADBannerView alloc] initWithFrame:CGRectZero];
self.bannerView.currentContentSizeIdentifier = ADBannerContentSizeIdentifierPortrait;
[self.bannerView setDelegate:self];
[self.view addSubview:self.bannerView];
}
For beginning, you need to modify bannerView size and setCenter if need to place it at top or bottom.
- (void)viewDidLayoutSubviewsj{
if (UIInterfaceOrientationIsLandscape(self.interfaceOrientation)) {
self.bannerView.currentContentSizeIdentifier = ADBannerContentSizeIdentifierLandscape;
self.bannerView.center = CGPointMake(self.view.center.x, self.view.frame.size.height - self.bannerView.frame.size.height/2);
} else {
self.bannerView.currentContentSizeIdentifier = ADBannerContentSizeIdentifierPortrait;
self.bannerView.center = CGPointMake(self.view.center.x, self.view.frame.size.height - self.bannerView.frame.size.height/2);
}
}
And do the same when rotate:
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration{
if (UIInterfaceOrientationIsLandscape(toInterfaceOrientation)) {
self.bannerView.currentContentSizeIdentifier = ADBannerContentSizeIdentifierLandscape;
self.bannerView.center = CGPointMake(self.view.center.x, self.view.frame.size.height - self.bannerView.frame.size.height/2);
} else {
self.bannerView.currentContentSizeIdentifier = ADBannerContentSizeIdentifierPortrait;
self.bannerView.center = CGPointMake(self.view.center.x, self.view.frame.size.height - self.bannerView.frame.size.height/2);
}
}
Related
I have a custom UIImagePickerController that works nicely, only I am facing one issue that I feel should be fairly simple - I just have yet to figure out the solution.
Upon touching my custom added "photo" button, I have it targeted to the build in takePicture method of the UIIPC. Here is my code
#interface CustomCameraController ()
#end
#implementation CustomCameraController {
CGFloat width, height;
}
- (instancetype)init {
if (self = [super init]) {
width = self.view.frame.size.width, height = self.view.frame.size.height;
self.allowsEditing = YES;
self.sourceType = UIImagePickerControllerSourceTypeCamera;
self.showsCameraControls = NO;
self.toolbarHidden = YES;
[self buildCameraOverlay];
}
return self;
}
- (void)buildCameraOverlay {
UIView *customOverlay = [UIView alloc] ...
// ... Custom overlay setup done here
_takePhoto = [[CustomButton alloc] initWithFrame:CGRectMake(0, 0, heightBottomBar*.5, heightBottomBar*.5)];
_takePhoto.center = CGPointMake(bottomBar.frame.size.width/2, bottomBar.frame.size.height/2);
[_takePhoto setImage:[UIImage imageNamed:#"camera button icon"] forState:UIControlStateNormal];
[_takePhoto addTarget:self action:#selector(takePicture) forControlEvents:UIControlEventTouchUpInside];
[bottomBar addSubview:_takePhoto];
// ...
self.cameraOverlayView = customOverlay;
}
This is done in my custom controller CustomCameraController init call.
The problem is, upon taking the picture via takePicture, the camera shutter goes off, everything works just fine, but the controller dismisses itself. I'm trying to figure out how to stop it from closing immediately after taking the picture, so I can A)present the taken picture, and B) give the user the option to choose the image or cancel and retake another one (returning to the camera)
If anyone knows why this happens or something that I am missing / doing incorrectly please let me know. I'm sure it's a simple answer - just can't seem to figure it out. Thanks!
The most common reason for such a weird behaviour is usually lack of delegate methods (for UIImagePickerController in this case) or their wrong implementation.
I have implemented Admob into my app but i've noticed that if the Admob view doesn't receive an ad, I can't remove it from the superview. If it already has an ad loaded it just stays there with that ad loaded even if the device is not connected to the internet. This is my code:
self.admobBannerView = [[GADBannerView alloc] init];
self.admobBannerView.frame = CGRectMake(0.0,self.view.frame.size.height-50,
GAD_SIZE_320x50.width,
GAD_SIZE_320x50.height);
self.admobBannerView.adUnitID = #"...";
self.admobBannerView.rootViewController = self;
self.admobBannerView.delegate = self;
[self.view addSubview:self.admobBannerView];
[self.admobBannerView loadRequest:[GADRequest request]];
Then the Admob delegate
- (void)adView:(GADBannerView *)view didFailToReceiveAdWithError:(GADRequestError *)error {
[self.admobBannerView removeFromSuperview];
}
Despite calling [self.admobBannerView removeFromSuperview]; the banner remains where it is. I can't understand why this is happening. Any help is appreciated.
Thanks
I had the same problem and debugged the view hierarchy w/ Xcode only to find multiple GADBannerViews existed. Fixed the code to check if ad view already existed before adding one.
In your case you should wrap the ad view creation in:
if (self.admobBannerView != nil)
{
// create ad
}
When my app was in development, iAds worked great. Every 30 seconds I would either get a call to "bannerViewDidLoadAd" or to "didFailToReceiveAdWithError" and I prepared the app to handle either callback. I get the green checkmark "You're connected to iAd's App Network" and the other test ads.
Now that the app is live, it only gets "didFailToReceiveAdWithError" and never loads an ad.
I'm running the released version of the app on my phone plugged in to the Xcode Organizer Console, and I see the NSLog that prints within "didFailToReceiveAdWithError"
The iAd Portal doesn't show any requests though, it lists 0 requests.
I've built it to my phone again from XCode with the development profile and again it works as it should. I've deleted the app, shut down my phone, signed out of my iTunes Apple ID, and redownloaded the app from the App Store and still the ad fails every time.
Here's how I've got the ad coded:
In my rootViewController, the user chooses to start a new game, and I animate the new view:
UIViewController *nextController = [[GamePlayViewController alloc] initWithNibName:#"GamePlayView" bundle:nil];
[nextController performSelector:#selector(setDelegate:) withObject:self];
nextController.view.frame = CGRectMake(0, 570, 320, 568);
[self.view addSubview:nextController.view];
[UIView beginAnimations:nil context:nil];
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
[UIView setAnimationDuration:0.23];
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:#selector(animationDidStop:finished:context:)];
nextController.view.frame = CGRectMake(0, 0, 320, 568);
[UIView commitAnimations];
temporaryController = nextController;
GamePlayViewController.h includes:
- #import <iAd/iAd.h>
- #interface GamePlayViewController : UIViewController <ADBannerViewDelegate, UIDocumentInteractionControllerDelegate> {
GamePlayViewController.m includes:
- ADBannerView *_bannerView;
Once the user is in GamePlayViewController.m, there is an animation triggered in viewDidLoad, and once that animation completes, an ad is called:
if ([ADBannerView instancesRespondToSelector:#selector(initWithAdType:)]) {
_bannerView = [[ADBannerView alloc] initWithAdType:ADAdTypeBanner];
} else {
_bannerView = [[ADBannerView alloc] init];
}
_bannerView.delegate = self;
[self.view bringSubviewToFront:_bannerView];
}
That's really all there is to it other than the callback methods for iAds.
- (void)bannerViewDidLoadAd:(ADBannerView *)banner
{
NSLog(#"ad loaded!");
_bannerView.hidden = NO;
[self layoutAnimated:YES];
}
- (void)layoutAnimated:(BOOL)animated
{
// As of iOS 6.0, the banner will automatically resize itself based on its width.
// To support iOS 5.0 however, we continue to set the currentContentSizeIdentifier appropriately.
CGRect contentFrame = self.view.bounds;
if (contentFrame.size.width < contentFrame.size.height) {
_bannerView.currentContentSizeIdentifier = ADBannerContentSizeIdentifierPortrait;
} else {
_bannerView.currentContentSizeIdentifier = ADBannerContentSizeIdentifierLandscape;
}
CGRect bannerFrame = _bannerView.frame;
if (_bannerView.bannerLoaded) {
bannerFrame.origin.y = 0;
} else {
bannerFrame.origin.y = contentFrame.size.height;
}
[UIView animateWithDuration:animated ? 0.25 : 0.0 animations:^{
_contentView.frame = contentFrame;
[_contentView layoutIfNeeded];
_bannerView.frame = bannerFrame;
[self.view addSubview:_bannerView];
}];
}
- (void)bannerView:(ADBannerView *)banner didFailToReceiveAdWithError:(NSError *)error
{
NSLog(#"ad failed!");
_bannerView.hidden = YES;
}
Maybe something I am doing is wrong or I should have the ad on the rootViewController itself, but this code works great with test iAds, so I'm not sure why it's not working with the App Store version of the app.
Thanks for any help!
I've not looked at your code but I've observed this also. Sometimes Apple just hasn't sold any ads so there aren't any to display for you. Consider using an ad aggregator. I use https://github.com/larsacus/LARSAdController but modified it to display ads the way I want to.
I cover that in my blog entry: http://www.notthepainter.com/iad-admob-integration-with-a-dynamic-uiview/
Here is the text of the blog entry so it is preserved here, just in case my blog goes away someday.
I’ve released 2 apps both with iAds. I used Apple’s BannerView sample code to implement this. Basically, in your delegate you don’t set root to your expected root UIViewController but rather you set root to a BannerView which contains your real root. When an iAd is available, your main view shrinks and the iAd is displayed at the bottom. When an ad isn’t available, your view expands to its “normal” size.
This worked very well in testing so I released both apps to the app store. They’re Done Yet? and Wheelin. However, when I first downloaded the versions from the store I was quite surprised to see no ads ever. It turns out that at least right now, iAd had a pretty horrible fill rate. So I wanted to show another ad when an iAd wasn’t available.
I found LARSAdController, an open source project by larsacus on GitHub. He makes ad integration very easy except for one thing. When you go down his quick development route you get the ads covering your view, it doesn’t shrink to accommodate the ad. This is a completely reasonable design decision, just not one I wanted.
So I decided to modify Apple’s BannerView to use LARSAdController. It was pretty easy.
The first thing you do is remove iAd from BannerView’s .h file and ad in the LARS TOLAdViewController class.
#import "TOLAdViewController.h"
#define KVO_AD_VISIBLE #"KVO_AD_VISIBLE"
#interface BannerViewController : TOLAdViewController
(Just ignore the KVO_AD_VISIBLE define for now, I’ll cover that later.) In the .m file also remove iAd and make these changes:
#implementation BannerViewController {
UIView *_bannerView;
UIViewController *_contentController;
BOOL isLoaded;
}
We changed _bannerView from an ADBannerView into a plain old UIVIew. ADBannerView also has a bannerLoaded property which we’ll have to replace with our isLoaded boolean. initWithContentViewController is also easy to modify.
// IAPHelper *sharedInstance = [//IAPHelper sharedInstance];
//if ([sharedInstance showBannerAds]) {
if (YES) {
_bannerView = [[UIView alloc] initWithFrame:CGRectZero];
} else {
_bannerView = nil; // not showing ads since the user has upgraded
}
Notice the commented out section. If you are using in-app purchases to transform an ad supported version into an ad free version you can do that right there.
At the end of the method well use Key-Value-Observing (KVO) to watch LARS and see when an ad is served or removed. (I’ll probably cover KVO in a future blog entry.)
[[LARSAdController sharedManager] addObserver:self
forKeyPath:kLARSAdObserverKeyPathIsAdVisible
options:0
context:KVO_AD_VISIBLE];
And the observing code:
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context;
{
if(context == KVO_AD_VISIBLE) {
NSNumber *isVisible = [object valueForKey:kLARSAdObserverKeyPathIsAdVisible];
if ([isVisible boolValue]) {
_bannerView.frame = [[LARSAdController sharedManager] containerView].frame;
isLoaded = YES;
} else {
isLoaded = NO;
}
}
[self.view setNeedsLayout];
[self.view layoutIfNeeded];
}
We save the frame of the new ad and also update the isLoaded variable. (Originally thought I would need to call setNeedsLayout and layoutIfNeeded but in practice I didn’t.) The mods to viewDidLayoutSubviews were also pretty straightforward. The only odd part was removing the references to ADBannerContentSizeIdentifierPortrait and ADBannerContentSizeIdentifierLandscape and just replacing that all with a single line:
bannerFrame.size = [_bannerView sizeThatFits:contentFrame.size];
And a few lines later you use the new isLoaded variable
if (isLoaded) {
Don’t forget to clean up your observer in dealloc:
-(void) dealloc;
{
[[LARSAdController sharedManager] removeObserver:self forKeyPath:kLARSAdObserverKeyPathIsAdVisible];
}
All that remains is to tell LARS about your ads in your app delegate:
[[LARSAdController sharedManager] registerAdClass:[TOLAdAdapterGoogleAds class] withPublisherId:#"a14e55c99c24b43"];
[[LARSAdController sharedManager] registerAdClass:[TOLAdAdapteriAds class]];
LARSBannerViewController *root = [[LARSBannerViewController alloc] initWithNibName:#"LARSBannerViewController" bundle:nil];
_bannerViewController = [[BannerViewController alloc] initWithContentViewController:root];
[self.window setRootViewController:_bannerViewController];
And that’s it. Now your app should show iAD or AdMob ads and your view will shrink to accommodate them.
Of course there’s a bug, I don’t know if this is in AdMob server or in LARS but when you are in Landscape mode on an iPhone the ad’s size and the reported size are different leaving a black bar at the bottom of the screen. I’ve pinged larsacus about it and will update this post when I know more.
I’ve forked LARSAdController and submitted the above sample code in a full project on my github.
After a few days, I was getting ready to call for support, and then the iAds started working. So I guess the answer is that there is potential for a lag to occur between an app going live and ads populating for that app.
Initially when the ads started coming in, the fill rate wasn't much above 50% and I was only seeing the one ad for iTunes Radio, but at least the ads had started working. Now fill rate is up to 67% and I'm seeing a bit more of a variety with the ads.
Since the iOS7 upgrade, I have a weird behaviour of the UIImagePickerController. In this application I am using the UIImagePickerController with a cameraOverlayView.
In iOS6 I called the UIImagePickerController using the following code:
_picker = [[UIImagePickerController alloc] init];
if ([UIImagePickerController isCameraDeviceAvailable:UIImagePickerControllerCameraDeviceRear]) {
_picker.sourceType = UIImagePickerControllerSourceTypeCamera;
_picker.cameraCaptureMode = UIImagePickerControllerCameraCaptureModePhoto;
_picker.cameraDevice = UIImagePickerControllerCameraDeviceRear;
_picker.showsCameraControls = NO;
_picker.navigationBarHidden = NO;
_picker.toolbarHidden = YES;
_picker.wantsFullScreenLayout = YES;
_overlayViewController = [[OverlayViewController alloc] init];
_overlayViewController.picker = _picker;
_overlayViewController.frameSize = self.frameSize;
_overlayViewController.delegate = self;
_picker.cameraOverlayView = _overlayViewController.view;
}
else {
_picker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
}
_picker.delegate = self;
Where the OverlayViewController is an UIViewController, with a transparent background which draws some custom controls on screen.
But now in iOS 7 the camera is drawn through the statusbar and a black bar appears beneath the live camera view.
I can solve this by applying a CGAffineTransformMakeTranslation to the cameraViewTransform property of the UIImagePickerController, but why is this like this?
In iOS 7, by default UIViewController views take up the entire screen area including the status bar.
wantsFullScreenLayout
is deprecated and ignored. In some cases, this fix works (in the view controller class):
if ([self respondsToSelector:#selector(setEdgesForExtendedLayout:)]) {
[self setEdgesForExtendedLayout:UIRectEdgeNone];
}
In other cases, it's a bit more complicated. It's late here, so see how you go with it. Helpful things to note - in a UIViewController, the following code will give the correct statusbar height on both iOS 6 and iOS 7, should it come to having to align things using CGRect math:
if (UIDeviceOrientationIsLandscape(self.interfaceOrientation)) {
statusBarHeight = [[UIApplication sharedApplication] statusBarFrame].size.width;
} else {
statusBarHeight = [[UIApplication sharedApplication] statusBarFrame].size.height;
}
And then don't forget that in Interface Builder, there are the new "iOS 6 delta" adjustments that allow you to design for iOS 7 and then use offsets to correct for iOS 6.
Anyhow, let me know how you go.
My understanding of the issue, based on a few other SO threads and such, is that UIImagePickerController does not do what we'd expect in terms of managing the status bar via [UIViewController -prefersStatusBarHidden].
This means you either have to disable view controller status bar management entirely, via plist, or figure out a way to get UIImagePickerController to do what we want. On the assumption that you're not looking for the former, I can say I've had success in the latter by putting the picker in a wrapper controller that does what I want (but fall back to your previous code if you still need to detect/support iOS6):
#interface PickerContainer : UIViewController
#property ( nonatomic, weak ) UIImagePickerController* picker;
#end
#implementation PickerContainer
- (void) setPicker: (UIImagePickerController*) picker
{
[self addChildViewController: picker];
[picker didMoveToParentViewController: self];
self->_picker = picker;
}
- (void) viewDidLoad
{
[super viewDidLoad];
self.picker.view.frame = self.view.bounds;
[self.view addSubview: self.picker.view];
}
// Will have no effect in ios6 -- see [-init] for that option
- (BOOL) prefersStatusBarHidden { return YES; }
- (id) init
{
if ( ! ( self = [super init] ) ) return nil;
if ( detectThatThisIsIos6() ) self.wantsFullScreenLayout = YES;
return self;
}
#end
This will work for you, scaled camera, you will have a black bar at the bottom but it will get overlayed by tool bar
https://stackoverflow.com/a/15803947
I'm looking for popover in iPhone and i want to make it like iOS 5 Reader feature:
After little research i found WEPopover and FPPopover but i'm looking if there anything like this API built-in iphone SDK.
You could make a UIView with some custom artwork and display it with an animation on top of your view as a "popover" with some buttons like so:
UIView *customView = [[UIView alloc] initWithFrame:CGRectMake(25, 25, 100, 50)]; //<- change to where you want it to show.
//Set the customView properties
customView.alpha = 0.0;
customView.layer.cornerRadius = 5;
customView.layer.borderWidth = 1.5f;
customView.layer.masksToBounds = YES;
//Add the customView to the current view
[self.view addSubview:customView];
//Display the customView with animation
[UIView animateWithDuration:0.4 animations:^{
[customView setAlpha:1.0];
} completion:^(BOOL finished) {}];
Don't forget to #import <QuartzCore/QuartzCore.h>, if you want to use the customView.layer.
Since iOS8 we are now able to create popovers, that will be the same on iPhone, as on iPad, which would be especially awesome for those who make universal apps, thus no need to make separate views or code.
You can get the class as well as demo project here: https://github.com/soberman/ARSPopover
All you need to do is subclass UIViewController, conform to the UIPopoverPresentationControllerDelegate protocol and set desired modalPresentationStyle along with the delegate value:
// This is your CustomPopoverController.m
#interface CustomPopoverController () <UIPopoverPresentationControllerDelegate>
#end
#implementation CustomPopoverController.m
- (instancetype)init {
if (self = [super init]) {
self.modalPresentationStyle = UIModalPresentationPopover;
self.popoverPresentationController.delegate = self;
}
return self;
}
- (UIModalPresentationStyle)adaptivePresentationStyleForPresentationController:(UIPresentationController *)controller {
return UIModalPresentationNone; //You have to specify this particular value in order to make it work on iPhone.
}
Afterwards, instantiate your newly created subclass in the method from which you want to show it and assign two more values to sourceView and sourceRect. It looks like this:
CustomPopoverController *popoverController = [[CustomPopoverController alloc] init];
popoverController.popoverPresentationController.sourceView = sourceView; //The view containing the anchor rectangle for the popover.
popoverController.popoverPresentationController.sourceRect = CGRectMake(384, 40, 0, 0); //The rectangle in the specified view in which to anchor the popover.
[self presentViewController:popoverController animated:YES completion:nil];
And there you have it, nice, neat blurred popover.