How to mimic the resizable StatusBar in the Spotify iOS app - iphone

I've been struggling to figure out how Spotify creates the UI for when the app goes into offline mode. They make it seem like the StatusBar has resized, but in reality they're just putting a view below, and resizing all controllers throughout the app. I've tried subclassing UINavigationController, subclassing UIWindow, resizing the window, but nothing seems to work for every case.
The interesting thing about the Spotify app, is that their solution seems to still work when iOS' own UIViewController subclasses are presented modally (as seen in the image below, showing apple's MFMailComposeViewController - you can tell it's not a custom controller because of the UIBarButtonItems).
If anyone has any insight into how this is possible, that would be awesome.

it's a very dangerous thing to do. I've done in the past and I had nightmares. Below the code that works in iOS4 and supports orientation changes.
- (void) _adjustViewControllerforTicker {
TickerView* vv = [ApplicationContext getTickerView];
if ([PreferenceDataModel isFxTickerOn]&& self.navigationController.view.frame.origin.y==0) {
CGRect tableRect = self.tableView.frame;
self.tableView.frame = CGRectMake(tableRect.origin.x,tableRect.origin.y, tableRect.size.width, tableRect.size.height -20);
UINavigationController *nav = self.navigationController;
CGRect gframe = CGRectOffset(self.navigationController.view.frame, 0, 20);
self.navigationController.view.frame = gframe;
if (!vv) {
vv = [[TickerView alloc] initWithFrame:CGRectMake(0, 0, 480, 20)];
[nav.view addSubview:vv];
[vv release];
self.tableView.contentInset=UIEdgeInsetsMake(0,0,20.0,0.0);
[ApplicationContext setTickerView:vv];
}
if (![PreferenceDataModel isTickerOn]) {
self.tableView.contentInset= UIEdgeInsetsZero;
if (vv){
[vv removeFromSuperview];
vv=nil;
[ApplicationContext setTickerView:nil];
}
}
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
[self _adjustViewControllerforTicker];
}
- (void) viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[self _adjustViewControllerforTicker];
TickerView* vv = [ApplicationContext getTickerView];
if ([vv count]) {
[vv startAnimation];
}
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self _adjustViewControllerforTicker];
}
And this is how it looks:

Related

Position an iAd at bottom of screen (both orientations)

I have a Universal app (iPhone/iPad) testing on IOS4.3. It has an iAd which I want to position at the bottom of the screen in both the orientations..
Below is the code;
- (void) viewWillAppear:(BOOL)animated {
adView_.requiredContentSizeIdentifiers = [NSSet setWithObjects: ADBannerContentSizeIdentifierPortrait, ADBannerContentSizeIdentifierLandscape, nil];
adView_.currentContentSizeIdentifier = ADBannerContentSizeIdentifierPortrait;
CGRect adFrame = adView_.frame;
adFrame.origin.y = self.view.frame.size.height-adView_.frame.size.height;
adView_.frame = adFrame;
adView_.delegate = self;
[webView addSubview:adView_];
[self.view bringSubviewToFront:adView_];
self.bannerIsVisible=NO;
[super viewWillAppear: animated];
}
Now for orienation handling, I have;
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
if (UIInterfaceOrientationIsLandscape(toInterfaceOrientation))
self.adView.currentContentSizeIdentifier = ADBannerContentSizeIdentifierLandscape;
else
self.adView.currentContentSizeIdentifier = ADBannerContentSizeIdentifierPortrait;
CGRect adFrame = adView_.frame;
adFrame.origin.y = self.view.frame.size.height-adView_.frame.size.height;
adView_.frame = adFrame;
[webView addSubview:adView_];
[self.view bringSubviewToFront:adView_];
self.bannerIsVisible=NO;
}
My issue is on app load (portrait), I am able to see the iAd clearly positioned at screen bottom.
But as soon as I change orientation to landscape, I cannot see the iAd. I see the message;
ADBannerView: WARNING A banner view (0x62534a0) has an ad but may be
obscured. This message is only printed once per banner view.
I guess I am doing something wrong inside willRotateToInterfaceOrientation
Please help me fix the issue.
Thank you.
Try moving your code to didRotateFromInterfaceOrientation. The new geometry is not set yet in willRotateToInterfaceOrientation.

