Hide iAd banner when no ads are available - iphone

This is a Phonegap app. I am completely lost here, please help.
I followed this tutorial to the t.
Here is my MainViewController.m:
- (void)webViewDidFinishLoad:(UIWebView*)theWebView
{
adView = [[ADBannerView alloc] initWithFrame:CGRectZero];
if([UIApplication sharedApplication].statusBarOrientation ==
UIInterfaceOrientationPortrait ||
[UIApplication sharedApplication].statusBarOrientation ==
UIInterfaceOrientationPortraitUpsideDown) {
adView.currentContentSizeIdentifier = ADBannerContentSizeIdentifierPortrait;
}
else {
adView.currentContentSizeIdentifier = ADBannerContentSizeIdentifierLandscape;
}
adView.currentContentSizeIdentifier = ADBannerContentSizeIdentifierPortrait;
CGRect adFrame = adView.frame;
adFrame.origin.y = self.view.frame.size.height-adView.frame.size.height;
adView.frame = adFrame;
[self.view addSubview:adView];
// Black base color for background matches the native apps
theWebView.backgroundColor = [UIColor blackColor];
return [super webViewDidFinishLoad:theWebView];
}

Fast answer:
What you need to do is conform to the ADBannerViewDelegate protocol and hide the ad when it fails to receive an ad.
Step by step version:
In that method you listed, you'll want to also include adView.delegate = self.
In MainViewController.h, where it says #interface MainViewController : UIViewController, you want to add <ADBannerViewDelegate> after, like this:
#interface MainViewController : UIViewController <ADBannerViewDelegate>. If there is already something in < > there, just add a comma and add ADBannerViewDelegate.
Back in the MainViewController.m, add these methods:
- (void)bannerViewDidLoadAd:(ADBannerView *)banner {
adView.hidden = NO;
}
- (void)bannerView:(ADBannerView *)banner didFailToReceiveAdWithError:(NSError *)error {
adView.hidden = YES;
}

Related

Why isn't AdWhirl displaying ads when it fetches okay?

