I created an app with raised Tab Bar like that we see in applications such as Foursquare, Instagram or DailyBooth. To do that I put a UIButton at the center of the Tab Bar and everything seems OK, here the code in my AppDelegate:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
self.mainButton = [UIButton buttonWithType:UIButtonTypeCustom];
// set up the mainButton with an image and calculate coordinates
[self.mainButton addTarget:self action:#selector(showMainViewController:) forControlEvents:UIControlEventTouchUpInside];
[self.window addSubview:tabBarController.view];
[self.window addSubview:mainButton];
[self.window makeKeyAndVisible];
}
The mainButton has been defined in AppDelegate.h with its property (non atomic, retain) and synthesized.
By pushing the mainButton:
- (IBAction) showMainViewController:(id)sender {
[self.mainButton setHidden:YES];
MainViewController *mainVC = [[MainViewController alloc] initWithNibName:#"MainViewController" bundle:nil];
UINavigationController *nc = [[UINavigationController alloc] initWithRootViewController:mainVC];
[self.mainNavController presentModalViewController:nc animated:YES];
[nc release];
[mainVC release];
}
Since the mainButton remain diplayed I decided to hide it.
At this point I have a doubt: I have to avoid hiding the button?
Problems comes when I decide to dismiss the MainViewController, I don't know if I'm working properly, I'm doing it by a simple [self dismissModalViewControllerAnimated:YES]; in MainViewController.m
The result is that I'm not abled to unhide the mainButton by something like [self.mainButton setHidden:NO];
This helped me to unhide the button.
NSArray *array = self.tabBarController.view.subviews;
for (int i=0; i<[array count]; i++) {
if ([[array objectAtIndex:i] isKindOfClass:[UIButton class]]){
UIButton *uibtn = (UIButton *) [array objectAtIndex:i];
uibtn.hidden = NO;
}
}
Related
When I add a view controller embedded by navigation controller, to a tab bar, its icon + title disappear briefly when coming back to the More tab.
However when the view controller is added as such, the icon+image are okay and don't disappear.
I've tried many things already, and am out of options. Any ideas?
Here's my AppDelegate code:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.tabBarController = [[UITabBarController alloc] init];
// Must be placed here, just before tabs are added. Otherwise navigation bar
// will overlap with status bar.
[[UIApplication sharedApplication] setStatusBarHidden:NO withAnimation:UIStatusBarAnimationSlide];
[self addViewControllersToTabBar];
self.window.rootViewController = self.tabBarController;
self.window.backgroundColor = [UIColor whiteColor];
[self.window makeKeyAndVisible];
return YES;
}
- (void)addViewControllersToTabBar
{
NSArray* tabBarClassNames =
#[
NSStringFromClass([FirstViewController class]),
NSStringFromClass([SecondViewController class]),
NSStringFromClass([FirstViewController class]),
NSStringFromClass([FirstViewController class]),
NSStringFromClass([FirstViewController class]),
NSStringFromClass([SecondViewController class]),
NSStringFromClass([FirstViewController class]),
];
NSMutableArray* viewControllers = [NSMutableArray array];
for (NSString* className in tabBarClassNames)
{
UIViewController* viewController = [[NSClassFromString(className) alloc] init];
UINavigationController* navigationController;
navigationController = [[UINavigationController alloc] initWithRootViewController:viewController];
[viewControllers addObject:navigationController];
}
[viewControllers addObject:[[FirstViewController alloc] init]]; // This one is fine.
self.tabBarController.viewControllers = viewControllers;
self.tabBarController.selectedViewController = viewControllers[2];
}
and the view controllers are literally nothing more than:
#implementation SecondViewController
- (instancetype)init
{
if (self = [super init])
{
self.title = #"second";
self.tabBarItem.image = [UIImage imageNamed:#"second.png"];
}
return self;
}
#end
Holy shit, spent so much time on this bug, but here's my workaround. Instead of:
navigationController = [[UINavigationController alloc] initWithRootViewController:viewController];
I created my own NavigationController as subclass of UINavigationController:
#implementation NavigationController
- (instancetype)initWithRootViewController:(UIViewController*)rootViewController
{
if (self = [super initWithRootViewController:rootViewController])
{
NSString* className = NSStringFromClass([rootViewController class]);
NSString* name = [className stringByReplacingOccurrencesOfString:#"ViewController" withString:#""];
self.tabBarItem.image = [UIImage imageNamed:[NSString stringWithFormat:#"%#Tab.png", name]];
}
return self;
}
#end
and then do:
navigationController = [[NavigationController alloc] initWithRootViewController:viewController];
Prerequisite is that tab images have the same base name as the view controller class name, which was already the case in my app.
I was setting self.tabBarItem.image inside the view controllers' init method, and this seems to cause the effect I was seeing. So in addition to using my own navigation controller, I also simply deleted setting the tabBarItem in each individual view controller.
I have a MPMoviePlayerController that is initialized as follows:
//Code in my UIViewController
#property (nonatomic, strong) UIView *myVideoView;
#property (nonatomic, strong) MPMoviePlayerController *myVideoPlayer;
- (void) initializeVideoPlayer
{
CGRect frame = CGRectMake(0, 70, self.view.frame.size.width, 200);
self.myVideoView = [[UIView alloc] initWithFrame:frame];
[self.view addSubview:self.myVideoView];
NSURL *videoURL = [NSURL fileURLWithPath:path];
self.myVideoPlayer = [[MPMoviePlayerController alloc] initWithContentURL:videoURL];
self.myVideoPlayer.controlStyle = MPMovieControlStyleEmbedded;
self.myVideoPlayer.shouldAutoplay = YES;
[self.myVideoPlayer.view setFrame: self.myVideoView.bounds];
[self.myVideoView addSubview: self.myVideoPlayer.view];
//Play video
[self.myVideoPlayer prepareToPlay];
[self.myVideoPlayer play];
}
My question is how do I get the video to play in fullscreen when the user rotates the phone to landscape and not fullscreen when the phone is in portrait.
I've tried adding the following to my UIViewController
- (void) willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
if(UIInterfaceOrientationIsLandscape(toInterfaceOrientation))
{
[self.myVideoPlayer setFullscreen:YES animated:YES];
}
else
{
[self.myVideoPlayer setFullscreen:NO animated:YES];
}
}
However, the problem with this is that once the player is in fullscreen, the willAnimateRotationToInterfaceOrientation no longer gets called; therefore, even when the user rotates back to portrait, the video is still in fullscreen.
Try using an MPMoviePlayerViewController instead of an MPMoviePlayerController. Just initialize it in your UIViewController and use its moviePlayer property as you would with a plain MPMoviePlayerController. If you subclass MPMoviePlayerViewController you can control what's happening when the device rotates by implementing willAnimateRotationToInterfaceOrientation etc.
In AppDelegate.h:
#property(nonatomic)BOOL allowRotation;
in AppDelegate.m:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
RootViewController * root = [[RootViewController alloc] init];
self.window.rootViewController = root;
//add two Notification
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(MPVisionVideoNotification:) name:MPMoviePlayerWillEnterFullscreenNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(MPVisionVideoNotification:) name:MPMoviePlayerWillExitFullscreenNotification object:nil];
self.window.backgroundColor = [UIColor whiteColor];
[self.window makeKeyAndVisible];
return YES;
}
- (void) moviePlayerWillEnterFullscreenNotification:(NSNotification*)notification {
self.allowRotation = YES;
}
- (void) moviePlayerWillExitFullscreenNotification:(NSNotification*)notification {
self.allowRotation = NO;
}
-(NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window
{
if (self.allowRotation) {
return UIInterfaceOrientationMaskLandscapeRight ;
}
return UIInterfaceOrientationMaskPortrait;
}
//this can rotate the windows when to fullscreen state
I have an non ARC project. So i am maintaining its memory management. It has an tabbar and navigation controller.In that on launch before showing tab bar i have to show an launch video of 5 sec kind of thing. so i have two question
Best and easy way to show an view controller before attaching tab bar controller to main window without leaks.Following is my current technique and code but code analyzer is showing me potential leaks in my video controller.
self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
UIViewController *viewController = [[MyViewController alloc] initWithNibName:#"MyViewController" bundle:nil] ;
myNavigationController = [ [ UINavigationController alloc ] initWithRootViewController: viewController ];
[viewController release];
NSMutableArray *viewControllers;
viewControllers = [[NSMutableArray alloc] init];
[viewControllers addObject: myNavigationController]; //Tab 1
myNavigationController release];
// ADD Tab 2 //ADD Tab 3 //ADD Tab 4
self.tabBarController = [[[UITabBarController alloc] init] autorelease];
self.tabBarController.viewControllers = viewControllers;
[viewControllers release];
//Add video contoller before showing tabs
self.videoController = [[VideoPlayViewController alloc] initWithNibName:#"VideoPlayViewController" bundle:nil];
[self.window addSubview:videoController.view];
[self.window makeKeyAndVisible];
Here is my Video Player controller code
- (void)viewDidLoad
{
[super viewDidLoad];
MPMoviePlayerController *moviePlayer = [[MPMoviePlayerController alloc] initWithContentURL:#"some url"];
//------ init code in between and added observer to movie playback finish callback ------
[self.view addSubview:moviePlayer.view ]; //show potential leak here if i not release moviePlayer
//[moviePlayer release]; //if i release here controller show me black window with no video playing
}
- (void) moviePlayBackDidFinish:(NSNotification*)notification {
MPMoviePlayerController *player = [notification object];
[[NSNotificationCenter defaultCenter] removeObserver:self
name:MPMoviePlayerDidExitFullscreenNotification
object:nil];
[player stop];
[player.view removeFromSuperview];
[player release]; //show incorrect decrement of reference count of an object that is not owned at this point by caller
//Fire notification to add tab bar as root view controller
}
And after video is played i get notificaton in my appdelegate and then
[videoController.view removeFromSuperview];
[self.videoController release];
self.videoController = nil;
self.window.rootViewController = self.tabBarController;
and my main app delegate dealloc as usual
- (void)dealloc {
[_window release];
[_tabBarController release];
[super dealloc];
}
i think i explained my problem correctly.Please anyone have any better way to do this.
Thanks
Declare the moviePlayer variable outside of viewDidLoad and then release it in moviePlayBackDidFinish. You're adding a reference to it and then removing only that reference. The reason you're being notified about a leak is that moviePlayer is never released - and with the current setup of your code you can't release it.
MPMoviePlayerController *moviePlayer; //keep reference to moviePlayer
- (void)viewDidLoad
{
[super viewDidLoad];
moviePlayer = [[MPMoviePlayerController alloc] initWithContentURL:#"some url"];
[self.view addSubview:moviePlayer.view ];
}
- (void) moviePlayBackDidFinish:(NSNotification*)notification {
[[NSNotificationCenter defaultCenter] removeObserver:self
name:MPMoviePlayerDidExitFullscreenNotification
object:nil];
[moviePlayer stop];
[moviePlayer.view removeFromSuperview];
[moviePlayer release];
}
The problem is pretty simple to understand with pictures.
I have a UINavigationController that allow the user to switch between to views.
The first view contains a search bar and a table view like so :
The second is a basic view where information about the cell are display
When I click on the search bar, the navigation controller gets hidden and the search bar is now at the top.
Now, if I click on a cell, it goes to the second views, but the navigation bar is first hidden like below :
And then, it automatically appears like that :
I have tried a couple of things like show the navigation bar before pushing the next view controller but it is quite ugly..
Does anyone know how to have the show the navigation bar directly on the second view (like in the contact application)?
[UPDATE] : Code
AppDelegate.m (I'm talking about navigationcontroller2)
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
FirstViewController *viewController1 = [[FirstViewController alloc] initWithNibName:#"FirstViewController" bundle:nil];
SecondViewController *viewController2 = [[SecondViewController alloc] initWithNibName:#"SecondViewController" bundle:nil];
viewController1.managedObjectContext = [self managedObjectContext];
viewController2.managedObjectContext = [self managedObjectContext];
viewController1.viewController2 = viewController2;
UINavigationController *navigationcontroller1 = [[UINavigationController alloc] initWithRootViewController:viewController1];
[navigationcontroller1.navigationBar setTintColor:[UIColor lightGrayColor]];
UINavigationController *navigationcontroller2 = [[UINavigationController alloc] initWithRootViewController:viewController2];
[navigationcontroller2.navigationBar setTintColor:[UIColor lightGrayColor]];
self.tabBarController = [[UITabBarController alloc] init];
self.tabBarController.viewControllers = [NSArray arrayWithObjects:navigationcontroller1, navigationcontroller2, nil];
self.window.rootViewController = self.tabBarController;
[self.window makeKeyAndVisible];
return YES;
}
FirstView.m
- (void) searchBarTextDidBeginEditing:(UISearchBar *)theSearchBar {
[self.navigationController setNavigationBarHidden:YES animated:YES];
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if (!noResultsToDisplay) {
PinDetailsViewController *pinDetailsViewController = [[PinDetailsViewController alloc] initWithNibName:#"PinDetailsViewController" bundle:nil];
NSManagedObject *managedObject = [fetchedResultsController objectAtIndexPath:indexPath];
Pin *pin = (Pin *) managedObject;
[self.navigationItem setTitle:#"Pins"];
[self.navigationController pushViewController:pinDetailsViewController animated:YES];
[pinDetailsViewController updateWithPin:pin];
}
}
If you need anything else, just ask but I think it's all there.
Try to use this code in each viewcontroller.
- (void) viewWillAppear:(BOOL)animated
{
[self.navigationController setNavigationBarHidden:NO animated:animated];
}
- (void) viewWillDisappear:(BOOL)animated
{
[self.navigationController setNavigationBarHidden:YES animated:animated];
}
Before you push the new view controller, you should unhide the navigation bar:
[self.navigationController setNavigationBarHidden:NO animated:YES];
I had a similar problem with the position of my navbar. Mine was moving up behind the status bar, and I fixed the issue by manually setting the navbar frame:
-(void)adjustNavBarOrigin
{
CGRect r = self.navigationController.navigationBar.frame;
r.origin = CGPointMake(0, 20); // 20 is the height of the status bar
self.navigationController.navigationBar.frame = r;
}
I had to call this method in a number of places, including viewWillAppear: and didRotateFromInterfaceOrientation:, but it worked a treat :)
Hiding the UINavigationBar can disturb the properties sometimes. Try using the property alpha instead of hidden.
I am currently making a camera app for iPhone and I have a strange phenomenon that I can't figure out. I would appreciate some help understanding.
When recreating an overlay view for passing to UIImagePickerController, I have been successfully been able to create the view programmatically. What I haven't been able to do is create the view with/without controller in IB, load it and pass it to the overlay pointer successfully. If I do it via IB, the view is not opaque and obscures the view of the camera completely. I can not figure out why.
I was thinking that the normal view pointer might be assigned when loading from XIB and therefore overwrite the camera's view, but I have an example programmatically where view and overlayView are set equal in the controller class. Perhaps the load order is overwriting a pointer?
Help would be appreciated... kind regards.
This works fine...
- (void)applicationDidFinishLaunching:(UIApplication *)application {
// dislodge the MainView from the MainViewController
//
MainViewController *aController = [[MainViewController alloc] init]; //WithNibName:#"MainView" bundle:nil];
aController.sourceType= UIImagePickerControllerSourceTypeCamera;
aController.delegate= aController;
aController.showsCameraControls= NO;
// Programmatically load the MainView from the Nib and assign it as the OverlayView instead
//
NSArray* nibViews= [[NSBundle mainBundle] loadNibNamed:#"MainView" owner:aController options:nil];
UIView* uiView= [nibViews objectAtIndex:0];
uiView.opaque= NO;
uiView.backgroundColor= [UIColor clearColor];
aController.cameraOverlayView= uiView;
self.mainViewController = aController;
[aController release];
mainViewController.view.frame = [UIScreen mainScreen].applicationFrame;
[window addSubview:[mainViewController view]];
[window makeKeyAndVisible];
}
This DOESN'T...
- (void)applicationDidFinishLaunching:(UIApplication *)application {
// dislodge the MainView from the MainViewController
//
MainViewController *aController = [[MainViewController alloc] initWithNibName:#"MainView" bundle:nil];
aController.sourceType= UIImagePickerControllerSourceTypeCamera;
aController.delegate= aController;
aController.showsCameraControls= NO;
aController.cameraOverlayView= aController.view;
self.mainViewController = aController;
[aController release];
mainViewController.view.frame = [UIScreen mainScreen].applicationFrame;
[window addSubview:[mainViewController view]];
[window makeKeyAndVisible];
}