Detecting current iPhone input language - iphone

Does anybody knows, can I get the current input language and/or keyboard layout in iPhone application? Can I also get a notification when input language was changed?

In iOS 4.2 and later, you can use the UITextInputMode class to determine the primary language currently being used for text input.
[UITextInputMode currentInputMode].primaryLanguage will give you an NSString representing the BCP 47 language code such as “es”, “en-US”, or “fr-CA”.
You can register for the UITextInputCurrentInputModeDidChangeNotification to be alerted when the current input mode changes.
(You might also be interested in the "Getting Your Apps Ready for China and other Hot New Markets" WWDC session, and Internationalization Programming Topics.)

You can ask current first responder (UITextField, UISearchBar, etc.) via UIResponder method textInputMode:
// assume you have instance variable pointing to search bar currently entering
UITextInputMode *inputMode = [self.searchBar textInputMode];
NSString *lang = inputMode.primaryLanguage;

You can add an observer to the default notification center:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(inputModeDidChange:)
name:#"UIKeyboardCurrentInputModeDidChangeNotification"
object:nil];
This method prints the currently selected input language (like "en_US" or "de_DE"):
- (void)inputModeDidChange:(NSNotification*)notification
{
id obj = [notification object];
if ([obj respondsToSelector:#selector(inputModeLastUsedPreference)]) {
id mode = [obj performSelector:#selector(inputModeLastUsedPreference)];
NSLog(#"mode: %#", mode);
}
}
BUT: All the above is not documented and you should not use it in shipping code!

From the Apple Reference Library - "Getting the Current Language and Locale":
NSUserDefaults* defs = [NSUserDefaults standardUserDefaults];
NSArray* languages = [defs objectForKey:#"AppleLanguages"];
NSString* preferredLang = [languages objectAtIndex:0];

In line with the top answers, the following is a generic solution to getting the keyboard language whenever it is changed. Register for the notification UITextInputCurrentInputModeDidChangeNotification:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(inputModeDidChange:) name:UITextInputCurrentInputModeDidChangeNotification object:nil];
Then in inputModeDidChange
-(void)inputModeDidChange:(NSNotification *)notification {
UIView *firstResponder = [UIView currentFirstResponder];
UITextInputMode *currentInputMode = firstResponder.textInputMode;
NSString *keyboardLanguage = [currentInputMode primaryLanguage];
NSLog(#"%#", keyboardLanguage); // e.g. en-US
}
Where currentFirstResponder is from a category on UIView to get the first responder view, as suggested in this SO post:
// UIView+Additions.h
#import <UIKit/UIKit.h>
#interface UIView (Additions)
+ (id)currentFirstResponder;
#end
Implementation
// UIView+Additions.m
#import "UIView+Additions.h"
static __weak id currentFirstResponder;
#implementation UIView (Additions)
+ (id)currentFirstResponder {
currentFirstResponder = nil;
// This will invoke on first responder when target is nil
[[UIApplication sharedApplication] sendAction:#selector(findFirstResponder:)
to:nil
from:nil
forEvent:nil];
return currentFirstResponder;
}
- (void)findFirstResponder:(id)sender {
// First responder will set the static variable to itself
currentFirstResponder = self;
}
#end

The way I would do it is as follows:
Register your ViewController as a listener to UIApplicationDidBecomeActiveNotification
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(applicationDidBecomeActive:) name:UIApplicationDidBecomeActiveNotification object:nil];
In applicationDidBecomeActive handler, check the current language using [NSLocale preferredLanguages] and act upon it accordingly.
This approach gives you what you want and is totally shippable without having to use private API.

Related

Posting score to facebook

I created an app in SpriteKit with xcode where when the game is over, it shows you your score, and I want to add the feature to post your score to facebook. Pretty much all my code is in MyScene.m where it can't access presentViewController. Only my ViewController.m file can access that, so I tried calling a instance method in Viewcontroller from Myscene.m but I can't find a way to do that. The only way I have found calling methods from other files is using +(void) which is a class method I think.
Myscene.m:
if (location.x < 315 && location.x > 261 && location.y < 404 && location.y > 361) {
//if you click the post to facebook button (btw, location is a variable for where you tapped on the screen)
[ViewController facebook];
}
ViewController.m:
+(void)facebook{
if ([SLComposeViewController isAvailableForServiceType:SLServiceTypeFacebook]) {
SLComposeViewController *facebook = [[SLComposeViewController alloc] init];
facebook = [SLComposeViewController composeViewControllerForServiceType:SLServiceTypeFacebook];
[facebook setInitialText:#"initial text"];
}
}
So that worked, and it called the facebook class method correctly, but when I put [self presentViewController:facebook animated:YES] after the setInitialText brackets, it gives me this error: No known class method for selector 'presentViewController:animated:'
By the way it lets me use presentViewController in a instance method but I can't call that method from inside the class method or from my Myscene file. Is there any way to either call an instance method from another file, or access presentViewController from a class method? Thanks
You can either pass a reference for your View Controller to your SKScene or you can use NSNotificationCenter instead. I prefer to use the latter.
First make sure you have added the Social.framework to your project.
Import the social framework into your View Controller #import <Social/Social.h>
Then in your View Controller's viewDidLoad method add this code:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(createPost:)
name:#"CreatePost"
object:nil];
Next add this method to your View Controller:
-(void)createPost:(NSNotification *)notification
{
NSDictionary *postData = [notification userInfo];
NSString *postText = (NSString *)[postData objectForKey:#"postText"];
NSLog(#"%#",postText);
// build your tweet, facebook, etc...
SLComposeViewController *mySLComposerSheet = [SLComposeViewController composeViewControllerForServiceType:SLServiceTypeFacebook];
[self presentViewController:mySLComposerSheet animated:YES completion:nil];
}
At the appropriate location in your SKScene, (won game, lost game, etc...) add this code:
NSString *postText = #"I just beat the last level.";
NSDictionary *userInfo = [NSDictionary dictionaryWithObject:postText forKey:#"postText"];
[[NSNotificationCenter defaultCenter] postNotificationName:#"CreatePost" object:self userInfo:userInfo];
The above code sends a NSNotification, with text, which your View Controller will pick up and execute the specified method.

Passing Delegate through multiple hierarchy

I wrote a custom class, TagsScrollView, which displays tags inside a scroll view.
When the tags are pressed, TagsScrollView relies on its delegate to implement what to do. Almost all the time, this involves:
Changing the tab index to another index
Pushing TagsDetailVC to the current navigation controller.
Right now, this is how my app is structured:
Dotted line indicates a "has" relationship. MainVC has a FeedView, which has a few FeedCellViews, which in turn has a TagsScrollView each.
Solid line indicates a "push" relationship. ImageDetailVc is pushed onto the navController of MainVC.
How can I organize my code such that TagsScrollView's delegate can be pointed at MainVC elegantly?
right now I have defined the following:
TagsScrollView.h
#protocol TagPressedDelegate<NSObject>
#required
- (void)tagPressed:(id)sender forQuery:(NSString *)query;
#end
FeedCellView.m
self.tagsScrollView.tagPressedDelegate = self.tagPressedDelegate
FeedView.m
self.cells[0].tagPressedDelegate = self.tagPressedDelegate
MainViewVC.m
self.feed.tagPressedDelegate = self
....
- (void)tagPressed...
How can I avoid this pattern? What can I do better? Should I have TagsScrollViewDelegate extend ScrollViewDelegate instead?
You can definitely do better, remove the delegation pattern, use blocks.
Add a block based property to your TagsScrollView .h file
#property (copy, nonatomic) void (^tagPressedBlock)(id sender, NSString *query);
in the .m file add the related callbacks
- (void)tagPressed:(id)sender {
if (_tagPressedBlock) {
_tagPressedBlock(sender, self.query); // I'm assuming that the query is your iVar
}
}
assign the property like this
tagsScrollView.tagPressedBlock = ^(id sender, NSString *query) {
// do stuff with those parameters
}
That's for "doing it better"
As for how to pass a tag pressed event to your MainVC class, you should use NSNotificationCenter.
Define the notification name somewhere globally visible, for instance I'd suggest creating a Defines.h file and #including it in your Prefix.pch file.
Anyway, define the notification name:
static NSString *const TagPressedNotification = #"TagPressedNotification";
Next publish that notification when the -tagPressed: is executed and encapsulate the valuable information into the userInfo dictionary:
- (void)tagPressed:(id)sender {
[[NSNotificationCenter defaultCenter] postNotificationName:TagPressedNotification object:nil userInfo:#{#"sender" : sender, #"query" : self.query, #"scrollView" : self.tagScrollView}];
//.. code
}
Next add your MainVC as an observer to that notification:
MainVC.m
- (void)viewDidLoad {
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(tagPressed:)
name:TagPressedNotification
object:nil];
}
And implement -tagPressed: method in your MainVC
- (void)tagPressed:(NSNotification *)notification {
id sender = notification.userInfo[#"sender"];
NSString *query = notification.userInfo[#"query"];
TagScrollView *scrollView = notification.userInfo[#"scrollView"];
if (scrollView == myScrollView) { // the one on your mainVC
// do stuff
}
}
Add don't forget to clean yourself out of NotificationCenter's register:
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
easy
edit
I suppose you should also pass the scroll view, which is the sender, since your mainVC contains that scroll view as well. Edited the code
Another edit
Create an enumeration define in your Defines.h file
enum {
TagSenderTypeFeed = 1,
TagSenderTypeImageDetail
};
typedef NSInteger TagSenderType;
When creating a notification add appropriate enum value to your notification's userInfo dictionary #"senderType" : #(TagSenderTypeFeed)

Move a value between UITabBarController /UIViewControllers

I have a project i'm working on which involves 3 tabs in a UITabBarController (all done in a storyboard).
Each tab is running off a different view controller.
I have a button on tab 1 that performs a calculation and returns a result in a text box. I want it so that when I hit calculate, the result is also returned in a text box in tab 2.
I'm not really sure how to pass data between UIViewControllers so any help is appreciated.
as per vshall says you can do this stuff like bellow:-
yourAppdelegate.h
#interface yourAppdelegate : UIResponder <UIApplicationDelegate,UITabBarControllerDelegate>
{
NSString *myCalResult;
}
#property (nonatomic,retain) NSString *myCalResult;
yourAppdelegate.m
#implementation yourAppdelegate
#synthesize myCalResult,
yourCalclass.h
#import "yourAppdelegate.h"
#interface yourCalclass : UIViewController
{
yourAppdelegate *objAppdelegate;
}
yourCalclass.m
- (void)viewDidLoad
{
objAppdelegate = (yourAppdelegate *) [[UIApplication sharedApplication]delegate];
[super viewDidLoad];
}
-(IBAction)ActionTotal
{
objAppdelegate.myCalResult=result;
}
Now you result stored in objAppdelegate.myCalResult you can use this variable in your another tab with creating object of yourAppdelegat. Hope its helps you
You can define a variable in app delegate and store the result in that variable for class one. And once you switch the class you can fetch that value in your class two by creating an instance of your appDelegate and assign it to your textfield.
As Sanjit has suggested, NSUserDefaults is also a very convenient and clean way to achieve this.
Thanks.
If you don't really need to store the computed value but just notify the other controller in tab2 that the value changed, you can use NSNotificationCenter to post an NSNotification.
When you initialize the controller in tab2 you'll need to add it as an observer of the notification.
Something like that:
in tab1:
NSNumber *value = nil; // the computed value
[[NSNotificationCenter defaultCenter]
postNotificationName:#"com.company.app:ValueChangedNotification"
object:self
userInfo:#{#"value" : value}];
in tab2: register as an observer (in init or viewDidLoad methods)
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:#selector(valueChanged:)
name:#"com.company.app:ValueChangedNotification"
object:nil];
the method that will be called when the notification is posted:
- (void)valueChanged:(NSNotification *)note
{
NSDictionary *userInfo = note.userInfo;
NSNumber *value = userInfo[#"value"];
// do something with value
}
Don't forget to remove the controller from the observers in viewDidUnload or sooner:
[[NSNotificationCenter defaultCenter] removeObserver:self];

iOS Accessing views data from delegate without allocating a new view

I need to change a data (a label) from the app's delegate method ApplicationDidEnterForeground without allocating a new view. The view is called "Reminder", so I imported it into the delegate and I can access its data only if I allocate it (Reminder *anything = [Reminder alloc...etc), but since I want to change the current view loaded I need to have direct access to the view that's already loaded.
How would I do to change the main view's label from the delegate as soon as my application enters foreground?
obs: I know I can do it on -(void)ViewDidLoad or -(void)ViewWillAppear but it won't solve my problem, since it won't change the label if, for example, the user opens the app through a notification box (slide icon when phone is locked). In that case, none of the above methods are called if the app was open in background.
I don't know if I was clear, hope I was. Thank you in advance.
IF you are using storyboards, you can do this to access the current view being seen
- (void)applicationDidEnterBackground:(UIApplication *)application
{
UINavigationController *a=_window.rootViewController;
Reminder *rem = a.topViewController;
rem.label.text=#"test";
}
IF not using story boards
When I create views that I need to access later, I define them as a property, like this
on AppDelegate.h
//#interface SIMCAppDelegate : UIResponder <..........>
//{
//Some variables here
//}
//Properties here
#property (strong, nonatomic) Reminder *reminder;
//Some method declaration here
//eg: -(void) showSomething;
on AppDelegate.m
//#implementation AppDelegate
#synthesize reminder;
so when I alloc/init the view like this
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
//codes before here
self.reminder = [[Reminder alloc] init];
self.reminder.label.text = #"OLD LABEL";
//codes after here
}
I will be able to access it again after allocation on other methods, like this
- (void)applicationWillEnterForeground:(UIApplication *)application
{
self.reminder.label.text = #"NEW LABEL";
}
just send a notification from your ApplicationDidEnterForeground: method and receive it on that class where you want to update the label... Like this..
//Your ApplicationDidEnterForeground:
[[NSNotificationCenter defaultCenter] postNotificationWithName:#"UpdateLabel" withObject:nill];
and add observer in it viewDidLoad: of that controller where you want to update label
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(updateLabel:)
name:#"UpdateLabel"
object:nil];
made your method in same class ...
- (void)updateLabel:(NSNotification *)notification{
update label
}
Might be you can try following code -
NSMutableArray *activeControllerArray = [self.navigationController.viewControllers mutableCopy];
for (int i = 0; i< [activeControllerArray count]; i++) {
if ([[activeControllerArray objectAtIndex:i] isKindOfClass:[Reminder Class]) {
Reminder *object = [activeControllerArray objectAtIndex:i];
//Perform all the task here which you want.
break; //Once found break the loop to do further processing.
}
}

NSnotifications for multiple downloads

I have a parser class and some view controller classes. In the parser class i am sending a request and receiving an asynchronous response. I want multiple downloads, say one per viewcontroller. So i register an observer in each of these classes :
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(dataDownloadComplete:) name:OP_DataComplete object:nil];
and then post a notification in :
-(void)connectionDidFinishLoading:(NSURLConnection *)connection method of the parser class.
[[NSNotificationCenter defaultCenter] postNotificationName:OP_DataComplete object:nil];
but then on running the code the first viewcontroller works fine but for the second one onwards after download and parser class posting notification infinitely the code enters the first class's dataDownloadComplete: method although i have specified a different method name in the selector each time. I don't understand what the error might be. Please help. Thanks in advance.
Both view controllers are listening for the notification so both methods should be being called, one after another.
There's a few ways to solve this. The easiest would be for the notification to contain some sort of identifier that the view controller can look at to see if it should ignore it or not. NSNotifications have a userInfo property for this.
NSDictionary *info = [NSDictionary dictionaryWithObjectsAndKeys:#"viewController1", #"id", nil];
[[NSNotificationCenter defaultCenter] postNotificationName:OP_DataComplete object:self userInfo:info];
and when you recieve the notification, check to see who it's for :
- (void)dataDownloadComplete:(NSNotification *)notification {
NSString *id = [[notification userInfo] objectForKey:#"id"];
if (NO == [id isEqualToString:#"viewController1"]) return;
// Deal with the notification here
...
}
There's a few other ways to deal with it but without knowing more about your code, I can't explain them well - basically you can specify the objects that you want to listen to notifications from (see how I have object:self but you sent object:nil) but sometimes your architecture won't allow that to happen.
it's better to create a protocol:
#protocol MONStuffParserRecipientProtocol
#required
- (void)parsedStuffIsReady:(NSDictionary *)parsedStuff;
#end
and to declare the view controller:
#class MONStuffDownloadAndParserOperation;
#interface MONViewController : UIViewController < MONStuffParserRecipientProtocol >
{
MONStuffDownloadAndParserOperation * operation; // add property
}
...
- (void)parsedStuffIsReady:(NSDictionary *)parsedStuff; // implement protocol
#end
and add some backend: to the view controller
- (void)displayDataAtURL:(NSURL *)url
{
MONStuffDownloadAndParserOperation * op = self.operation;
if (op) {
[op cancel];
}
[self putUpLoadingIndicator];
MONStuffDownloadAndParserOperation * next = [[MONStuffDownloadAndParserOperation alloc] initWithURL:url recipient:viewController];
self.operation = next;
[next release], next = 0;
}
and have the operation hold on to the view controller:
#interface MONStuffDownloadAndParserOperation : NSOperation
{
NSObject<MONStuffParserRecipientProtocol>* recipient; // << retained
}
- (id)initWithURL:(NSURL *)url Recipient:(NSObject<MONStuffParserRecipientProtocol>*)recipient;
#end
and have the operation message the recipient when the data is downloaded and parsed:
// you may want to message from the main thread, if there are ui updates
[recipient parsedStuffIsReady:parsedStuff];
there are a few more things to implement -- it's just a form. it's safer and involves direct messaging, ref counting, cancellation, and such.