Following the Ray Wenderlich tutorial, I've incorporated the latest AdWhirl folders, deleted the adapters I'm not using, setup an account with iAd, uploaded a few house banners, configured the AdWhirl delivery and implemented the retrieve/display code in my Cocos2d app. My logs show that ads are being retrieved successfully but they do not appear on the screen. I'm thinking either they are displayed off screen (no man's land) or are hidden behind a CCLayer. Any suggestions for how to test/resolve this is much appreciated. It's a landscape only orientation if that helps.
Update: the RW tutorial is out of date for Cocos2d version 2.0 users...see below for the solution
Here is my AdWhirl code:
Library.h
#import "AdWhirlView.h"
#import "AdWhirlDelegateProtocol.h"
#import "RootViewController.h"
#interface Library : CCLayer <AdWhirlDelegate>{
this is what the tutorial said but is outdated
//RootViewController *viewController;
this is what is needed instead
UINavigationController *viewController
AdWhirlView *adWhirlView;
enum GameStatePP _state; }
#property(nonatomic,retain) AdWhirlView *adWhirlView;
#property(nonatomic) enum GameStatePP state;
Library.mm
#implementation Library
#synthesize state = _state, adWhirlView;
In Init
self.state = kGameStatePlaying;
Then the AdWhirl methods
- (void)adWhirlWillPresentFullScreenModal {
if (self.state == kGameStatePlaying) {
[[CCDirector sharedDirector] pause];} }
- (void)adWhirlDidDismissFullScreenModal {
if (self.state == kGameStatePaused)
return;
else {
self.state = kGameStatePlaying;
[[CCDirector sharedDirector] resume]; }}
- (NSString *)adWhirlApplicationKey {
return #"myadWhirlKeyGoesHere"; }
- (UIViewController *)viewControllerForPresentingModalView {
return viewController;}
-(void)adjustAdSize {
[UIView beginAnimations:#"AdResize" context:nil];
[UIView setAnimationDuration:0.2];
CGSize adSize = [adWhirlView actualAdSize];
CGRect newFrame = adWhirlView.frame;
newFrame.size.height = adSize.height;
CGSize winSize = [CCDirector sharedDirector].winSize;
newFrame.size.width = winSize.width;
newFrame.origin.x = (self.adWhirlView.bounds.size.width - adSize.width)/2;
newFrame.origin.y = (winSize.height - adSize.height);
adWhirlView.frame = newFrame;
[UIView commitAnimations]; }
- (void)adWhirlDidReceiveAd:(AdWhirlView *)adWhirlVieww {
[adWhirlView rotateToOrientation:UIInterfaceOrientationLandscapeRight];
[self adjustAdSize];
NSLog(#"Library - adWhirlDidReceiveAd");
NSLog(#"%#",[adWhirlView mostRecentNetworkName]); }
-(void)adWhirlDidFailToReceiveAd:(AdWhirlView *)adWhirlVieww usingBackup:(BOOL)yesOrNo {
NSLog(#"Library - adWhirlDidFailToReceiveAd");
NSLog(#"%#",[adWhirlView mostRecentNetworkName]); }
-(void)onEnter {
this is what the tutorial said and is wrong for version Cocos2d 2.0 users
//viewController = [(AppDelegate *)[[UIApplication sharedApplication] delegate] viewController];
this is the correct way
viewController = [(AppDelegate *)[[UIApplication sharedApplication] delegate] navController];
self.adWhirlView = [AdWhirlView requestAdWhirlViewWithDelegate:self];
self.adWhirlView.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin|UIViewAutoresizingFlexibleRightMargin;
[adWhirlView updateAdWhirlConfig];
CGSize adSize = [adWhirlView actualAdSize];
CGSize winSize = [CCDirector sharedDirector].winSize;
self.adWhirlView.frame = CGRectMake((winSize.width/2)-(adSize.width/2),winSize.height-adSize.height,winSize.width,adSize.height);
self.adWhirlView.clipsToBounds = YES;
[viewController.view addSubview:adWhirlView];
[viewController.view bringSubviewToFront:adWhirlView];
[super onEnter]; }
-(void)onExit {
if (adWhirlView) {
[adWhirlView removeFromSuperview];
[adWhirlView replaceBannerViewWith:nil];
[adWhirlView ignoreNewAdRequests];
[adWhirlView setDelegate:nil];
self.adWhirlView = nil;
}
[super onExit]; }
- (void) dealloc {
self.adWhirlView.delegate = nil;
self.adWhirlView = nil; }
In My AppDelegate.h
Wrong:
//RootViewController *viewController;
//UIViewController *viewController;
this is right and is provided in the 2.0 Cocos2d template so doesn't need to be added, just needs to be used:
UINavigationController *navController_;
And so these are not needed
//#property (nonatomic, retain) RootViewController *viewController;
//#property (nonatomic, retain) UIViewController *viewController;
AppDelegate.mm
#synthesize window=window_, navController=navController_, director=director_;
not needed because we will be using the navController instead:
//viewController = viewController_;
DidFinishLaunchingWithOptions (with all references to viewController commented out)
//viewController = [[UIViewController alloc] initWithNibName:nil bundle:nil];
//viewController.wantsFullScreenLayout = YES;
CCGLView *glView = [CCGLView viewWithFrame:[window_ bounds]
pixelFormat:kEAGLColorFormatRGB565
depthFormat:0
preserveBackbuffer:NO
sharegroup:nil
multiSampling:NO
numberOfSamples:0];
director_ = (CCDirectorIOS*) [CCDirector sharedDirector];
director_.wantsFullScreenLayout = YES;
[director_ setDisplayStats:NO];
[director_ setAnimationInterval:1.0/60];
[director_ setDelegate:self];
[director_ setProjection:kCCDirectorProjection2D];
[director_ setView:glView];
if( ! [director_ enableRetinaDisplay:YES] )CCLOG(#"Retina Display Not supported");
navController_ = [[UINavigationController alloc] initWithRootViewController:director_];
navController_.navigationBarHidden = YES;
this is the important line, setting the navController
[window_ addSubview:navController_.view];
[glView setMultipleTouchEnabled:YES];
//[viewController_ setView:glView];
//[window_ addSubview:viewController_.view];
[window_ makeKeyAndVisible];
[CCTexture2D setDefaultAlphaPixelFormat:kCCTexture2DPixelFormat_RGBA8888];
[CCFileUtils setiPadSuffix:#"-ipad"];
[CCFileUtils setRetinaDisplaySuffix:#"-hd"];
[CCTexture2D PVRImagesHavePremultipliedAlpha:YES];
[director_ pushScene: [TitleScene scene]];
return YES;
You specify the viewController with this line:
viewController = [(AppDelegate *)[[UIApplication sharedApplication] delegate] viewController];
Are you sure that your appDelegate is setting its viewController property? In other words, is viewController nil? I would check that.
If you still have this problem, use the code below.
Your previous is
[viewController.view addSubview:adWhirlView];<br>
[viewController.view bringSubviewToFront:adWhirlView];
My suggestion is
[[CCDirector sharedDirector].view addSubview:adWhirlView];<br>
[[CCDirector sharedDirector].view bringSubviewToFront:adWhirlView];
Maybe, after that can you see ads.

When does bounds get updated?

I am loading a view as soon as the user rotates to landscape but when I use view.bounds in viewDidLoad it is still in portrait.
Here the code where I load the view which is triggered from a notification
- (void)orientationChanged:(NSNotification *)notification
{
UIDeviceOrientation deviceOrientation = [UIDevice currentDevice].orientation;
if (UIDeviceOrientationIsLandscape(deviceOrientation) && !isShowingLandscapeView)
{
[self presentModalViewController:self.graphViewController animated:YES];
isShowingLandscapeView = YES;
} else if (deviceOrientation == UIDeviceOrientationPortrait && isShowingLandscapeView)
{
[self dismissModalViewControllerAnimated:YES];
isShowingLandscapeView = NO;
}
}
And in the graphViewController in viewDidLoad I use self.view.bounds to initialise a core-plot.
This is how it looks like
Any hint on how to fix that?
Thanks a lot
EDIT
in viewDidLoad
// Create graph from theme
graph = [[CPTXYGraph alloc] initWithFrame:self.view.bounds];
CPTTheme *theme = [CPTTheme themeNamed:kCPTDarkGradientTheme];
[graph applyTheme:theme];
CPTGraphHostingView *hostingView = [[CPTGraphHostingView alloc] initWithFrame:self.view.bounds];
hostingView.collapsesLayers = NO; // Setting to YES reduces GPU memory usage, but can slow drawing/scrolling
hostingView.hostedGraph = graph;
[self.view addSubview:hostingView];
Assuming CPTGraphHostingView will adjust the orientation of how it renders, you need to set the autoresizingMask on it. This determines how views resize when their superviews resize, which is happening in this case.
hostingView.autoresizingMask = (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight);
It's not the correct approach. You should be doing this stuff in layoutSubviews. It will be called first time in, and subsequently each time orientation changes. Do something like this:
- (void)layoutSubviews
{
[super layoutSubviews];
CGRect frame = self.bounds;
// Do your subviews layout here based upon frame
}

How to change orientation of main windows and other views navigated by window dynamically?

I have a code in which all UI items created by dynamically. I have a navigation controller which connect to other view to main window. In this I have a problem that when I write a code for orientation change then it not work automatically.
pragma mark MainViewController
#implementation MainViewController
#synthesize imageView;
#synthesize btnBegin, btnSite;
#synthesize phoneNumber;
- (void)viewDidLoad { self.title=#"Midwest Sleep Test";self.view.backgroundColor = [UIColor whiteColor];
CGRect myImageRect = CGRectMake(-10,-10,320,313);
imageView = [[UIImageView alloc] initWithFrame:myImageRect];
[imageView setImage:[UIImage imageNamed:#"midwestsleep.png"]];
[self.view addSubview:imageView];
[imageView release];
btnBegin = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[btnBegin addTarget:self action:#selector(Begin:) forControlEvents:UIControlEventTouchUpInside];
[btnBegin setTitle:#"Begin!" forState:UIControlStateNormal];
btnBegin.frame = CGRectMake(112,295,95,45);
[self.view addSubview:btnBegin];
CGRect myLabelRect = CGRectMake(112,345,130,22);
phoneNumber=[[UILabel alloc] initWithFrame:myLabelRect];
phoneNumber.text=#"937-350-5645";
phoneNumber.textColor=[UIColor blueColor];
phoneNumber.font= [UIFont fontWithName:#"ComicSansMsBold" size:18];
[self.view addSubview:phoneNumber];
btnSite = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[btnSite addTarget:self action:#selector(Site:) forControlEvents:UIControlEventTouchUpInside];
[btnSite setTitle:#"www.midwestsleepmed.com" forState:UIControlStateNormal];
btnSite.frame = CGRectMake(1,370,320,22);
[self.view addSubview:btnSite];
}
- (BOOL)shouldAutorotateToInterfaceOrientation: (UIInterfaceOrientation)interfaceOrientation{
return YES;}
enter code-(IBAction)Begin:(id)sender{
SecondView *sV = [[SecondView alloc] init];
[self.navigationController pushViewController:sV animated:YES];
[sV release];}
`-(IBAction)Site:(id)sender{
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:#"http://www.midwestsleepmed.com"]];}
pragma mark AppDelegate
pragma mark -
#implementation MidWestSleepAppDelegate
#synthesize window;
#synthesize viewController;
#synthesize score;
- (void)applicationDidFinishLaunching:(UIApplication *)application{
LogMethod();
// If you want the status bar to be hidden at launch use this:
// application.statusBarHidden = YES;
//
// To set the status bar as black, use the following:
// application.statusBarStyle = UIStatusBarStyleBlackOpaque;
// Create window
window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// this helps in debugging, so that you know "exactly" where your views are placed;
// if you see "red", you are looking at the bare window, otherwise use black
// window.backgroundColor = [UIColor redColor];
viewController = [ [ MainViewController alloc ] init ];
navigationController = [ [ UINavigationController alloc ] initWithRootViewController: viewController ];
/* Anchor the view to the window */
[window addSubview:[navigationController view]];
/* Make the window key and visible */
[window makeKeyAndVisible];}
- (BOOL)shouldAutorotateToInterfaceOrientation: (UIInterfaceOrientation)interfaceOrientation{
return YES;}
#end
In above code when i implement then i get problem in simulator that is in landscape mode whole uiview becomes different and not getting their own position. What it is problem in this code and how do I get it fixed?
-(void)willAnimateSecondHalfOfRotationFromInterfaceOrientation:
(UIInterfaceOrientation)fromInterfaceOrientation duration:(NSTimeInterval)duration
{
UIInterfaceOrientation myview=self.interfaceOrientation;
[UIView beginAnimations:#"move Button" context:nil];
if (myview==UIInterfaceOrientationPortrait || myview==UIInterfaceOrientationPortraitUpsideDown)
{ //here is the frames for portrait orientation
coverimage.frame=CGRectMake(2, 2, 315, 310);
myscrollview.frame=CGRectMake(2, 305,320, 480);
}
else
{
//here is the frames for landscape orientation
coverimage.frame=CGRectMake(65,10,330,180);
myscrollview.backgroundColor=[UIColor clearColor];
myscrollview.frame=CGRectMake(2, 150,500,250);
}
[UIView commitAnimations];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait ||
interfaceOrientation==UIInterfaceOrientationLandscapeLeft ||
interfaceOrientation == UIInterfaceOrientationLandscapeRight||
interfaceOrientation == UIInterfaceOrientationPortraitUpsideDown);
}

View controller not getting -shouldAutorotateToInterfaceOrientation: messages on second load?

I have a UIViewController that I'm using to control a "pop-up" view for viewing images throughout my application. It supports autorotation, as it automatically sizes the image to fit properly regardless of orientation. This works perfectly, but only the first time I initialize and display the view controller. When it closes, I am removing the UIView from my view hierarchy and releasing the view controller - but the next time I instantiate and add it to my view hierarchy, it stops receiving the -shouldAutorotateToInterfaceOrientation messages when the phone is rotated.
This is how I instantiate and display it:
popupVC = [[PopupVC alloc] init];
[popupVC viewWillAppear:NO];
[[[UIApplication sharedApplication] keyWindow] addSubview:popupVC.view];
[popupVC viewDidAppear:NO];
this is how I remove/release it when it's finished:
[popupVC viewWillDisappear:NO];
[popupVC.view removeFromSuperview];
[popupVC viewDidDisappear:NO];
[popupVC release];
popupVC = nil;
I've tried looping through [[UIApplication sharedApplication] keyWindow] subviews to see if somehow my popup view isn't on top, but it always is. And it has a different address each time so I do know that it's a different instance of the view controller class.
As requested, here is the complete loadView method from PopupVC:
- (void)loadView {
UIView *myView = [[UIView alloc] initWithFrame:[UIScreen mainScreen].applicationFrame];
myView.backgroundColor = self.overlayColor;
myView.autoresizesSubviews = NO;
myView.hidden = YES;
myView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
self.view = myView;
[myView release];
_isVisible = NO;
UIView *myMaskView = [[UIView alloc] initWithFrame:self.view.bounds];
myMaskView.backgroundColor = [UIColor clearColor];
myMaskView.clipsToBounds = YES;
myMaskView.hidden = YES;
myMaskView.autoresizesSubviews = NO;
myMaskView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
[self.view addSubview:myMaskView];
self.imageMaskView = myMaskView;
[myMaskView release];
UIImageView *myImageView = [[UIImageView alloc] initWithFrame:CGRectZero];
myImageView.center = self.view.center;
myImageView.hidden = NO;
myImageView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleHeight;
[self.imageMaskView addSubview:myImageView];
self.imageView = myImageView;
[myImageView release];
UIButton *myImageButton = [UIButton buttonWithType:UIButtonTypeCustom];
myImageButton.frame = self.view.frame;
myImageButton.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleHeight;
[myImageButton addTarget:self action:#selector(clickImage:) forControlEvents:UIControlEventTouchUpInside];
[self.imageMaskView addSubview:myImageButton];
self.imageButton = myImageButton;
UIActivityIndicatorView *myActivityView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
myActivityView.hidden = YES;
[self.view addSubview:myActivityView];
myActivityView.center = self.view.center;
self.activityView = myActivityView;
[myActivityView release];
}
The shouldAutorotateToInterfaceOrientation handler isn't the place for custom code to respond to rotation events. It's purpose is just to tell the OS that your view controller can rotate. If you want to have custom code to handle the rotation events you should overide - didRotateFromInterfaceOrientation or one of the other similar callback methods depending on your needs:
willRotateToInterfaceOrientation:duration:
willAnimateRotationToInterfaceOrientation:duration:
didRotateFromInterfaceOrientation:
willAnimateFirstHalfOfRotationToInterfaceOrientation:duration:
didAnimateFirstHalfOfRotationToInterfaceOrientation:
willAnimateSecondHalfOfRotationFromInterfaceOrientation:duration:
See Autorotating Views in the developer docs.
I think that might be a bug in the new OS 3.0. A workaround to this would be to use NSNotificationCenter after turning on beginGeneratingDeviceOrientationNotifications.
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(receivedRotate:) name:UIDeviceOrientationDidChangeNotification object:nil];
}
-(void) receivedRotate: (NSNotification *) notification {
DebugLog(#"ORIENTATION CHANGE");
UIDeviceOrientation interfaceOrientation = [[UIDevice currentDevice] orientation];
if(interfaceOrientation == UIDeviceOrientationPortrait) {
self.view.transform = CGAffineTransformMakeRotation(degreesToRadian(0));
self.view.bounds = CGRectMake(0, 0, 320, 480);
}
else if(interfaceOrientation == UIDeviceOrientationLandscapeLeft) {
self.view.transform = CGAffineTransformMakeRotation(degreesToRadian(90));
self.view.bounds = CGRectMake(0, 0, 480, 320);
}
else if(interfaceOrientation == UIDeviceOrientationLandscapeRight) {
self.view.transform = CGAffineTransformMakeRotation(degreesToRadian(-90));
self.view.bounds = CGRectMake(0, 0, 480, 320);
}
}
My problem is that I get the rotation event for my rootViewController, but as soon as I launch my second viewController, it won't get any signals except motion (shaking) and touch. Is that the same issue as the original post -- it is certainly similiar??
I followed-up with above recommendation and I don't have the selector coded correctly so it throws an exception as soon as I try to rotate. Meanwhile, I found this other discussion on the web which confirms that the problem was introduced in 3.0.
link text

How to get UIMenuController work for a custom view?

I'm trying to get the following code work:
UIMenuController * menu = [UIMenuController sharedMenuController];
[menu setTargetRect: CGRectMake(100, 100, 100, 100) inView: self.view];
[menu setMenuVisible: YES animated: YES];
The menu instance is ready but it doesn't show - the width is always zero.
Or is there some sample code on this UIPasteboard/UIMenuController topic?
I was not able to get it working even when I read all of your answers. I'm presenting ready code that will work for everyone.
Let's say we have a controller class named Controller. You can simply paste the following code to this controller to have the menu working on its view:
- (void)loadView {
[super loadView];
UILongPressGestureRecognizer *gr = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(longPress:)];
[self.view addGestureRecognizer:gr];
}
- (void) longPress:(UILongPressGestureRecognizer *) gestureRecognizer {
if ([gestureRecognizer state] == UIGestureRecognizerStateBegan) {
CGPoint location = [gestureRecognizer locationInView:[gestureRecognizer view]];
UIMenuController *menuController = [UIMenuController sharedMenuController];
UIMenuItem *resetMenuItem = [[UIMenuItem alloc] initWithTitle:#"Item" action:#selector(menuItemClicked:)];
NSAssert([self becomeFirstResponder], #"Sorry, UIMenuController will not work with %# since it cannot become first responder", self);
[menuController setMenuItems:[NSArray arrayWithObject:resetMenuItem]];
[menuController setTargetRect:CGRectMake(location.x, location.y, 0.0f, 0.0f) inView:[gestureRecognizer view]];
[menuController setMenuVisible:YES animated:YES];
}
}
- (void) copy:(id) sender {
// called when copy clicked in menu
}
- (void) menuItemClicked:(id) sender {
// called when Item clicked in menu
}
- (BOOL) canPerformAction:(SEL)selector withSender:(id) sender {
if (selector == #selector(menuItemClicked:) || selector == #selector(copy:)) {
return YES;
}
return NO;
}
- (BOOL) canBecomeFirstResponder {
return YES;
}
What has to be done in order for menu to work is that the firstResponder(in our case our controller - see line with [self becomeFirstResponder]) has to be able to become first responder (override method canBecomeFirstResponder cause default implementation returns NO) as well as - (BOOL) canPerformAction:(SEL)selector withSender:(id) sender which should return YES to any action that can be performed by firstResponder
If you're implementing a custom view and that view is supposed to be the responder (as opposed to some other view, like a UITextField), you need to override the canBecomeFirstResponder function in your view and return YES:
- (BOOL)canBecomeFirstResponder {
return YES;
}
then, when you're displaying the menu, you should do something like the following:
- (void)myMenuFunc {
if (![self becomeFirstResponder]) {
NSLog(#"couldn't become first responder");
return;
}
UIMenuController *theMenu = [UIMenuController sharedMenuController];
CGRect selectionRect = CGRectMake(0, 0, 0, 0);
[theMenu setTargetRect:selectionRect inView:self];
[theMenu setMenuVisible:YES animated:YES];
}
In case somebody still has problems: My menu used to work and some day stopped working miraculously. Everything else in my app still worked. Now I had removed the [window makeKeyAndVisible] method from application:didFinishLaunchingWithOptions: method, and while everything else still worked, this breaks UIMenuController!
Stupid error on my side, difficult to find the culprit...
to display UIMenuController one has to add following
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender
{
if (action == #selector(cut:))
return NO;
else if (action == #selector(copy:))
return YES;
else if (action == #selector(paste:))
return NO;
else if (action == #selector(select:) || action == #selector(selectAll:))
return NO;
else
return [super canPerformAction:action withSender:sender];
}
I think Cam is right, need override both canPerformAction and canBecomeFirstResponder
- (BOOL) canPerformAction:(SEL)action withSender:(id)sender
{
if (action == #selector(doSomething:)) {
return YES;
}
return NO;
}
- (BOOL)canBecomeFirstResponder {
return YES;
}
// MyView.h
#interface MyView : UIView {
IBOutlet UITextField * textField_;
}
#end
// MyView.m
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
NSLog(#"show menu");
[textField_ becomeFirstResponder];
// [self.window becomeFirstResponder];
UIMenuController * menu = [UIMenuController sharedMenuController];
[menu setTargetRect: CGRectMake(0, 0, 100, 10) inView: self];
[menu setMenuVisible: YES animated: YES];
NSLog(#"menu width %f, visible %d", menu.menuFrame.size.width, menu.menuVisible);
}
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender{
return YES;
}
You need to add more code in canPerformAction:withSender: - it should check the pasteboard and your selection state. Apple's iPhone Application Programming Guide provide several code snippets.
Don't you have to add the UIMenuController* menu to the main or subview, E.G. self.view?
I think it's something like [self.view addSubView:menu.view]; Or am I missing the point of your question. You might also want to set the frame of the menu's view.
UIMenuController doesn't have a view. I just searched some code from apple's iPhone Application Programming Guide: Event Handling:
Listing 3-4 Displaying the editing menu
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *theTouch = [touches anyObject];
if ([theTouch tapCount] == 2 && [self becomeFirstResponder]) {
// selection management code goes here...
// bring up editing menu.
UIMenuController *theMenu = [UIMenuController sharedMenuController];
CGRect selectionRect = CGRectMake(currentSelection.x, currentSelection.y, SIDE, SIDE);
[theMenu setTargetRect:selectionRect inView:self];
[theMenu setMenuVisible:YES animated:YES];
}
}
I did it in the following way below. Just call the method that shows the menu after very short delay in init. I didn't want to call it from View Controller and also didn't find an event that indicates that my custom view appeared and i'm ready to show menu. So this way OK from my perspective. Delay can be less, but its up to you.
#implementation DTSignatureImageView
- (id)initWithImage:(UIImage *)image
{
self = [super initWithImage:image];
if(self){
self.contentMode = UIViewContentModeScaleAspectFit;
self.frame = CGRectMake(0, 0, image.size.width / 2.5, image.size.height / 2.5);
self.userInteractionEnabled = YES;
UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(signatureDidPan:)];
[self addGestureRecognizer:pan];
[self becomeFirstResponder];
[self performSelector:#selector(showMenu) withObject:nil afterDelay:0.5];
}
return self;
}
- (BOOL)canBecomeFirstResponder
{
return YES;
}
- (void)showMenu
{
UIMenuController *menu = [UIMenuController sharedMenuController];
menu.menuItems = #[
[[UIMenuItem alloc] initWithTitle:#"Apply" action:#selector(applySignature)],
[[UIMenuItem alloc] initWithTitle:#"Update" action:#selector(updateSignature)],
[[UIMenuItem alloc] initWithTitle:#"Clear" action:#selector(delegateSignature)]];
[menu setTargetRect:self.bounds inView:self];
[menu setMenuVisible:YES animated:YES];
}
- (NSArray *)menuActions
{
static NSArray *actions = nil;
if (actions == nil){
actions = #[
NSStringFromSelector(#selector(applySignature)),
NSStringFromSelector(#selector(updateSignature)),
NSStringFromSelector(#selector(delegateSignature))];
}
return actions;
}
- (void) signatureDidPan: (UIPanGestureRecognizer *)gesture
{
switch (gesture.state) {
case UIGestureRecognizerStateBegan: {
[[UIMenuController sharedMenuController] setMenuVisible:NO animated:YES];
break;
}
case UIGestureRecognizerStateEnded: {
[self becomeFirstResponder];
[self showMenu];
}
default:
break;
}
CGPoint point = [gesture locationInView:gesture.view.superview];
gesture.view.center = point;
}