Is there a way to change the application language during runtime?
So, after the change NSLocalizedString immediately returns the string for the new language.
What I'm doing now is changing the language using the code below:
- (void)onChangeLanguage: (id)sender
{
NSArray *lang = [NSArray arrayWithObjects:((InfoWhatever *)sender).language, nil];
[[NSUserDefaults standardUserDefaults] setObject:lang forKey:#"AppleLanguages"];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSArray *languages = [defaults objectForKey:#"AppleLanguages"];
NSString *currentLanguage = [languages objectAtIndex:0];
NSLog(#"Current language: %#", currentLanguage);
}
The language will change but only after restarting the app.
NSUserDefaults* userDefaults = [NSUserDefaults standardUserDefaults];
NSMutableArray* languages = [userDefaults objectForKey:#"AppleLanguages"];
[languages insertObject:#"de" atIndex:0]; // ISO639-1
[[NSUserDefaults standardUserDefaults] synchronize];
The trick to use specific language by selecting it from the app is to force the NSLocalizedString to use specific bundle depending on the selected language ,
here is the post i have written for this http://learning-ios.blogspot.com/2011/04/advance-localization-in-ios-apps.html
and here is the code of one sample app https://github.com/object2dot0/Advance-Localization-in-ios-apps
You Can do it . Here is the way
http://aggressive-mediocrity.blogspot.com/2010/03/custom-localization-system-for-your.html
In Brief Download and add 2 files to the project
http://dl.dropbox.com/u/2917666/LocalizationSystem/LocalizationSystem.h
http://dl.dropbox.com/u/2917666/LocalizationSystem/LocalizationSystem.m
2
#import "LocalizationSystem.h"
3
- (IBAction)btnEnglishClicked:(id)sender {
LocalizationSetLanguage(#"en");
}
4 After you set the language as above
AMLocalizedString(#"Key", nil)
Thats it.
I doubt you can do this, even the Settings app cannot do it.
(When you change the language in the Settings app, the screen goes black, and displays "setting language..." and a progress wheel. After a long wait, you are back in Springboard. It almost looks like the phone reboots.)
I came up with a solution that allows you to use NSLocalizedString. I create a category of NSBundle call NSBundle+RunTimeLanguage. The interface is like this.
// NSBundle+RunTimeLanguage.h
#import <Foundation/Foundation.h>
#interface NSBundle (RunTimeLanguage)
#define NSLocalizedString(key, comment) [[NSBundle mainBundle] runTimeLocalizedStringForKey:(key) value:#"" table:nil]
- (NSString *)runTimeLocalizedStringForKey:(NSString *)key value:(NSString *)value table:(NSString *)tableName;
#end
The implementation is like this.
// NSBundle+RunTimeLanguage.m
#import "NSBundle+RunTimeLanguage.h"
#import "AppDelegate.h"
#implementation NSBundle (RunTimeLanguage)
- (NSString *)runTimeLocalizedStringForKey:(NSString *)key value:(NSString *)value table:(NSString *)tableName
{
AppDelegate *appDelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
NSString *path= [[NSBundle mainBundle] pathForResource:[appDelegate languageCode] ofType:#"lproj"];
NSBundle *languageBundle = [NSBundle bundleWithPath:path];
NSString *localizedString=[languageBundle localizedStringForKey:key value:key table:nil];
return localizedString;
}
#end
Than just add import NSBundle+RunTimeLanguage.h into the files that use NSLocalizedString.
As you can see I store my languageCode in a property of AppDelegate. This could be stored anywhere you'd like.
This only thing I don't like about it is a Warning that NSLocalizedString marco redefined. Perhaps someone could help me fix this part.
Simply add these lines:
#define currentLanguageBundle [NSBundle bundleWithPath:[[NSBundle mainBundle] pathForResource:[[NSLocale preferredLanguages] objectAtIndex:0] ofType:#"lproj"]]
1. NSUserDefaults * defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:#[#"en"] forKey:#"AppleLanguages"]; [defaults
synchronize];
2. _label.text = NSLocalizedStringFromTableInBundle(#"Key", nil, currentLanguageBundle, #"");
try this:
object_setClass([NSBundle mainBundle],[MyBundle class]);
https://github.com/maximbilan/ios_language_manager/blob/master/README.md
Related
i have two .m file .i used nsstring object on another .m file its always null.
//postputgetFunction.h
#property(retain,nonatomic) IBOutlet NSMutableString *postRegisterResponseUserId;
//postputgetFunction.m
#synthesize postRegisterResponseUserId;
-(void) connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
if ([flag isEqualToString:#"post"])
{
NSLog(#"Post received data here.....");
NSDictionary *dict=[NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
postRegisterResponseName=[dict valueForKey:#"Name"];
postRegisterResponseSuccess=[dict valueForKey:#"Success"];
postRegisterResponseUserId=[dict valueForKey:#"UserId"];
NSLog(#"ReceiveData :Name : %# \n Success : %# \n UserId : %#",postRegisterResponseName,postRegisterResponseSuccess,postRegisterResponseUserId);
//Above statement display the value properly..........
flag=Nil;
}
}
but i am using in another .m file ... In this .m file its shown null value .. like this,
//Verification.h
#import "PostPutGetFunction.h"
#property (retain, nonatomic) PostPutGetFunction *postputgetFunction;
//verification.m
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
postputgetFunction=[[PostPutGetFunction alloc]init];
}
- (IBAction)verificationBtnClick:(id)sender
{
NSLog(#"%#",postputgetFunction.postRegisterResponseUserId);
//here its always shown NULL ... i didnt get the value here ...
}
In other .m file's viewDidLoad method you allocating and initializing the PostPutGetFunction using
postputgetFunction=[[PostPutGetFunction alloc]init];
That's why the variable defined in PostPutGetFunction class NSMutableString *postRegisterResponseUserId initialized to Null. You can use the Delegates for passing the data between the two controllers. Or alternatively you can store the userID in NSUserDefault class like below
**First Part**
NSDictionary *dict=[NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
NSString *name =[dict valueForKey:#"Name"];
NSString *success=[dict valueForKey:#"Success"];
NSString *userid =[dict valueForKey:#"UserId"];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:name forKey:#"NAME"];
[defaults setObject:success forKey:#"SUCCESS"];
[defaults setObject:userid forKey:#"USERID"];
[defaults synchronize];
And to retrieve the values in another class.m use below code
**Second Part**
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *name = [defaults objectForKey:#"NAME"];
NSString *success = [defaults objectForKey:#"SUCCESS"];
NSString *userid = [defaults objectForKey:#"USERID"];
And also you do not use IBOutlet for NSMutableString type rather it's used for UI Control types like Below
You are Declaring a new object of PostPutGetFunction, and for sure the value postRegisterResponseUserId will be null
if you want to accomplish that you have to use Delegation, take a look at this answer
I was using xCode 3.2 and then moved to xCode 4.2 and getting some values from Settings.bundle ... it was working fine.
Mean while I need to edit some values in Settings.bundle but The Root.plist file was not showing so I follow the below procedure but did not make any change in file.
1) Click on the Settings.Bundle file, go over to the utilities window,
and look in the File Inspector.
2) Using the drop-down, change the file type to 'Application Bundle'
After that I could see Root.plist but now could not get its values in application. Actually getting Null instead of value.
Below is code and image of Settings.bundle
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
host = [defaults stringForKey:#"meter_preference"];
if(host == nil)
{
host = #"10.20.20.1";
DDLogError(#"Meter host is nil from NSUserDefaults, defaulting to %#", host);
}
I got the solution, just call the below code in applicationDidFinishLaunchingWithOptions to initialize User defaults. and it works
Replace somePropertyYouExpect in first line with property you stored in User Defaults.
if (![[NSUserDefaults standardUserDefaults] objectForKey:#"somePropertyYouExpect"]) {
NSString *mainBundlePath = [[NSBundle mainBundle] bundlePath];
NSString *settingsPropertyListPath = [mainBundlePath
stringByAppendingPathComponent:#"Settings.bundle/Root.plist"];
NSDictionary *settingsPropertyList = [NSDictionary
dictionaryWithContentsOfFile:settingsPropertyListPath];
NSMutableArray *preferenceArray = [settingsPropertyList objectForKey:#"PreferenceSpecifiers"];
NSMutableDictionary *registerableDictionary = [NSMutableDictionary dictionary];
for (int i = 0; i < [preferenceArray count]; i++) {
NSString *key = [[preferenceArray objectAtIndex:i] objectForKey:#"Key"];
if (key) {
id value = [[preferenceArray objectAtIndex:i] objectForKey:#"DefaultValue"];
[registerableDictionary setObject:value forKey:key];
}
}
[[NSUserDefaults standardUserDefaults] registerDefaults:registerableDictionary];
[[NSUserDefaults standardUserDefaults] synchronize];
}
From your code , try this thinks..
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
host = [defaults stringForKey:#"meter_preference"];
if(!host == nil)
{
host = #"10.20.20.1";
DDLogError(#"Meter host is nil from NSUserDefaults, defaulting to %#", host);
}
OR
Review this link may be helped you...
iPhone - reading Setting.bundle returns wrong values
NSUserDefaults Settings Bundle Plist
Hi I have my ALAsset URL save in NSMutableArray,
"ALAsset - Type:Photo, URLs:assets-library://asset/asset.JPG?id=119A0D2D-C267-4B69-A200-59890B2B0FE5&ext=JPG",
"ALAsset - Type:Photo, URLs:assets-library://asset/asset.JPG?id=92A7A24F-D54B-496E-B250-542BBE37BE8C&ext=JPG",
"ALAsset - Type:Photo, URLs:assets-library://asset/asset.JPG?id=77AC7205-68E6-4062-B80C-FC288DF96F24&ext=JPG
I wasnt able to save NSMutableArray in NSUserDefaults due to it having an error Note that dictionaries and arrays in property lists must also contain only property values.
Im thinking of using this :
- (void)encodeWithCoder:(NSCoder *)encoder {
//Encode properties, other class variables, etc
[encoder encodeObject:self.selectedPhotos forKey:#"selectedPhotos"];
}
- (id)initWithCoder:(NSCoder *)decoder {
if((self = [super init])) {
//decode properties, other class vars
self.selectedPhotos = [decoder decodeObjectForKey:#"selectedPhotos"];
}
return self;
}
then save and retrieve it with this code:
- (void)saveCustomObject:(MyCustomObject *)obj {
NSData *myEncodedObject = [NSKeyedArchiver archivedDataWithRootObject:obj];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:myEncodedObject forKey:#"myEncodedObjectKey"];
}
- (MyCustomObject *)loadCustomObjectWithKey:(NSString *)key {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSData *myEncodedObject = [defaults objectForKey:key];
MyCustomObject *obj = (MyCustomObject *)[NSKeyedUnarchiver unarchiveObjectWithData: myEncodedObject];
return obj;
}
But I somehow dont quite get it, still crashes in my code. Dont know how. And I wasnt able to save it in NSUserDefaults. Hope someone help. Really been having problem with this a while. Hope someone guide me on the right path of saving and retrieving it the right way from NSUserDefaults. Then back to a NSMutableArray.
The NSUserDefaults only takes a restricted set of classes as objects. See the documentation. You must take care only to store values of these types (NSData, NSString, NSNumber, NSDate, NSArray, or NSDictionary, and of course it applies recursively) in the dictionary.
To store the URLs in the NSUserDefaults, store them as strings, then read them back as URLs. If you need to have the dictionary in the current format, you may have to transform it before saving it.
- (void) saveMyUrls
{
NSMutableArray* urls = [NSMutableArray arrayWithCapacity:self.myUrls.count];
for(NSURL* url in self.myUrls) {
[urls addObject:[url absoluteString]];
}
[[NSUserDefaults standardUserDefaults] setObject:urls forKey:#"myUrls"];
}
- (void) loadUrls
{
NSArray* urls = [[NSUserDefaults standardUserDefaults] objectForKey:#"myUrls"];
self.myUrls = [NSMutableArray arrayWithCapacity:urls.count];
for(NSString* urlString in urls) {
[self.myUrls addObject:[NSURL URLWithString:urlString]];
}
[[NSUserDefaults standardUserDefaults] setObject:urls forKey:#"myUrls"];
}
If you need to save more information than just the URL, let's say a user-specified label, you could save the object as a NSDictionary instead, e.g.
- (void) saveMyUrlsWithLabels
{
NSMutableArray* objs = [NSMutableArray arrayWithCapacity:self.myObjects.count];
for(MyObject* obj in self.myObjects) {
[objs addObject:[NSDictionary dictionaryWithKeys:#"url", #"label"
forObjects:obj.url.absoluteString, obj.userSpecifiedLabel];
}
[[NSUserDefaults standardUserDefaults] setObject:objs forKey:#"myObjects"];
}
Maybe you should do it like this:
- (MyCustomObject *)loadCustomObjectWithKey:(NSString *)key {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults synchronize]; // note this
NSData *myEncodedObject = [defaults objectForKey:key];
MyCustomObject *obj = nil;
// it would be even better
// to wrap this into #try-#catch block
if(myEncodedObject)
{
obj = (MyCustomObject *)[NSKeyedUnarchiver unarchiveObjectWithData: myEncodedObject];
}
return obj;
}
Also note that if you want to use NSKeyedArchiver and NSKeyedUNarchiver your MyCustomObject class has to conform to NSCoding protocol. Check NSCoding protocol reference and Archives and Serializations Programming Guide.
This is another way to do it and yes you can use NSUserDefaults. Basically you get asset URL, save it and then convert it back to an asset / image
//SET IT
ALAsset *asset3 = [self.assets objectAtIndex:[indexPath row]];
NSMutableString *testStr = [NSMutableString stringWithFormat:#"%#", asset3.defaultRepresentation.url];
//NSLog(#"testStr: %# ...", testStr);
[[NSUserDefaults standardUserDefaults] setObject:testStr forKey:#"userPhotoAsset"];
[[NSUserDefaults standardUserDefaults] synchronize];
//GET IT
NSString *assetUrlStr = [[NSUserDefaults standardUserDefaults] objectForKey:#"userPhotoAsset"];
NSURL* aURL = [NSURL URLWithString:assetUrlStr];
NSLog(#"aURL: %# ...", aURL);
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
[library assetForURL:aURL resultBlock:^(ALAsset *asset)
{
UIImage *copyOfOriginalImage = [UIImage imageWithCGImage:[[asset defaultRepresentation] fullScreenImage] scale:1.0 orientation:UIImageOrientationUp];
imgVwPortrait.image = copyOfOriginalImage;
}
failureBlock:^(NSError *error)
{
// error handling
NSLog(#"failure-----");
}];
I will be sending out an update to my app with a new data structure, therefore if a user is updating my app I need to update their current data. So I was wondering how can I programatically tell if the user updated my app or installed a new copy (if a new copy is installed I don't need to update anything) ?
Checking the data structure is a solid solution. I began to worry in my own apps about folks who don't upgrade for several versions. I felt this would lead to a myriad of structure checks. The code I show below determines and stores the version and previous version in the NSUserDefaults. You could code for those varying version difference scenarios if needed.
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
BOOL versionUpgraded;
NSString *version = [[[NSBundle mainBundle] infoDictionary] objectForKey:#"CFBundleVersion"];
NSString *preVersion = [prefs stringForKey:#"appVersion"];
if ([prefs stringForKey:#"appVersion"] != nil) {
//see if version is the same as prior
//if not it is an Upgraded
versionUpgraded = !([preVersion isEqualToString: version]);
} else {
//nil means new install
//This needs to be YES for the case that
//"appVersion" is not set anywhere else.
versionUpgraded = YES;
}
if (versionUpgraded) {
[prefs setObject:version forKey:#"appVersion"];
[prefs setObject:preVersion forKey:#"prevAppVersion"];
[prefs synchronize];
}
That depends on the kind of data structure you're using.
In general, I would advise you against relying on checking your application version: a user using 2.0 might have just upgraded or it might be a new user.
I'd rather check if there's a data structure already, and act accordingly. Assuming that you're using a Sqlite-backed Core Data storage, you can either check whether the .sqlite file exists, or check if there are objects in your storage.
Just save the bundle version somewhere and check if it differs from
[[NSBundle mainBundle] objectForInfoDictionaryKey:#"CFBundleVersion"]]
on each app startup.
I have created a category for this. Just implement the two new delegate calls found in the header. It relies quite heavily on the obj-c runtime libraries, so make sure you are confident with them before using this.
.h
#import <UIKit/UIKit.h>
#protocol UIApplicationDelegate <UIApplicationDelegate>
#optional
- (void) application:(UIApplication *)application willUpdateToVersion: (NSString*) newVersion fromVersion: (NSString*) previousVersion;
- (void) application:(UIApplication *)application didUpdateToVersion: (NSString*) newVersion fromVersion: (NSString*) previousVersion;
#end
#interface UIApplication (Versioning)
#end
.m
#import "UIApplication+Versioning.h"
#import <objc/message.h>
#import <objc/runtime.h>
static NSString* UIApplicationVersionFileName = #"app.ver";
#implementation UIApplication (Versioning)
+ (void) load {
Method original, swizzled;
original = class_getInstanceMethod(self, #selector(setDelegate:));
swizzled = class_getInstanceMethod(self, #selector(swizzled_setDelegate:));
method_exchangeImplementations(original, swizzled);
}
- (void) swizzled_setDelegate: (id<UIApplicationDelegate>) delegate {
IMP implementation = class_getMethodImplementation([self class], #selector(swizzled_application:didFinishLaunchingWithOptions:));
class_addMethod([delegate class], #selector(swizzled_application:didFinishLaunchingWithOptions:), implementation, "B#:##");
Method original, swizzled;
original = class_getInstanceMethod([delegate class], #selector(application:didFinishLaunchingWithOptions:));
swizzled = class_getInstanceMethod([delegate class], #selector(swizzled_application:didFinishLaunchingWithOptions:));
method_exchangeImplementations(original, swizzled);
[self swizzled_setDelegate: delegate];
}
- (BOOL)swizzled_application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
//Check for a version change
NSError* error;
NSArray* directories = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString* versionFilePath = [[directories objectAtIndex: 0] stringByAppendingPathComponent: UIApplicationVersionFileName];
NSString* oldVersion = [NSString stringWithContentsOfFile: versionFilePath
encoding: NSUTF8StringEncoding
error: &error];
NSString* currentVersion = [[[NSBundle mainBundle] infoDictionary] objectForKey: #"CFBundleVersion"];
switch (error.code) {
case NSFileReadNoSuchFileError:
{
//Delegate methods will not be called first time
oldVersion = [currentVersion copy];
[currentVersion writeToFile: versionFilePath
atomically: YES
encoding: NSUTF8StringEncoding
error: &error];
break;
}
default:
{
NSLog(#"Warning: An error occured will loading the application version file -> Recreating file");
[[NSFileManager defaultManager] removeItemAtPath: versionFilePath
error: nil];
oldVersion = [currentVersion copy];
[currentVersion writeToFile: versionFilePath
atomically: YES
encoding: NSUTF8StringEncoding
error: &error];
break;
}
}
if( ![oldVersion isEqualToString: currentVersion] ) {
if ([[application delegate] respondsToSelector: #selector(application:willUpdateToVersion:fromVersion:)]) {
objc_msgSend([application delegate], #selector(application:willUpdateToVersion:fromVersion:), currentVersion, oldVersion);
}
[currentVersion writeToFile: versionFilePath
atomically: YES
encoding: NSUTF8StringEncoding
error: &error];
if ([[application delegate] respondsToSelector: #selector(application:didUpdateToVersion:fromVersion:)]) {
objc_msgSend([application delegate], #selector(application:willUpdateToVersion:fromVersion:), currentVersion, oldVersion);
}
}
SEL realSelector = #selector(swizzled_application:didFinishLaunchingWithOptions:);
return (BOOL) objc_msgSend([application delegate], realSelector, application, launchOptions);
}
#end
I have a flipView application. On the FlipView I keep settings for the MainView and would like to use NSUserDefaults to store 2 integers for the MainView. I will use those integers in the main application. Is there some basic code that can implement what I'm trying to do?
I'd like to store the information on the FlipView (if changed) and have a default setting when the program is first run in the MainView.
Also, will I need to release the NSUserDefaults object once I'm finished using it?
check out below code, I had wrapped and make easy for you,
you can use below codes by:
import "Settings.h" in any code where you want to use, (maybe AppDelegate.m) in your case. then
to set value, use
[Settings setSetting:#"AUTOLOGIN" value:#"1"];
[Settings setSetting:#"OTHERPROPERTY" value:#"100"];
and remember that, store pointer datatype only,
to get setting value:
[Settings getSetting:#"AUTOLOGIN"];
here below there are two files, Setting.h and Setting.m , make those
header file is :
#import <Foundation/Foundation.h>
#interface Settings : NSObject {
}
+(id) getSetting:(NSString *)key;
+(void) setSetting:(NSString *)key value:(id)v;
#end
- and implementation file is
#import "Settings.h"
#implementation GCCSettings
static NSMutableDictionary *settings = nil;
+(id) getSetting:(NSString *)key{
if(settings == nil){
settings = [[NSMutableDictionary alloc] initWithDictionary:
[[NSUserDefaults standardUserDefaults] dictionaryForKey:#"settings"]
];
}
return [settings objectForKey:key];
}
+(void) setSetting:(NSString *)key value:(id)v{
[settings setValue:v forKey:key];
[[NSUserDefaults standardUserDefaults] setObject:settings forKey:#"settings"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
#end
and finally don't release any objects, which you had not allocated using(new, init, retain etc).
You can store the integer with
[[NSUserDefaults standardUserDefaults] setInteger:1 forKey:#"theKey"] and retrive it with [[NSUserDefaults standardUserDefautls] integerForKey:#"theKey"]. That's really all you need to handle an integer with user defaults.
You don't release the NSUserDefaults object. It's a singleton, so only one will be created for the life of your app. You also don't own it which means you wouldn't be releasing it.