I'm new to native iOS development, and have been playing with localizing storyboards.
I've been localizing the text for UILabel and UIButton objects in my storyboard by updating the Main.strings files for the storyboard:
// UIButton:
"cEx-Yi-RY8.normalTitle" = "Done";
// UILabel:
"1l2-H9-hRc.text" = "Safety information!";
How do I do the same for a UITabBarItem? I have tried:
//UITabBarItem:
"oSH-y1-hFoB.title" = "Scan";
But it doesn't work :(
I don't get why... I can see it is possible to update the text manually in the UITabBarController:
- (void)viewDidLoad
{
//...
item.title = NSLocalizedString(#"scan", nil);
}
But then I have to put those translations in a separate Localizable.strings file, which seems lame.
On the other hand, at least the translations in Localizable.strings are somewhat readable, i.e. "scan" = "Scan"; rather than "oSH-y1-hFoB.title" = "Scan"; ...
Still... I'm not following why some stuff works in Interface Builder but other stuff doesn't and you have to do it manually.
I hate having to add all this boilerplate code to do repetitive stuff like localizing text in the UI, when the framework should just be able to do it for me. (If it can do it for a UIButton, why not a UITabBarItem...)
Or am I just overlooking something?
You can localize UITabBarItem exactly how you localize other storyboard components.
In my opinion, the main reason this doesn't work in your case, is because in a Storyboard you have two title: one on the tab bar, another on the view controller...and you are using the wrong ID :-)
Starting from a new project, tabbed application, click on the project (on the left), the Project (not target) on the right, info tab, add a language in the Localizations (I used Italian). Leave all defaults.
Xcode will create this .strings file for the Italian language:
...
/* Class = "IBUIViewController"; title = "First"; ObjectID = "CZ4-MO-1fc"; */
"CZ4-MO-1fc.title" = "First";
/* Class = "IBUIViewController"; title = "Second"; ObjectID = "QUy-BD-bpt"; */
"QUy-BD-bpt.title" = "Second";
/* Class = "IBUITabBarItem"; title = "Second"; ObjectID = "Z7h-1E-pvt"; */
"Z7h-1E-pvt.title" = "Second";
/* Class = "IBUITabBarItem"; title = "First"; ObjectID = "u1e-5L-l1D"; */
"u1e-5L-l1D.title" = "First";
....
As you can see, there are two titles, one on the VC, another on the TabBarItem.
You have to update the titles marked with IBUITabBarItem
Another option is to set the titles on the tab bar item to NSLocalizedStrings programmatically.
tabBar.items![0].title = NSLocalizedString("tab1", comment: "")
tabBar.items![1].title = NSLocalizedString("tab2", comment: "")
tabBar.items![2].title = NSLocalizedString("tab3", comment: "")
tabBar.items![3].title = NSLocalizedString("tab4", comment: "")
While you can localize UITabBarItems just like any other StoryBoard component, be aware that if you set a title for the associated view controller, it will overwrite the title of the UITabBarItem. That will prevent your localizations from appearing.
obviously you can set it in your view controller. You can set this action in .m file. It also worked if you are using storyboard.
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
self.tabBarItem.title =NSLocalizedString(#"Alerts",nil);
}
return self;
}
or you can also write it in your viewDidLoad method.
self.tabBarItem.title =NSLocalizedString(#"Alerts",nil);
=============== EDIT ================
Let me update for swift with example of storyboard.
This is out UIStoryBoard TabBarController.
And set Identifier for tabbarController in storyboard as
Now in Appdelegate let we set its titles.
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
let story = UIStoryboard(name: "Main", bundle: nil)
let tabVC = story.instantiateViewController(withIdentifier: "tabBarController") as! UITabBarController
let names = [NSLocalizedString("First", comment: "First Tab"), NSLocalizedString("Second", comment: "Second Tab"), NSLocalizedString("Third", comment: "Third Tab")]
var index = 0
if let views = tabVC.viewControllers {
for tab in views {
tab.tabBarItem.title = names[index]
index = index + 1
}
}
self.window?.rootViewController = tabVC
return true
}
And the result is
As you can see its showing tab bar titles with updated localised text. And it will set titles at once.
Related
Is there a way I could create gui for ios using like xml in android. Storyboard is so heavy and make my computer unresponsive. I can't find any decent tutorial for creating gui through coding it
Well storyboard is an XML file only. As you can see by right clicking on it and opening it as a source file.
You can create a storyboard and open it as a source file and it will open as an XML file. But mind it, its not a single Scene, its the whole scene of your application as a single XML file.
So you can configure it like that.
first try with .XIB. it gives you amazing features of auto-layout functionality.
or you can create any view with code. eg. if you are using swift you can create your view like this.
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = UIColor.whiteColor()
self.navigationController?.navigationBarHidden = true
/////creating navigation bar//////////
var _customNavView = UIView(frame: CGRectMake(0, 0, self.view.frame.size.width, 64))
_customNavView.backgroundColor = headerColor
self.view.addSubview(_customNavView)
var _lblMapTitle = UILabel(frame: CGRectMake(_customNavView.center.x-75,self.view.frame.origin.y+30,150,30))
_lblMapTitle.text = "SETTINGS"
_lblMapTitle.textColor = UIColor.whiteColor()
_lblMapTitle.font = UIFont(name: "ErasITC-Demi", size: 18)
_lblMapTitle.textAlignment = .Center
_customNavView.addSubview(_lblMapTitle)
_imgCoverPic.frame = CGRectMake(0, _customNavView.frame.origin.y+_customNavView.frame.height, self.view.frame.size.width,160)
_imgCoverPic.contentMode = .ScaleAspectFill
_imgCoverPic.alpha = 0.5
_imgCoverPic.clipsToBounds = true
self.view.addSubview(_imgCoverPic)
_imgPicturePic.frame = CGRectMake(self.view.frame.width/2-50, _imgCoverPic.frame.origin.y+30, 110 ,110)
_imgPicturePic.clipsToBounds = true
_imgPicturePic.layer.borderColor = UIColor.whiteColor().CGColor
_imgPicturePic.layer.borderWidth = 3
_imgPicturePic.layer.cornerRadius = _imgPicturePic.frame.width/2
self.view.addSubview(_imgPicturePic)
////////creating table view///////////
var heightForTab :CGFloat = AppDelegate.sharedInstance.heightForTabBar as! CGFloat
//println(heightForTab)
get_all_status_value_for_user()
_tblSettings.frame = CGRectMake(0,_imgCoverPic.frame.origin.y+_imgCoverPic.frame.size.height+15, self.view.frame.size.width, self.view.frame.size.height-heightForTab-64)
_tblSettings.separatorStyle = UITableViewCellSeparatorStyle.None
_tblSettings.showsVerticalScrollIndicator = false
// _tblSettings.rowHeight = UITableViewAutomaticDimension
// _tblEvent.backgroundColor = UIColor.redColor()
self.view.addSubview(_tblSettings)
}
As Mohit tomar answer states, i would suggest using just xibs instead, they are a bit more light weight to edit. But what I would personally do, because storyboards give more functionality than xibs, is to break up your storyboard into very small storyboards, no more than 3 viewcontrollers on one. Then in code, if you need to segue to a viewcontroller on another storyboard, you can just load the storyboard manually and then segue to a particular viewcontroller on it programatically.
Its even possible to make your own custom segue that will segue to a view controller on another storyboard without having to write much code
#interface StoryboardLinkSegue () {
NSString *segueType;
}
#end
#implementation StoryboardLinkSegue
+ (UIViewController *)viewControllerNamed:(NSString *)identifier
{
NSArray *info = [identifier componentsSeparatedByString:#"#"];
NSString *storyboardName = info[1];
NSString *viewControllerName = info[0];
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:storyboardName
bundle:nil];
UIViewController *scene = nil;
if (viewControllerName.length == 0 || [viewControllerName isEqualToString:#"default"]) {
scene = [storyboard instantiateInitialViewController];
}
else {
scene = [storyboard instantiateViewControllerWithIdentifier:viewControllerName];
}
return scene;
}
- (id)initWithIdentifier:(NSString *)identifier
source:(UIViewController *)source
destination:(UIViewController *)destination
{
NSArray *info = [identifier componentsSeparatedByString:#"#"];
if(info.count > 2){
segueType = [info[2] lowercaseString];
}
else {
segueType = #"push";
}
return [super initWithIdentifier:identifier
source:source
destination:[StoryboardLinkSegue viewControllerNamed:identifier]];
}
- (void)perform
{
if([segueType isEqualToString:#"push"]){
[[self.sourceViewController navigationController] pushViewController:self.destinationViewController animated:YES];
}
else if ([segueType isEqualToString:#"modal"]) {
[self.sourceViewController presentViewController:self.destinationViewController animated:YES completion:nil];
}
}
now if you make a segue to an empty viewcontroller in your storyboard, just put the segue identifier as <storyboardName>#<viewControllerName> and to be technical you can use <storyboardName>#<viewControllerName>#modal or <storyboardName>#<viewControllerName>#push to make it a modal or push segue. This will make it segue from one storyboard to another without having to write any code (besides copy pasting the code ive given you)
example: mainStoryboard#loginViewController
will go to the viewcontroller named loginViewController that is in the mainStoryboard
you just need to give all your viewcontrollers names in your storyboards under the identity inspector -> Storyboard ID
So I have an application with different buttons, labels and some Text Views in storyboard where I entered the text directly in the storyboard. I enabled base localization and added a couple of languages.
This generated storyboards for Base (English) and the other languages with a list of items objectIDs.
I translated everything, and the labels and buttons (ALL OF THEM) work and show in the language I set the device to.
The text fields however keep showing the initial English text no matter which language I set...
Are there any extra steps involved for Text View?
So, I did some research, and it seems that in order for this to work correctly, the text for the UITextView needs to be set programmatically.
Source: Devforums.apple
Quote:
as I understand it, strings such as the text property of a text view/field have to be set in code using NSLocalizedString. The first 1/2 hour of WWDC 2013 video session #219 Making Your App World Ready covers this if you have the time to watch it
So, it seems that the workaround (if you don't want to set the text programmatically) is to convert the strings file into a storyboard before shipping the app. This does seem to work as intended and shows the UITextView properly localized.
EDIT: Found another workaround that allows to keep .strings file.
In - (void)viewDidLoad:
for(UIView* v in self.view.subviews)
{
if([v isKindOfClass:[UITextView class]])
{
UITextView* txv = (UITextView*)v;
NSString *loctxt = [txv.text stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
txv.text = NSLocalizedString(loctxt, #"");
}
}
This produces a Percent Escapes encoded string from whatever is inside the storyboard, like this:
Hello%20World
In your Localizable.strings file, you use the above as the key, and this will produce the localized text in the app at runtime for the selected locale, like this:
"Hello%20World" = "Hallo Welt";
The Percent escaping takes care of all escape characters in the base string.
As my comment on Dmitry's answer cannot be formatted nicely, I repeat this as an answer here. The Swift version of his solution looks like this:
override func viewDidLoad() {
super.viewDidLoad()
for view in self.view.subviews {
if let tv = view as? UITextView, ident = view.restorationIdentifier {
tv.text = NSLocalizedString("\(ident).text", tableName: "Main", comment: "")
}
}
// Do any additional setup after loading the view.
}
(Note that in Swift, NSLocalizedString replaces several Objective-C macros, one of them being NSLocalizedStringFromTable)
P.S.: Unfortunately, in iOS 10 this seems not to work any more. Instead, the call gives back the id that was supplied as first parameter (e.g. "abc-xy-pqr.text"). Any ideas?
If anyone is still interested, I have solved this problem a different way, this will allow you to still use the SAME .Strings file that is generated by Xcode for storyboards.
There are two parts to this solution:
In the .m file for your view add this code:
- (void)viewDidLoad
{
[super viewDidLoad];
for(UIView* view in self.view.subviews)
{
if([view isKindOfClass:[UITextView class]] && view.restorationIdentifier)
{
UITextView* textView = (UITextView*)view;
NSString *textViewName = [NSString stringWithFormat:#"%#.text",textView.restorationIdentifier];
textView.text = NSLocalizedStringFromTable(textViewName, #"Main", nil);
//change this to be the same as the name of your storyboard ^^^
}
}
// Do any additional setup after loading the view.
}
and in your storyboard in the identity inspector copy the "Object ID" to the "Restoration ID" field.
This will apply the new localized text to all of your UITextViews on screen load and will allow you to use the already generated strings files.
I made my own Categories for the components.
For example, a button:
#import <UIKit/UIKit.h>
#interface LocalizedButton : UIButton
#end
#import "LocalizedButton.h"
#implementation LocalizedButton
- (id)initWithCoder:(NSCoder *)aDecoder {
NSLog(#"Loading LocalizedButton: initWithCoder");
if ((self = [super initWithCoder:aDecoder])){
[self localizeButton];
}
return self;
}
- (id)initWithFrame:(CGRect)frame{
NSLog(#"Loading LocalizedButton: initWithFrame");
self = [super initWithFrame:frame];
if (self) {
[self localizeButton];
}
return self;
}
-(void) localizeButton{
self.titleLabel.adjustsFontSizeToFitWidth = YES;
NSString* text = NSLocalizedString(self.titleLabel.text, nil);
[self setTitle:text forState:UIControlStateNormal];
[self setTitle:text forState:UIControlStateHighlighted];
[self setTitle:text forState:UIControlStateDisabled];
[self setTitle:text forState:UIControlStateSelected];
}
#end
You can se the complete code on: https://github.com/exmo/equizmo-ios/blob/master/Quiz/LocalizedButton.m
The Swift solution of https://stackoverflow.com/users/1950945/stefan works for me on iOS 10.2 when I replace the "Main" with the correct id (e.g. "MainStoryboard") which references the localized file id.storyboard (e.g. "MainStoryboard.storyboard")
I want to use standard tab bar item with custom title. I change title after TabBarItem creating directly self.tabBarItem.title = #"Liked". For example "Favorites" - UITabBarSystemItemFavorites:
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
self.title = NSLocalizedString(#"Liked", #"Liked");
self.tabBarItem = [[UITabBarItem alloc] initWithTabBarSystemItem:UITabBarSystemItemFavorites tag:0];
self.tabBarItem.title = #"Liked";
}
return self;
}
Can I do this? If yes, where I made mistake?
UPD: Changing tab bar item title in the viewDidLoad() works on iOS4 but doesn't work on iOS5. Does exist another approach?
Thanks
You should do that in the viewDidLoad and not in the init.
As per Swift5.1 and Xcode the title and image of the system tab bar items cannot be changed. They are defined as enum in obj c as follow. This is the list:
typedef enum UITabBarSystemItem : NSInteger {
case more, favorites, featured, topRated, recents, contacts, history, bookmarks, search, downloads ,mostRecent, mostViewed
} UITabBarSystemItem;
This has a reason. Some icons are immediately recognisable across the whole iOS ecosystem and mean the same thing to everyone, therefore it is not possible to use them differently.
In this way Apple creates a better user experience.
You cannot use the Apple icon and use a different title. You could use a custom icon and custom title instead or in the Storyboards select an SF Symbol like this:
I'm beginning to learn how to localize iOS applications and hit a wall while trying to localize my UITabBarItems.
Note that these were created in interface builder (using XCode 4).
Is there a way to do this or would I need to create the UITabBarController using just code and manually inserting a localized string for each UITabBarItem?
Cheers
PS:
I do know that I can set the tile of a UITabBarItem by setting the view controller's title like so:
- (void)viewDidLoad
{
[super viewDidLoad];
self.title = NSLocalizedString(#"Test", #"");
}
... but this only woks once you hit the tab bar item. Before that it just shows what you put in interface builder...
It seems to work if you set title in awakeFromNib instead:
- (void)awakeFromNib
{
self.title = NSLocalizedString(#"Test", #"");
}
On Swift 3 and 4:
override func awakeFromNib() {
super.awakeFromNib()
self.title = NSLocalizedString("Test", comment: "")
}
I have a Controller/View for a generic list of items, that can be extended for displaying a custom list.. Listing and navigation works fine.. but I can't change the title of UINavigationController.
In the generic Controller:
- (void)viewDidLoad
{
[super viewDidLoad];
[self.view addSubview: navigationController.view];
}
- (void)setNavigationTitle: (NSString *)title
{
NSLog(#"set title: %#", title); // this works
self.navigationController.title = title; // Nothing works here
}
Then, the extended class does..
- (void)viewDidLoad
{
[super viewDidLoad];
[self setNavigationTitle: #"Custom list"];
}
The navigationBar still have "Item" as title :(
In your UIViewController there is a title property and it is that property that will be displayed by the NavigationController. So when pushing a new UIViewController onto the navigation stack set the title of that UIViewController to whatever is appropriate.
In your case it looks like it would be:
[self setTitle:#"WhateverTitle"];
For those looking for a Swift solution:
class CustomViewController: SuperViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.title = "My Custom Title"
}
}
The title needs to be set on the UIViewController and not on the UINavigationControllerembedding the view controller.
Documentation: UIViewController Class Reference: title Property
use
self.title = #"yourTitle";
Swift
title = "whateverTitle". It's a UIViewController instance property ; invoke anytime.
From the documentation:
Declaration
var title: String? { get set }
Discussion
Set the title to a human-readable string that describes the view. If the view controller has a valid navigation item or tab-bar item, assigning a value to this property updates the title text of those objects.
I know its old thread but thought of sharing this
Set the 'self.title' in 'init' method in UIVIewControler derived class,
This worked for me!!