Force ADBannerView to rotate (Not "orientation" but actual transform)

this is NOT a how do I force orientation of my app question as it might look like.
My problem is probably very simple, but it is hard to describe it so here goes.
I am trying to implement iAd to my iphone game. This is not a problem, it was a 10 minute fix, just follow tutorials on the web. However, my game was programmed by a contractor since I can't program very well and he made the game translated to landscape orientation instead of oriented to landscape. This has leads to some problems for me when trying to rotate the ads correctly.
Bottom line is that CGRect which is what iAd uses does simply not have the transform function so no matter what I do the ads are standing on their side. This is quite natural since the app doesn't know that the game is meant to be played in landscape.
After a full day of research it seems that I need to put my iAd CGRect in a different view and rotate that view using the CGAffineTransformMakeRotation. My really big problem is that I am not good enough at Objective-C to actually do that.
So can you help me how I should be able to apply transform to my ad?
Code below compiles and shows the ad but standing on its side (when holding the game in landscape)
//iAD starts
// lower right:-136, 295, 320, 32 lower left:-136, 135, 320, 32 upper right:136, 295, 320, 32
// *Controller becomes a UIViewController
UIViewController *controller = [[UIViewController alloc] init];
controller.view.frame = CGRectMake(100, 100, 320, 32);
//controller.view.transform = CGAffineTransformMakeRotation(M_PI / 2.0); // turn 180 degrees
NSLog(#"*controller");
//adView becomes a CGRectZero called adView
adView = [[ADBannerView alloc] initWithFrame:CGRectZero];
//adView.frame = CGRectOffset(adView.frame, 0, 0);
adView.requiredContentSizeIdentifiers = [NSSet setWithObjects:ADBannerContentSizeIdentifierLandscape,ADBannerContentSizeIdentifierPortrait,nil];
adView.currentContentSizeIdentifier = ADBannerContentSizeIdentifierLandscape;
[self.view addSubview:adView];
adView.delegate=self;
//self.bannerIsVisible=NO;
// iAD ends
Best regards
Marcus
This should rotate as you are asking.
This code has worked for me in my iAd apps: Directly from Apple Source Code
.h
#import <UIKit/UIKit.h>
#import <iAd/iAd.h>
#interface TextViewController : UIViewController <ADBannerViewDelegate>
{
UIView *contentView;
ADBannerView *banner;
}
#property(nonatomic, retain) IBOutlet UIView *contentView;
#property(nonatomic, retain) IBOutlet ADBannerView *banner;
#end
.m
#import ".h"
#interface TextViewController()
// 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
#implementation TextViewController
#synthesize contentView, banner;
-(void)viewDidLoad{
[super viewDidLoad];
// If the banner wasn't included in the nib, create one.
if(banner == nil)
{
[self createADBannerView];
}
[self layoutForCurrentOrientation:NO];
}
-(void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
[self layoutForCurrentOrientation:NO];
}
-(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation{
return YES;
}
-(void)willAnimateRotationToInterfaceOrientation: (UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration{
[self layoutForCurrentOrientation:YES];
}
-(void)createADBannerView{
// --- WARNING ---
// If you are planning on creating banner views at runtime in order to support iOS targets that don't support the iAd framework
// then you will need to modify this method to do runtime checks for the symbols provided by the iAd framework
// and you will need to weaklink iAd.framework in your project's target settings.
// See the iPad Programming Guide, Creating a Universal Application for more information.
// http://developer.apple.com/iphone/library/documentation/general/conceptual/iPadProgrammingGuide/Introduction/Introduction.html
// --- WARNING ---
// Depending on our orientation when this method is called, we set our initial content size.
// If you only support portrait or landscape orientations, then you can remove this check and
// select either ADBannerContentSizeIdentifierPortrait (if portrait only) or ADBannerContentSizeIdentifierLandscape (if landscape only).
NSString *contentSize;
if (&ADBannerContentSizeIdentifierPortrait != nil)
{
contentSize = UIInterfaceOrientationIsPortrait(self.interfaceOrientation) ? ADBannerContentSizeIdentifierPortrait : ADBannerContentSizeIdentifierLandscape;
}
else
{
// user the older sizes
contentSize = UIInterfaceOrientationIsPortrait(self.interfaceOrientation) ? ADBannerContentSizeIdentifier320x50 : ADBannerContentSizeIdentifier480x32;
}
// Calculate the intial location for the banner.
// We want this banner to be at the bottom of the view controller, but placed
// offscreen to ensure that the user won't see the banner until its ready.
// We'll be informed when we have an ad to show because -bannerViewDidLoadAd: will be called.
CGRect frame;
frame.size = [ADBannerView sizeFromBannerContentSizeIdentifier:contentSize];
frame.origin = CGPointMake(0.0f, CGRectGetMaxY(self.view.bounds));
// Now to create and configure the banner view
ADBannerView *bannerView = [[ADBannerView alloc] initWithFrame:frame];
// Set the delegate to self, so that we are notified of ad responses.
bannerView.delegate = self;
// Set the autoresizing mask so that the banner is pinned to the bottom
bannerView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleTopMargin;
// Since we support all orientations in this view controller, support portrait and landscape content sizes.
// If you only supported landscape or portrait, you could remove the other from this set.
bannerView.requiredContentSizeIdentifiers = (&ADBannerContentSizeIdentifierPortrait != nil) ?
[NSSet setWithObjects:ADBannerContentSizeIdentifierPortrait, ADBannerContentSizeIdentifierLandscape, nil] :
[NSSet setWithObjects:ADBannerContentSizeIdentifier320x50, ADBannerContentSizeIdentifier480x32, nil];
// At this point the ad banner is now be visible and looking for an ad.
[self.view addSubview:bannerView];
self.banner = bannerView;
[bannerView release];
}
-(void)layoutForCurrentOrientation:(BOOL)animated{
CGFloat animationDuration = animated ? 0.2f : 0.0f;
// by default content consumes the entire view area
CGRect contentFrame = self.view.bounds;
// the banner still needs to be adjusted further, but this is a reasonable starting point
// the y value will need to be adjusted by the banner height to get the final position
CGPoint bannerOrigin = CGPointMake(CGRectGetMinX(contentFrame),CGRectGetMaxY(contentFrame));
CGFloat bannerHeight = 0.0f;
// First, setup the banner's content size and adjustment based on the current orientation
if(UIInterfaceOrientationIsLandscape(self.interfaceOrientation))
banner.currentContentSizeIdentifier = (&ADBannerContentSizeIdentifierLandscape != nil) ? ADBannerContentSizeIdentifierLandscape : ADBannerContentSizeIdentifier480x32;
else
banner.currentContentSizeIdentifier = (&ADBannerContentSizeIdentifierPortrait != nil) ? ADBannerContentSizeIdentifierPortrait : ADBannerContentSizeIdentifier320x50;
bannerHeight = banner.bounds.size.height;
// Depending on if the banner has been loaded, we adjust the content frame and banner location
// to accomodate the ad being on or off screen.
// This layout is for an ad at the bottom of the view.
if(banner.bannerLoaded)
{
contentFrame.size.height -= bannerHeight;
bannerOrigin.y -= bannerHeight;
}
else
{
bannerOrigin.y += bannerHeight;
}
// And finally animate the changes, running layout for the content view if required.
[UIView animateWithDuration:animationDuration
animations:^{
contentView.frame = contentFrame;
[contentView layoutIfNeeded];
banner.frame = CGRectMake(bannerOrigin.x, bannerOrigin.y, banner.frame.size.width, banner.frame.size.height);
}];
}
-(void)viewDidUnload{
self.contentView = nil;
banner.delegate = nil;
self.banner = nil;
}
-(void)dealloc{
[contentView release]; contentView = nil;
banner.delegate = nil;
[banner release]; banner = nil;
[super dealloc];
}
#pragma mark ADBannerViewDelegate methods
-(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
If the game is rotated, why don't you stop the rotation of the game?
I managed to do what I wanted by using a different ad SDK (mobfox) the rotation I wanted to do looks like this:
// MOBFOX Starts
// create the banner view just outside of the visible area
MobFoxBannerView *bannerView = [[MobFoxBannerView alloc] initWithFrame:
CGRectMake(-800, self.view.bounds.size.height - 240, 320, 50)];
bannerView.delegate = self; // triggers ad loading
//bannerView.backgroundColor = [UIColor darkGrayColor]; // fill horizontally
bannerView.transform = CGAffineTransformMakeRotation(M_PI / 2.0);
//bannerView.refreshAnimation = UIViewAnimationTransitionCurlDown;
[self.view addSubview:bannerView];
NSLog(#"MobFox: Ad initated and placed offscreen");
//
The transform = CGAffineTransformMakeRotation was not accepted by the iAd stuff and since I am too weak on objective-c to force it to my will. This is what I did.
Thanks for offering to help!

iOS willRotateToInterfaceOrientation proper usage

I have a very simply UIViewController, and I'm trying to figure out how to use willRotateToInterfaceOrientation. my UIViewController has a very simple viewDidLoad method:
-(void)viewDidLoad {
[super viewDidLoad];
theBar = [[UINavigationBar alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 320.0f, 48.0f)];
theBar.tintColor = [UIColor orangeColor];
UINavigationItem *item = [[UINavigationItem alloc] initWithTitle:#"The Title"];
item.hidesBackButton = YES;
[theBar pushNavigationItem:item animated:YES];
[item release];
[self.view addSubview:theBar];
}
So basically, I just have a UINavigationBar at the top of my controller. That's it. I implemented some methods for rotation, based on what I found online:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return YES;
}
-(void)willRotateToInterfaceOrientation: (UIInterfaceOrientation)orientation duration:(NSTimeInterval)duration {
if ((orientation == UIInterfaceOrientationLandscapeLeft) || (orientation == UIInterfaceOrientationLandscapeRight)) {
theBar = [[UINavigationBar alloc] initWithFrame:CGRectMake(0, 0, 640, 48)];
}
}
So, I launch the app in portrait mode, and then I twist in in landscape mode. And basically, theBar still stays it's normal size, and doesn't get resized. I'm sure this is a silly question, but what is the proper way to use the rotation capability? I want to make it so that it also works if the app is launched in landscape mode. What is the best way to initialize my components when the UIViewController first launches, keeping in mind that I want support for both orientations, and also keeping in mind that I want to be able to change the size of everything based on orientation changes throughout the duration of the life of the UIViewController? Thanks!
What you want to do is change the frame of your existing theBar object, and not instantiate a new one. You can do that with something like this:
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)orientation
duration:(NSTimeInterval)duration {
CGRect f = CGRectMake(0,
0,
CGRectGetWidth(self.view.frame),
CGRectGetHeight(theBar.frame);
theBar.frame = f;
}
Note that the value of self.view.frame is used, which contains values post rotation. Also note that the function I'm using here is different than yours. I haven't tested it with the function you're using, so I can't say if that'll work or not. Finally, you can avoid this altogether by just setting the autoresizingmask on theBar in viewDidLoad instead:
[theBar setAutoresizingMask:UIViewAutoresizingFlexibleRightMargin];

iAd cannot be clicked in programmatically created window - bannerViewActionShouldBegin not called?

In an iOS 4.2 application, I've programmatically created a window, view controller, UIView and two subviews: an EAGLView and an ADBannerView. My code is a combination of a standard EAGLView and Apple's BasicAdBanner code.
I'm able to see iAd banners, but am not able to "click" on them - on device or in simulator. The view controller implements ADBannerDelegate, and receives bannerViewDidLoadAd etc messages, but does not ever receive a bannerViewActionShouldBegin message.
The behaviour is not changed by replacing the EAGL view with a UITextView or only having the ADBannerView, so I'm assuming that my problem lies with the programmatic creation of the window or view controller. Any help would be appreciated.
In addition, my views don't rotate/resize automatically like the ad banner sample does, but that's a less pressing issue.
Code Follows-
AppDelegate:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Create the window programatically:
window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
// let's enable multitouch and user interaction
window.userInteractionEnabled=YES;
window.multipleTouchEnabled=YES;
window.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
window.autoresizesSubviews = YES;
controller = [GLViewController alloc];
window.rootViewController = controller;
[window addSubview:controller.view];
glView = [controller.glView retain];
[window makeKeyAndVisible];
return YES;
}
ViewController:
- (void)loadView
{
self.view = [[UIView alloc] initWithFrame:[UIScreen mainScreen].bounds];
}
-(void)viewDidLoad
{
[super viewDidLoad];
// I'd like to get device orientation notifications.
//[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
//[[NSNotificationCenter defaultCenter] addObserver:self
// selector:#selector(didRotate:) name:UIDeviceOrientationDidChangeNotification object:nil];
// the GL VIEW I want.
glView = [[EAGLView alloc] initWithFrame:[UIScreen mainScreen].bounds];
glView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleBottomMargin;
glView.userInteractionEnabled=NO;
[self.view addSubview:glView];
// testing with a textview
//content = [[UITextView alloc] initWithFrame:[UIScreen mainScreen].bounds];
//content.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleBottomMargin;
//content.userInteractionEnabled=NO;
//[self.view addSubview:content];
if (self.banner==NULL)
[self createADBannerView];
[self layoutForCurrentOrientation:NO];
}
-(void)createADBannerView
{
// --- WARNING ---
// If you are planning on creating banner views at runtime in order to support iOS targets that don't support the iAd framework
// then you will need to modify this method to do runtime checks for the symbols provided by the iAd framework
// and you will need to weaklink iAd.framework in your project's target settings.
// See the iPad Programming Guide, Creating a Universal Application for more information.
// http://developer.apple.com/iphone/library/documentation/general/conceptual/iPadProgrammingGuide/Introduction/Introduction.html
// --- WARNING ---
// Depending on our orientation when this method is called, we set our initial content size.
// If you only support portrait or landscape orientations, then you can remove this check and
// select either ADBannerContentSizeIdentifierPortrait (if portrait only) or ADBannerContentSizeIdentifierLandscape (if landscape only).
NSString *contentSize;
if (&ADBannerContentSizeIdentifierPortrait != nil)
{
contentSize = UIInterfaceOrientationIsPortrait(self.interfaceOrientation)
? ADBannerContentSizeIdentifierPortrait : ADBannerContentSizeIdentifierLandscape;
}
else
{
// user the older sizes
contentSize = UIInterfaceOrientationIsPortrait(self.interfaceOrientation)
? ADBannerContentSizeIdentifier320x50 : ADBannerContentSizeIdentifier480x32;
}
// Calculate the intial location for the banner.
// We want this banner to be at the bottom of the view controller, but placed
// offscreen to ensure that the user won't see the banner until its ready.
// We'll be informed when we have an ad to show because -bannerViewDidLoadAd: will be called.
CGRect frame;
frame.size = [ADBannerView sizeFromBannerContentSizeIdentifier:contentSize];
frame.origin = CGPointMake(0.0f, 0.0f);//CGRectGetMaxY(self.view.bounds)); // this can't be called until the view exists!
// Now to create and configure the banner view
ADBannerView *bannerView = [[ADBannerView alloc] initWithFrame:frame];
// Set the delegate to self, so that we are notified of ad responses.
bannerView.delegate = self;
bannerView.hidden = YES;
// Set the autoresizing mask so that the banner is pinned to the bottom
bannerView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleTopMargin;
// Since we support all orientations in this view controller, support portrait and landscape content sizes.
// If you only supported landscape or portrait, you could remove the other from this set.
bannerView.requiredContentSizeIdentifiers = [NSSet setWithObjects:ADBannerContentSizeIdentifierPortrait, ADBannerContentSizeIdentifierLandscape, nil];
//support for 4.0 and 4.1: //(&ADBannerContentSizeIdentifierPortrait != nil) ?[NSSet setWithObjects:ADBannerContentSizeIdentifierPortrait, ADBannerContentSizeIdentifierLandscape, nil]:[NSSet setWithObjects:ADBannerContentSizeIdentifier320x50, ADBannerContentSizeIdentifier480x32, nil];
// At this point the ad banner is now be visible and looking for an ad.
self.banner = bannerView;
[self.view addSubview:bannerView];
[bannerView release];
}
-(void)layoutForCurrentOrientation:(BOOL)animated
{
//CGFloat animationDuration = animated ? 0.2f : 0.0f;
// by default content consumes the entire view area
CGRect contentFrame = self.view.bounds;
// the banner still needs to be adjusted further, but this is a reasonable starting point
// the y value will need to be adjusted by the banner height to get the final position
CGPoint bannerOrigin = CGPointMake(CGRectGetMinX(contentFrame), CGRectGetMaxY(contentFrame));
CGFloat bannerHeight = 0.0f;
// First, setup the banner's content size and adjustment based on the current orientation
if(UIInterfaceOrientationIsLandscape(self.interfaceOrientation))
// if(UIDeviceOrientationIsLandscape([[UIDevice currentDevice] orientation]))
banner.currentContentSizeIdentifier = ADBannerContentSizeIdentifierLandscape;//(&ADBannerContentSizeIdentifierLandscape != nil) ? ADBannerContentSizeIdentifierLandscape : ADBannerContentSizeIdentifier480x32;
else
banner.currentContentSizeIdentifier = ADBannerContentSizeIdentifierPortrait;//(&ADBannerContentSizeIdentifierPortrait != nil) ? ADBannerContentSizeIdentifierPortrait : ADBannerContentSizeIdentifier320x50;
bannerHeight = banner.bounds.size.height;
// Depending on if the banner has been loaded, we adjust the content frame and banner location
// to accomodate the ad being on or off screen.
// This layout is for an ad at the bottom of the view.
if(banner.bannerLoaded)
{
// HMM this is going to be a problem for a GLView..
banner.hidden = NO;
contentFrame.size.height -= bannerHeight;
bannerOrigin.y -= bannerHeight;
NSLog(#"Banner will be %f x %f, content resized to %f x %f\n", banner.frame.size.width,bannerHeight,contentFrame.size.width,contentFrame.size.height);
}
else
{
banner.hidden = YES;
//bannerOrigin.y += bannerHeight;
NSLog(#"Banner will be offscreen, content resized to %f x %f\n", contentFrame.size.width,contentFrame.size.height);
}
//EDIT: contrary to my OP, this UIViewAnimateWithDuration code was not commented out!
// And finally animate the changes, running layout for the content view if required.
[UIView animateWithDuration:animationDuration
animations:^{
glView.frame = contentFrame;
[glView layoutIfNeeded];
banner.frame = CGRectMake(bannerOrigin.x, bannerOrigin.y, banner.frame.size.width, banner.frame.size.height);
}];
}
ADBannerViewDelegate:
-(void)bannerViewDidLoadAd:(ADBannerView *)banner
{
[self layoutForCurrentOrientation:YES];
}
-(void)bannerView:(ADBannerView *)banner didFailToReceiveAdWithError:(NSError *)error
{
NSLog(#"Failed to receive and ad: %s %s %s\n",error.localizedFailureReason.UTF8String, error.localizedDescription.UTF8String, error.localizedRecoverySuggestion.UTF8String);
[self layoutForCurrentOrientation:YES];
}
-(BOOL)bannerViewActionShouldBegin:(ADBannerView *)banner willLeaveApplication:(BOOL)willLeave
{
// this indicates that the banner has been clicked.. confirm?
NSLog(#"banner clicked: will %s application\n",willLeave?"leave":"cover");
return YES;
}
Just to reiterate what forksandhope said,
Placing the ADView in a UIView, then modifying the frame size of both causes the ADView to become un-clickable.
To get around this, create a super UIView and add your UIView and ADView to this view.
For example:
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
// initialize the content UIView and/or UIViewController
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
// copy the content view to a class data member
_contentView = self.view;
// create the super UIView
self.view = [[UIView alloc] initWithFrame:_contentView.frame];
// add the content view to the super view
[self.view addSubview:_contentView];
if (self) {
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(willBeginBannerViewActionNotification:) name:BannerViewActionWillBegin object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(didFinishBannerViewActionNotification:) name:BannerViewActionDidFinish object:nil];
}
return self;
}
Then when an ad is loaded, add the ADView to the super view:
- (void)showBannerView:(ADBannerView *)bannerView animated:(BOOL)animated
{
// ...
// add the AdView to the super view
[self.view addSubview:_adBanner.adBannerView];
// ...
}
Then layout your view as desired
- (void)layoutAnimated:(BOOL)animated
{
// ...
[UIView animateWithDuration:animated ? 0.4 : 0.0 animations:^{
_adBanner.adBannerView.frame = _bannerFrame;
[self.view layoutIfNeeded];
// update the content view AND NOT the super view
_contentView.frame = _contentFrame;
}];
// ...
}
Clarification:
It seems that problem is related to the code attempting to set or animate the content frame changes at the end of -layoutForCorrentOrientation(BOOL). if I only set the banner frame's location, I'm able to click the banner, but if I also attempt to set the glView's frame and recreate the surface, the banner does not respond to input.
//EDIT: contrary to my OP, this UIViewAnimateWithDuration code was not commented out!
// And finally animate the changes, running layout for the content view if required.
//[UIView animateWithDuration:animationDuration
// animations:^{
// glView.frame = contentFrame;
// [glView layoutIfNeeded];
banner.frame = CGRectMake(bannerOrigin.x, bannerOrigin.y, banner.frame.size.width, banner.frame.size.height);
// }];
APENDED:
In the end, the problem was that I have a top level UIView (controller.view) which has the glView and the banner as sub-views. Turns out I needed to call
[self.view setNeedsLayout]
to get this top level view to update its bounds so that it allowed button presses down to the banner! (credit goes to Ephraim's answer to a post on UIButtons not responding after rotations

Twitter-esque UITabBarController?

I'd like to make an app that uses a UITabBarController that is similar to the one in the Twitter app for iPhone and iPod touch. (Blue light for unread and sliding arrow for switching between content views).
Is there an easy way to do this? Any open source code?
Edit:
I've added a bounty. I'm looking for an answer that works across devices and on iOS 4 and iOS 5.
EDIT:
I've messed with my original code and I found a simple solution. I've added the following check, for iOS 5 in my animation code:
// iOS 5 changes the subview hierarchy
// so we need to check for it here
BOOL isUsingVersionFive = NO;
NSString *reqSysVer = #"5.0";
NSString *currSysVer = [[UIDevice currentDevice] systemVersion];
if ([currSysVer compare:reqSysVer options:NSNumericSearch] != NSOrderedAscending){
//On iOS 5, otherwise use old index
isUsingVersionFive = YES;
}
Then, instead of this:
CGFloat tabMiddle = CGRectGetMidX([[[[self tabBar] subviews] objectAtIndex:index] frame]);
... I use this:
CGFloat tabMiddle = CGRectGetMidX([[[[self tabBar] subviews] objectAtIndex:index + (isUsingVersionFive ? 1 : 0)] frame]);
Still holding out on the bounty though, in case I get a chance to find something that works in the answers.
I would create a subclass of the standard UITabBarController and add a couple of subviews for the blue light and the sliding arrow. Shouldn't be too hard to accomplish.
Edit:
Here's an idea of some of the stuff that should go in your subclass. I don't even know if this code will compile.
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
if ((self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil])) {
super.delegate = self;
}
return self;
}
- (void)viewDidLoad {
[super viewDidLoad];
//Create blueLight
UIImage* img = [UIImage imageNamed:#"blue_light.png"]
self.blueLight = [[UIImageView alloc] initWithImage:img];
self.blueLight.center = CGPointMake(320, 460); //I'm using arbitrary numbers here, position it correctly
[self.view addSubview:self.blueLight];
[self.blueLight release];
//Create arrow, similar code.
}
- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController{
//Put code here that moves (animates?) the blue light and the arrow
//Tell your delegate, what just happened.
if([myDelegate respondsToSelector:#selector(tabBarController:didSelectViewController:)]){
[myDelegate tabBarController:self didSelectViewController:viewController]
}
}
Here is how you create the arrow
http://isagoksu.com/2009/development/iphone/fancy-uitabbar-like-tweetie/
And follow Rafael's answer to get the blue light.
Although an ageing post, I though I would interject with a GitHub project that no one mentioned, they've recreated the Twitter styled tab bar quite well, here is the link:
https://github.com/boctor/idev-recipes/tree/master/CustomTabBar
The project is in the sub folder named "CustomTabBar", more information can be found here:
http://idevrecipes.com/2011/01/04/how-does-the-twitter-iphone-app-implement-a-custom-tab-bar/
BCTabBarController is what I based my custom twitter style tab-bar on and it works in iOS 4 and 5:
http://pastebin.me/f9a876a6ad785cb0b2b7ad98b1024847
Each tab view controller should implement the following method for the tabs image:
- (NSString *)iconImageName {
return #"homeTabIconImage.png";
}
(In the App delegate) you would initialize the tab bar controller like so:
self.tabBarController = [[[JHTabBarController alloc] init] autorelease];
self.tabBarController.delegate = self;
self.tabBarController.viewControllers = [NSArray arrayWithObjects:
[[[UINavigationController alloc] initWithRootViewController:[[[iPhoneHomeViewController alloc] init] autorelease]] autorelease],
[[[UINavigationController alloc] initWithRootViewController:[[[iPhoneAboutUsViewController alloc] init] autorelease]] autorelease],
[[[UINavigationController alloc] initWithRootViewController:[[[iPhoneContactInfoViewController alloc] init] autorelease]] autorelease],
[[[UINavigationController alloc] initWithRootViewController:[[[iPhoneMapUsViewController alloc] init] autorelease]] autorelease],
[[[UINavigationController alloc] initWithRootViewController:[[[iPhoneCreditsViewController alloc] init] autorelease]] autorelease],
nil];
Optionally, you can use a custom background image for each tabs view controller with this method:
- (UIImage *)tabBarController:(JHTabBarController *)tabBarController backgroundImageForTabAtIndex:(NSInteger)index {
NSString *bgImagefilePath;
switch (index) {
case 0:
bgImagefilePath = #"homeBG.png";
break;
case 1:
bgImagefilePath = #"aboutBG.png";
break;
case 2:
bgImagefilePath = #"contactBG.png";
break;
case 3:
bgImagefilePath = #"mapBG.png";
break;
case 4:
bgImagefilePath = #"creditsBG.png";
break;
default:
bgImagefilePath = #"homeBG.png";
break;
}
return [UIImage imageNamed:bgImagefilePath];
}
The arrow at the top of the tab bar slides across to the tab that is selected just like the twitter tab bar.
Some screenshots:
Just 2day I made one app like this one for POC, it turns out that it is easier than you can think.
First in my applicationdidfinishlaunching: method I added the imageview holding the image of pointer or arrow with the width as 64 as the screen width is 320 and I have 5 tabs in the tab bar. you can also calculate it if you think that your project will have dynamic tabs and then just change the width of the imageview accordingly something like this.
CGRect rect = tabBarpointerImageView.frame;
rect.size.width = sel.window.frame.size.width / tabbarcontroller.viewControllers.count;
tabBarpointerImageView.frame = rect;
You might also want to set the content mode of that image view as uiviewcontentmodecenter
Also I have set the frame of the imageview by some calculation like this
CGRect rect = CGRectZero;
rect.size.width = self.window.frame.size.width / tabbarcontroller.viewControllers.count;
rect.size.height = 10;// as image was of 10 pixel height.
rect.origin.y = self.window.frame.size.height - 49;// as the height of tab bar is 49
tabBarpointerImageView.frame = rect;
And then in the delegate method of tab bar controller
- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController
just use an animation block to animate the movement of pointer like this
[uiview beginanimation:nil context:nil];
CGRect rect = tabBarpointerImageView.frame;
rect.origin.x = rect.size.width * tabbarcontroller.selectedIndex;
tabBarpointerImageView.frame = rect;
[uiview commitanimations];
PS.
Sorry if some spellings are not correct.