I am trying to present a UIDocumentInteractionController from a navigation controller, when the user selects a file from the tableView.
The interactionControllerWithURL returns NO and the delegate method documentInteractionControllerViewControllerForPreview is never called and the documentInteraction controller does not appear.
The following code is executed when the user selects an item in table:
NSURL *fileURL;
fileURL = (NSURL *)[[DataMng sharedMng] getFileInFolder:self.navigationItem.title atRow:indexPath.row type:type];
if (self.docInteractionController == nil){
self.docInteractionController = [UIDocumentInteractionController interactionControllerWithURL:fileURL];
if (!self.docInteractionController) {
NSLog(#"Selected a file with estension not supported for visualization");
return;
}
self.docInteractionController.delegate = self;
}else{
self.docInteractionController.URL = fileURL;
}
if(! [self.docInteractionController presentPreviewAnimated:YES]){
NSLog(#"ERROR in presenting preview");
}
The delegate controller (self) conforms with UIDocumentInteractionControllerDelegate protocol and is a navigation controller inside a Tabbar controller.
Any ideas will be welcome
I answer my own question:
the form I was launching the docInteractionController was correct, the problem was in the file url, which lack the correct extension (.pdf in my case) and in the controller UTI, which must of the extended form (com.adobe.pdf).
Once correctly set the preview was displayed without problems.
you use these methods to show UIDocumentInteractionController:-
-(UIViewController *)documentInteractionControllerViewControllerForPreview:(UIDocumentInteractionController *)controller
{
return [[[[UIApplication sharedApplication] delegate]window]rootViewController];
}
-(void)documentInteractionControllerDidEndPreview: (UIDocumentInteractionController *)controller
{
self.navigationController.navigationBarHidden = YES;
}
if(! [self.docInteractionController presentPreviewAnimated:YES]){
NSLog(#"ERROR in presenting preview");
}
Think, this is not the right way to present document interactionController. Try setting the below code
CGRect rect = CGRectMake(0, 0, 0, 0);
[self.docInteractionController presentOpenInMenuFromRect:rect inView:self.view animated:YES];
Hope this helps you.
Related
I have two UITableViewControllers A and B, and this is what I'm trying to do when I click on a table view cell in A:
Prepare to segue from A to B by setting some of B's variables from A.
Perform segue from A to B.
B appears.
Display a "Loading" activity indicator with [MBProgressHUD][1].
In a background task, retrieve data from a URL.
If an error occurs in the URL request (either no data received or non-200 status code), (a) hide activity indicator, then (b) display UIAlertView with an error message
Else, (a) Reload B's tableView with the retrieved data, then (b) Hide activity indicator
However, this is what's happening, and I don't know how to fix it:
After clicking a cell in A, B slides in from the right with an empty plain UITableView. The MBProgressHUD DOES NOT SHOW.
After a while, the tableView reloads with the retrieved data, with the MBProgressHUD appearing very briefly.
The MBProgressHUD immediately disappears.
There doesn't seem to be an error with the way the background task is performed. My problem is, how do I display the MBProgressHUD activity indicator as soon as my B view controller appears? (And actually, how come it's not showing?) Code is below.
A's prepareForSegue
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
B *b = (B *)[segue destinationViewController];
// Set some of B's variables here...
}
Relevant methods in B
- (void)viewDidAppear:(BOOL)animated {
[self startOver];
}
- (void)startOver {
[self displayLoadingAndDisableTableViewInteractions];
[self retrieveListings];
[self.tableView reloadData];
[self hideLoadingAndEnableTableViewInteractions];
}
- (void)displayLoadingAndDisableTableViewInteractions {
MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
hud.labelText = #"Loading";
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
self.tableView.userInteractionEnabled = NO;
}
- (void)hideLoadingAndEnableTableViewInteractions {
[MBProgressHUD hideHUDForView:self.view animated:YES];
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
self.tableView.userInteractionEnabled = YES;
}
- (void)retrieveListings {
__block NSArray *newSearchResults;
// Perform synchronous URL request in another thread.
dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
newSearchResults = [self fetchNewSearchResults];
});
// If nil was returned, there must have been some error--display a UIAlertView.
if (newSearchResults == nil) {
[[[UIAlertView alloc] initWithTitle:#"Oops!" message:#"An unknown error occurred. Try again later?" delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil] show];
} else {
// Add the retrieved data to this UITableView's model. Then,
[self.tableView reloadData];
}
}
- (NSArray *)fetchNewSearchResults {
// Assemble NSMutableArray called newSearchResults from NSURLConnection data.
// Return nil if an error or a non-200 response code occurred.
return newSearchResults;
}
I think you have to call [self hideLoadingAndEnableTableViewInteractions]; after newSearchResults = [self fetchNewSearchResults]; You are retrieving data in another thread which means -startOver will continue executing after calling [self retrieveListings]; and will hide the HUD right away. Also because you are updating the display you have to make sure you are doing that on the main thread. See example
dispatch_async(dispatch_get_main_queue(), ^{
//update UI here
});
When B appears, it displays a plain and empty UITableView, but does not display the MBProgressHUD even if the task does begin in the background (and yet, the MBProgressHUD is called to show before that). Hence, my solution is to show the MBProgressHUD in viewDidLoad, which precedes viewWillAppear.
- (void)viewDidLoad {
// ...
[self displayLoadingAndDisableUI];
}
I set up two additional boolean properties to B--one in .h, called shouldStartOverUponAppearing, and one in a class extension in .m, called isLoadingAndDisabledUI. In startOver, I added the following lines:
- (void)startOver {
if (!self.isLoadingAndDisabledUI) {
[self displayLoadingAndDisabledUI];
}
}
The check is done so that startOver doesn't display another MBProgressHUD when it has already been displayed from viewDidLoad. That is because I have a third view controller, called C, that may call on B's startOver, but doesn't need to call viewDidLoad just to display the MBProgressHUD.
Also, this is how I defined viewDidAppear:
- (void)viewDidAppear:(BOOL)animated {
if (self.shouldStartOverUponAppearing) {
[self startOver];
self.shouldStartOverUponAppearing = NO;
}
}
This way, startOver will only be invoked IF B appeared from A. If B appears by pressing "Back" in C, it will do nothing and only display the old data that was there.
I think that this solution is FAR from elegant, but it works. I guess I'll just ask for a better approach in a separate SO question.
I have used a common method for MBProgressHUD.
#import "MBProgressHUD.h" in AppDelegate.h also following methods.
- (MBProgressHUD *)showGlobalProgressHUDWithTitle:(NSString *)title;
- (void)dismissGlobalHUD;
In AppDelegate.m add following methods.
- (MBProgressHUD *)showGlobalProgressHUDWithTitle:(NSString *)title {
[MBProgressHUD hideAllHUDsForView:self.window animated:YES];
MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.window animated:YES];
hud.labelText = title;
return hud;
}
- (void)dismissGlobalHUD {
[MBProgressHUD hideHUDForView:self.window animated:YES];
}
How to use?
AppDelegate *appDel = (AppDelegate *)[[UIApplication sharedApplication]delegate];
//Show Indicator
[appDel showGlobalProgressHUDWithTitle:#"Loading..."];
//Hide Indicator
[appDel dismissGlobalHUD];
Hope this helps.
When I try to dismiss my UIImagePickerController, it crashes the app. the error is: "Terminating app due to uncaught exception 'UIApplicationInvalidInterfaceOrientation', reason: 'preferredInterfaceOrientationForPresentation must return a supported interface orientation!'"
I have the preferred interface orientation set in my view controller.
-(NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskPortrait;
}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
return UIInterfaceOrientationPortrait;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return UIInterfaceOrientationIsPortrait(interfaceOrientation);
}
- (BOOL) shouldAutorotate {
return YES;
}
Here is the method I'm calling to bring up the camera, this works fine for adding the camera, but like I said, crashes when I try to remove the camera.
-(IBAction)addCamera:(id)sender
{
self.cameraController = [[UIImagePickerController alloc] init];
self.cameraController.sourceType = UIImagePickerControllerSourceTypeCamera;
self.cameraController.cameraViewTransform = CGAffineTransformScale(self.cameraController.cameraViewTransform,
1.13f,
1.13f);
self.cameraController.showsCameraControls = NO;
self.cameraController.navigationBarHidden = YES;
self.wantsFullScreenLayout = YES;
ar_overlayView = [[UIView alloc] initWithFrame:CGRectZero];
self.view = ar_overlayView;
[self.cameraController setCameraOverlayView:ar_overlayView];
[self presentViewController:cameraController animated:NO completion:nil];
[ar_overlayView setFrame:self.cameraController.view.bounds];
}
-(IBAction)back:(id)sender
{
[ar_overlayView removeFromSuperview];
[cameraController dismissViewControllerAnimated:NO completion:nil];
}
Alright, found the solution, it was really simple, I just changed my back method to:
[self dismissModalViewControllerAnimated:YES];
Now my camera goes away and I can see my original view when I press the back button.
I still haven't figured out what was causing the original problem as I've gone through the info.plist and the methods for supported orientations, but this accomplishes what I wanted.
I'm still curious as to what was causing the error though if anyone has any ideas.
You can try remove this method:
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
It will fix it.but sometimes it will lead to reduce stateBar height.
You cannot remove a UIViewController's main view from its superview.
Instead of this:
self.view = ar_overlayView;
Try this:
[self.view addSubview:ar_overlayView];
Then you will be able to remove it from the superview correctly.
You should be using the didFinishPickingMedieWithInfo method similar to below and use [self dismissModalViewControllerAnimated:YES];
-(void) imagePickerController:(UIImagePickerController *) picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
if (picker.sourceType == UIImagePickerControllerSourceTypeCamera) {
NSLog(#"Camera");
}
else {
NSLog(#"Album");
}
[self dismissModalViewControllerAnimated:YES];
}
I don't think you need to add ar_overlayView to the current view yourself if it's a camera overlay.
Here's what your code is doing now:
Add ar_overlayView to the current view
Add ar_overlayView as the camera's overlay view
Show the camera view (modal)
At this point, ar_overlayView is being displayed twice. When you send it the removeFromSuperview message on dismissing the camera view, it might be getting confused since it's in two view hierarchies at the same time.
Skipping the self.view = ar_overlayView; or [self.view addSubview:ar_overlayView]; lines should fix the problem.
Also, dismissViewControllerAnimated:completion: should be sent to the same object that presentViewController:animated:completion was called on (self in this case):
-(IBAction)addCamera:(id)sender
{
// -- snip -- //
ar_overlayView = [[UIView alloc] initWithFrame:self.cameraController.view.bounds];
[self.cameraController setCameraOverlayView:ar_overlayView];
[self presentViewController:self.cameraController animated:NO completion:nil];
}
-(IBAction)back:(id)sender
{
[self dismissViewControllerAnimated:NO completion:nil];
}
Hi i am facing a strange problem. I have a class which is NSObject type, and on same class i want to share image on Facebook and Email and also on Twitter. I successfully done FB and Email, On Email i use [[[UIApplication sharedApplication] keyWindow] addSubview:mailer.view]; to show Mail controller onto Window instread of Presenting Model view controller and same as on Dismiss i use Remove from super view. But when i do same with twitter controller then twitter controller just show me few mili seconds and then hide. I also add new View controller onto Window and then Present its controller onto that View controller but same output. Don't know what going wrong. Please help on that. Thanks in advance. This will be great for me :)
Edited
if (_engine != nil) {
_engine = nil;
[_engine release];
}
_engine = [[SA_OAuthTwitterEngine alloc] initOAuthWithDelegate: self];
_engine.consumerKey = kOAuthConsumerKey;
_engine.consumerSecret = kOAuthConsumerSecret;
UIViewController *controller = [SA_OAuthTwitterController controllerToEnterCredentialsWithTwitterEngine:_engine delegate: self];
if (controller) {
NSLog(#"Sharing on Twitter on loading controller of twitter");
[[UIApplication sharedApplication].keyWindow addSubview:controller.view];
}
else {
NSLog(#"Sharing on Twitter in else condition");
[self sharetoTwitter:screenImg];
}
ShareToTwitterMethod
- (void) sharetoTwitter:(UIImage *)img {
NSString *response = [_engine _uploadImage:img requestType:MGTwitterPublicTimelineRequest responseType:MGTwitterStatus];
NSLog(#"twitter post notification");
}
I used Add subview to load view instead of Presenting View.
Have you tried this
UIWindow *window = [[UIApplication sharedApplication] keyWindow];
[window addSubView:controller.view];
[window makeKeyAndVisible];//important line dont forget to set this line
In my Project, each of the user interaction events make a network call (Which is TCP, not HTTP). I need Activity Indicator to be global to show from a random UIViewController and hide from NetworkActivityManager Class (a custom class to handle network activities, Which is not a subclass of UIViewController or UIView).
After searching the web I found out that MBProgressHUD is used for the same purpose, but I wasn't able to find out an example on how would I use it globally. (By saying global I mean a singleton object of MBProgressHUD and class methods to SHOW and HIDE it.)
Following is what I have tried yet, but, failed:
In AppDelegate.h:
#property (nonatomic, retain) MBProgressHUD *hud;
In AppDelegate.m:
#synthesize hud;
In some random UIViewController object:
appDelegate.hud = [MBProgressHUD showHUDAddedTo:appDelegate.navigationController.topViewController.view animated:YES];
appDelegate.hud.labelText = #"This will take some time.";
And while hiding it, from NetworkActivityManager Class:
[MBProgressHUD hideHUDForView:appDelegate.navigationController.topViewController.view animated:YES];
This makes the project to crash after some time (due to memory issues.)
I am using ARC in my project and also, I am using the ARC version of MBProgressHUD.
Am I missing something?
Important Question:
Can I make MBProgressHUD work like UIAlertView? (Saying that I mean implementation of MBProgressHUD independent of UIView -- sa it uses showHUDAddedTo: to present itself) ???
Please Note: In the above code of hiding MBProgressHUD, View may be changed from what it was when showing MBProgressHUD.
Any Help greatly appreciated.
You could add this to a class of your liking:
+ (MBProgressHUD *)showGlobalProgressHUDWithTitle:(NSString *)title {
UIWindow *window = [[[UIApplication sharedApplication] windows] lastObject];
MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:window animated:YES];
hud.labelText = title;
return hud;
}
+ (void)dismissGlobalHUD {
UIWindow *window = [[[UIApplication sharedApplication] windows] lastObject];
[MBProgressHUD hideHUDForView:window animated:YES];
}
This can be than called on any class. You don't need to keep a strong reference to the HUD when using those class convenience methods.
Depending on your specific situation you'll probably also want to handle cases where a new hud is requested before the other one is hidden. You could eater hide the previous hud when a new comes in or come up with some sort of queueing, etc.
Hiding the previous HUD instance before showing a new one is pretty straightforward.
+ (MBProgressHUD *)showGlobalProgressHUDWithTitle:(NSString *)title {
UIWindow *window = [[[UIApplication sharedApplication] windows] lastObject];
[MBProgressHUD hideAllHUDsForView:window animated:YES];
MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:window animated:YES];
hud.labelText = title;
return hud;
}
NOTE...
as with many iOS issues, this is now drastically, totally out of date.
These days you certainly just use a trivial
Container view
for any issue like this.
Full container view tutorial for beginners .. tutorial!
MBProgressHUD was a miraculous solution back in the day, because there was a "drastic hole" in Apple's pipeline.
But (as with many wonderful things from the past), this is only history now. Don't do anything like this today.
Just FWIW, 2014, here's a very simple setup we use. Per David Lawson...
UIWindow *window = [[UIApplication sharedApplication] delegate].window
as Matej says, just use AppDelegate...
#define APP ((AppDelegate *)[[UIApplication sharedApplication] delegate])
AppDelegate.h
// our convenient huddie system (messages with a hud, spinner)
#property (nonatomic, strong) MBProgressHUD *hud;
-(void)huddie;
AppDelegate.m
-(void)huddie
{
// centralised location for MBProgressHUD
[self.hud hide:YES];
UIWindow *windowForHud = [[UIApplication sharedApplication] delegate].window;
self.hud = [MBProgressHUD showHUDAddedTo:windowForHud animated:YES];
self.hud.dimBackground = YES;
self.hud.minShowTime = 0.1;
self.hud.labelText = #"";
self.hud.detailsLabelText = #"";
}
Set the titles in your code where you are using it - because you very often change them during a run. ("Step 1" ... "Step 2" etc)
-(void)loadBlahFromCloud
{
[APP huddie];
APP.hud.labelText = #"Connecting to Parse...";
APP.hud.detailsLabelText = #"step 1/2";
[blah refreshFromCloudThen:
^{
[... example];
}];
}
-(void)example
{
APP.hud.labelText = #"Connecting to the bank...";
APP.hud.detailsLabelText = #"step 2/2";
[blah sendDetailsThen:
^{
[APP.hud hide:YES];
[... showNewDisplay];
}];
}
Change huddle to take the texts as an argument if you wish
You always want self.hud.minShowTime = 0.1; to avoid flicker
Almost always self.hud.dimBackground = YES; which also blocks UI
Conceptually of course you usually have to "slightly wait" to begin work / end work when you bring up such a process, as with any similar programming with the UI.
So in practice code will usually look like this...
-(void)loadActionSheets
{
[APP huddie];
APP.hud.labelText = #"Loading json from net...";
dispatch_after_secs_on_main(0.1 ,
^{
[STUBS refreshNowFromCloudThen:
^{
[APP.hud hide:YES];
dispatch_after_secs_on_main(0.1 , ^{ [self buildActionsheet]; });
}];
}
);
}
Handy macro ..
#define dispatch_after_secs_on_main( SS, BB ) \
dispatch_after( \
dispatch_time(DISPATCH_TIME_NOW, SS*NSEC_PER_SEC), \
dispatch_get_main_queue(), \
BB \
)
This is all history now :) https://stackoverflow.com/a/23403979/294884
This answer is what I've been using for 5-6 Apps now because it works perfectly inside blocks too. However I found a problem with it. I can make it shown, but can't make it disappear if a UIAlertView is also present. If you look at the implementation you can see why. Simply change it to this:
static UIWindow *window;
+ (MBProgressHUD *)showGlobalProgressHUDWithTitle:(NSString *)title {
window = [[[UIApplication sharedApplication] windows] lastObject];
MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:window animated:YES];
hud.labelText = title;
return hud;
}
+ (void)dismissGlobalHUD {
[MBProgressHUD hideHUDForView:window animated:YES];
}
This will make sure you're removing the HUD from the same windows as it was shown on.
I found #Matej Bukovinski 's answer very helpful, since I just started using Swift and my purpose using his methods was to set a global font for the MBProgressHUD, I have converted the code to swift and am willing to share the code below:
class func showGlobalProgressHUDWithTitle(title: String) -> MBProgressHUD{
let window:UIWindow = UIApplication.sharedApplication().windows.last as! UIWindow
let hud = MBProgressHUD.showHUDAddedTo(window, animated: true)
hud.labelText = title
hud.labelFont = UIFont(name: FONT_NAME, size: 15.0)
return hud
}
class func dismissGlobalHUD() -> Void{
let window:UIWindow = UIApplication.sharedApplication().windows.last as! UIWindow
MBProgressHUD.hideAllHUDsForView(window, animated: true)
}
The above code is put into a global file where I keep all my global helpers and constants.
I've used it as below..Hope it helps you..
in appDelegate.m
-(void)showIndicator:(NSString *)withTitleString currentView:(UIView *)currentView
{
if (!isIndicatorStarted) {
// The hud will dispable all input on the view
self.progressHUD = [[[MBProgressHUD alloc] initWithView:currentView] autorelease];
// Add HUD to screen
[currentView addSubview:self.progressHUD];
self.progressHUD.labelText = withTitleString;
[window setUserInteractionEnabled:FALSE];
[self.progressHUD show:YES];
isIndicatorStarted = TRUE;
}
}
-(void)hideIndicator
{
[self.progressHUD show:NO];
[self.progressHUD removeFromSuperview];
self.progressHUD = nil;
[window setUserInteractionEnabled:TRUE];
isIndicatorStarted = FALSE;
}
From Random Views:-
[appDel showIndicator:#"Loading.." currentView:presentView.view];
Note: Considering the views this Question is getting I decided to post the the way I did choose as a solution. This is NOT an answer to my question. (Hence, the accepted answer remains accepted)
At that time I ended up using SVProgressHUD as it was very simple to integrate and use.
All you need to do is just drag the SVProgressHUD/SVProgressHUD folder into your project. (You may choose to go for cocoapods OR carthage, as well)
In Objective-C:
[SVProgressHUD show]; // Show
[SVProgressHUD dismiss]; // Dismiss
In Swift:
SVProgressHUD.show() // Show
SVProgressHUD.dismiss() // Dismiss
Additionally, Show and hide HUD needs to be executed on main thread. (Specifically you would need this to hide the HUD in some closure in background)
e.g.:
dispatch_async(dispatch_get_main_queue(), ^{
[SVProgressHUD dismiss]; // OR SHOW, whatever the need is.
});
There are additional methods for displaying custom messages with HUD, showing success/failure for short duration and auto dismiss.
MBProgressHUD still remains a good choice for developers. It's just that I found SVProgressHUD to suit my needs.
I was using the code from #Michael Shang and having all kinds of inconsistent behavior with showing HUDs. Turns out using the last window is unreliable as the iOS keyboard may just hide it. So in the majority of cases you should get the window using the AppDelegate as mentioned by #David Lawson.
Here's how in Swift:
let window = UIApplication.sharedApplication().delegate!.window!!
let hud = MBProgressHUD.showHUDAddedTo(window, animated: true)
However, with the above your HUD will show up behind the iOS keyboard (if they overlap). If you need your HUD to overlay the keyboard use the last window method.
In my case, what was happening is I would show the HUD then call resignFirstResponder() immediately hiding the window the HUD was added to. So this is something to be aware of, the only window guaranteed to stick around is the first one.
I ended up creating a method that could optionally add the HUD above the keyboard if needed:
func createHUD(size: CGSize, overKeyboard: Bool = false) -> MBProgressHUD {
let window = overKeyboard ? UIApplication.sharedApplication().windows.last!
: UIApplication.sharedApplication().delegate!.window!!
let hud = MBProgressHUD.showHUDAddedTo(window, animated: true)
hud.minSize = size
hud.bezelView.style = .SolidColor
hud.bezelView.color = UIColor(white: 0, alpha: 0.8)
return hud
}
To show the one MBProgressHUD at one time, you can check weather HUD is already added in same view or not. If not, then add the HUD otherwise do not add new HUD.
-(void)showLoader{
dispatch_async(dispatch_get_main_queue(), ^{
BOOL isHudAlreadyAdded = false;
UIWindow *window = [[[UIApplication sharedApplication] windows] lastObject];
NSEnumerator *subviewsEnum = [window.subviews reverseObjectEnumerator];
for (UIView *subview in subviewsEnum) {
if ([subview isKindOfClass:[MBProgressHUD class]]) {
isHudAlreadyAdded = true;
}
}
if(isHudAlreadyAdded == false){
[MBProgressHUD showHUDAddedTo:window animated:YES];
}
});
}
-(void)hideLoader{
dispatch_async(dispatch_get_main_queue(), ^{
UIWindow *window = [[[UIApplication sharedApplication] windows] lastObject];
[MBProgressHUD hideHUDForView:window animated:YES];
});
}
Add these two methods to show or hide loader in your singleton class
- (void)startLoaderWithText:(NSString *)title View:(UIView *)view{
progressHud = [MBProgressHUD showHUDAddedTo:view animated:YES];
progressHud.labelText = title;
progressHud.activityIndicatorColor = [UIColor grayColor];
progressHud.color = [UIColor clearColor];
[progressHud show:YES];
}
- (void)stopLoader{
[progressHud hide:YES];
}
on language switch within my App, I need to access the views of the MoreViewController in a TabBar and change their titles.
Could anyone pls tell me how to do that?
Your help is much appreciated.
Cols
Here are some snippets that might work for you. Note that all of the below is subject to break on each and every new iOS release at it is not meant to be done.
Customizing the More view title as itself as well as the title while it is being edited by the user.
- (void)customizeTitleViewWithNavigationItem:(UINavigationItem *)navigationItem
{
VASSERT(navigationItem != nil, #"invalid navigationItem supplied", navigationItem);
UILabel *titleView = [[UILabel alloc] initWithFrame:CGRectZero];
titleView.backgroundColor = [UIColor clearColor];
titleView.font = [UIFont boldSystemFontOfSize:20.0f];
titleView.text = navigationItem.title;
[titleView sizeToFit];
navigationItem.titleView = titleView;
[titleView release];
}
This needs to be implemented within the UINavigationController's delegate;
- (void)navigationController:(UINavigationController *)navigationController
willShowViewController:(UIViewController *)viewController
animated:(BOOL)animated
{
if (navigationController == tabBarController_.moreNavigationController)
{
if ([viewController isKindOfClass:NSClassFromString(#"UIMoreListController")])
{
[self customizeTitleViewWithNavigationItem:viewController.navigationItem];
}
else
{
NSLog(#"viewController (%#) does not seem to be a UIMoreListController", viewController);
}
}
else
{
NSLog(#"navigationController (%#) does not seem to be the moreNavigationController", navigationController);
}
}
This needs to be implemented within the UITabBarController's delegate;
- (void)tabBarController:(UITabBarController *)controller willBeginCustomizingViewControllers:(NSArray *)viewControllers
{
//get the second view of the upcoming tabbar-controller
UIView *editView = [controller.view.subviews objectAtIndex:1];
//did we get what we expected, which is a UITabBarCustomizeView?
if (editView != nil && [editView isKindOfClass:NSClassFromString(#"UITabBarCustomizeView")])
{ //yes->get the navigation-view
UIView *navigationView = [editView.subviews objectAtIndex:0];
//is that a navigationBar?
if (navigationView != nil && [navigationView isKindOfClass:[UINavigationBar class]])
{ //yes->...
UINavigationBar *navigationBar = (UINavigationBar *)navigationView;
[self customizeTitleViewWithNavigationItem:navigationBar.topItem];
}
else
{
NSLog(#"the navigationView (%#) does not seem to be a navigationBar", navigationView);
}
}
else
{
NSLog(#"the editView (%#) does not seem to be a UITabBarCustomizeView", editView);
}
}
I was able to change the title of one of the tabBar items for my 6th view controller (the last one in the more list) with the following code:
NSArray *vcs = [(UITabBarController *)self.window.rootViewController viewControllers];
[[vcs.lastObject tabBarItem] setTitle:#"New Title"];
Is this what you want to do?
After Edit: To change these titles after they are set up the first time, you need to re-set the viewControllers property of the tabBar controller. In this code example, I connected a button in my 6th view controller to an action method that changes the titles of 3 of my controllers, 2 in the more list and one in the regular list.
-(IBAction)changeNames:(id)sender {
UITabBarController *tbc = (UITabBarController *)[[UIApplication sharedApplication] delegate].window.rootViewController;
NSArray *vcs = tbc.viewControllers;
[[vcs.lastObject tabBarItem] setTitle:#"New Title"];
[[[vcs objectAtIndex:4] tabBarItem] setTitle:#"New VC"];
[[[vcs objectAtIndex:3] tabBarItem] setTitle:#"New VC2"];
tbc.viewControllers = tbc.viewControllers;
}