Connecting multiple keyboards in iOS 14 Objective-C - objective-c

iOS 14 introduced GCKeyboard. I was able to successfully connect a BlueTooth NumberPad in my app but I need to be able to connect two of them in tandem. Could anyone please point me to sample code?
In the GCController class there is a "controllers" method which returns an array of all connected controllers, but there isn't anything similar for GCKeyboard.
Here is my code:
- (void)viewDidLoad {
[super viewDidLoad];
[[UIApplication sharedApplication]setIdleTimerDisabled:YES];
// notifications for keyboard (dis)connect
[[NSNotificationCenter defaultCenter]addObserver:self selector:#selector(keyboardWasConnected:) name:GCKeyboardDidConnectNotification object:nil];
[[NSNotificationCenter defaultCenter]addObserver:self selector:#selector(keyboardWasDisconnected:) name:GCKeyboardDidDisconnectNotification object:nil];
}
GCKeyboard *leftKeyboard;
GCKeyboard *rightKeyboard;
- (void)keyboardWasConnected:(NSNotification *)notification {
if (!self.leftKeyboard) {
leftKeyboard = (GCKeyboard *)notification.object;
NSLog(#"Left Keyboard connected: %#\n", leftKeyboard.description);
NSString *keyboardStatus = [NSString stringWithFormat:#"Left Keyboard CONNECTED:\n%#", leftKeyboard.description];
self.statusLabel.text = keyboardStatus;
self.leftKeyboard = leftKeyboard;
[self reactToKeyboardInput];
}
else if (!self.rightKeyboard) {
NSLog(#"Right Keyboard connected: %#\n", rightKeyboard.description);
rightKeyboard = (GCKeyboard *)notification.object;
NSString *keyboardStatus = [NSString stringWithFormat:#"Right Keyboard CONNECTED:\n%#", rightKeyboard.description];
self.statusLabel.text = keyboardStatus;
self.rightKeyboard = rightKeyboard;
[self reactToKeyboardInput];
}
}
- (void)keyboardWasDisconnected:(NSNotification *)notification {
if (self.leftKeyboard) {
GCKeyboard *keyboard = (GCKeyboard *)notification.object;
NSString *status = [NSString stringWithFormat:#"Left Keyboard DISCONNECTED:\n%#", keyboard.description];
self.statusLabel.text = status;
self.leftKeyboard = nil;
}
else if (self.rightKeyboard) {
GCKeyboard *keyboard = (GCKeyboard *)notification.object;
NSString *status = [NSString stringWithFormat:#"Right Keyboard DISCONNECTED:\n%#", keyboard.description];
self.statusLabel.text = status;
self.rightKeyboard = nil;
}
}
When a keyboard or number pad is connected I get the message:
2020-09-18 13:09:15.845943-0700 Controller[1653:351628] Left Keyboard connected: GCController 0x280bb8dd0 ('Keyboard' - 0x27c3dc28cec4d818)

"Bring keyboard and mouse gaming to iPad" WWDC video transcript states:
In the case of keyboards, you can't differentiate multiple keyboards, and all keyboard events from multiple keyboards are instead coalesced for you. So while you might use notifications to notice when a keyboard disconnects and perhaps pause the game to prompt for different input, in general, you'll find that just using GCKeyboard-Device.coalesced to check on the key state when non-nil is the right path.
So while you may have multiple keyboards connected, it sounds like they all get coalesced into a single GCKeyboard instance and you can't differentiate between them. That is why there's no +[GCKeyboard keyboards] method. Instead there is +[GCKeyboard coalescedKeyboard]

Related

iCloud key-value sync iOS8 Xcode 6

Trying to fix some issue with iCloud. Here are two versions of my code.
- (void)viewDidLoad{
[super viewDidLoad];
[self checkICloudData];
}
version 1
- (void)checkICloudData
{
NSFileManager * fileManager=[NSFileManager defaultManager];
NSURL *iCloudURL=[fileManager URLForUbiquityContainerIdentifier:nil];
NSLog(#"iCloud URL is %#",[iCloudURL absoluteString]);
if (iCloudURL){
NSUbiquitousKeyValueStore * store=[NSUbiquitousKeyValueStore defaultStore];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(updateICloudData:)
name:NSUbiquitousKeyValueStoreDidChangeExternallyNotification
object:store];
[store synchronize];
}else{
NSLog(#"iCloud is not supported or enabled");
[self loadDataFromBundle];
}
}
iCloudURL always returns nil. Other methods do not call.
version 2
- (void)checkICloudData
{
NSUbiquitousKeyValueStore * store=[NSUbiquitousKeyValueStore defaultStore];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(updateICloudData:)
name:NSUbiquitousKeyValueStoreDidChangeExternallyNotification
object:store];
[store synchronize];
}
- (void)updateICloudData:(NSNotification*)notification
{
NSDictionary *userInfo = [notification userInfo];
NSNumber *changeReason = [userInfo objectForKey:NSUbiquitousKeyValueStoreChangeReasonKey];
NSInteger reason = -1;
if (!changeReason) {
return;
} else {
reason = [changeReason integerValue];
}
if ((reason == NSUbiquitousKeyValueStoreServerChange) || (reason == NSUbiquitousKeyValueStoreInitialSyncChange)) {
NSArray *changedKeys = [userInfo objectForKey:NSUbiquitousKeyValueStoreChangedKeysKey];
for (NSString *key in changedKeys) {
if ([key isEqualToString:casesKey]) {
[self iCloudNotification];
}else {
[self loadDataFromBundle];
}
}
}
}
With that version iCloud sync works fine with the iPad, but doesn't work with the iPhone. In the iPhone's iCloud Drive settings i see my app same as in the iPad
My iCloud settings:
Member center - iCloud Enabled. Compatibility - Include CloudKit support
Target Capabilities - Services checked only Key-Value storage
Created by default entitlements dictionary contains key com.apple.developer.icloud-container-identifiers with an empty array and key com.apple.developer.ubiquity-kvstore-identifier with a correct string value of my appID
So, why iCloudURL always returns nil? And why the second version works correct with the iPad but my iPhone does not see NSUbiquitousKeyValueStoreDidChangeExternallyNotification?
Well, the problem was in my iPhone after upgrade iOS.
When I've delete iCloud profile from iPhone's settings and add it again, the iPhone started to sync with iCloud.
Right now the second version of the code works with both devices.
Hope it helps somebody to solve that kind of problem.

How to interrupt AVPlayer for incoming call manually

I've used this method How do I get my AVPlayer to play while app is in background? to have an AVPlayer play in the background so the user can listen while browsing safari etc. Only problem is now AVPlayer continues to play when there is an incoming call and stays playing during the call. Is there a way to catch the incoming call and end call events so AVPlayer can be stopped and started manually?
Through coreTelephony framework we have to find or detect the incoming call. From there you have to initiate your local notification to stop your AVPlayer.
after importing do like this
CTCallCenter * _callCenter = [[CTCallCenter alloc] init];
_callCenter.callEventHandler = ^(CTCall* call)
{
if ([call.callState isEqualToString:CTCallStateDisconnected])
{
NSLog(#"Call has been disconnected");
}
else if([call.callState isEqualToString:CTCallStateDialing])
{
NSLog(#"Call start");
}
else if ([call.callState isEqualToString:CTCallStateConnected])
{
NSLog(#"Call has just been connected");
}
else if([call.callState isEqualToString:CTCallStateIncoming])
{
NSLog(#"Call is incoming");
// You have to initiate/post your local notification through NSNotification center like this
[[NSNotificationCenter defaultCenter] postNotificationName:#"stopAVPlayer" object:nil];
} else
{
NSLog(#"None of the conditions");
}
};
Refer this :https://developer.apple.com/library/ios/navigation/#section=Frameworks&topic=CoreTelephony
You could use the CallKit for getting phone call event now. (iOS 10.0+)
This is good for me.
#import <CallKit/CallKit.h>
#interface ViewController ()<CXCallObserverDelegate> {
CXCallObserver *_center;
}
-(void)viewDidLoad
_center = [[CXCallObserver alloc] init];
dispatch_queue_t queue = dispatch_queue_create("THIS_IS_A_CALL",NULL);
[_center setDelegate:self queue:queue];
Delegate
- (void)callObserver:(CXCallObserver *)callObserver callChanged:(CXCall *)call {
NSString *tag = #"callObserver";
NSString *content = #"";
if (call.isOutgoing) {
content = #"call.isOutgoing";
}
if (call.hasEnded) {
content = #"call.hasEnded";
}
if (call.hasConnected) {
content = #"call.hasConnected";
}
if (call.isOnHold) {
content = #"call.isOnHold";
}
NSLog(#"%# - %#", tag, content);
}
More detail: https://developer.apple.com/documentation/callkit

How to determine if AVCaptureDevice has focus

I have successfully turned on the setSubjectAreaChangeMonitoringEnabled:
self.videoDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
[self.videoDevice lockForConfiguration:nil]; //you must lock before setting torch mode
[self.videoDevice setSubjectAreaChangeMonitoringEnabled:YES];
[self.videoDevice unlockForConfiguration];
in start method I have included the following notification:
[[NSNotificationCenter defaultCenter] addObserver: self
selector: #selector(areWeFocused:)
name: AVCaptureDeviceSubjectAreaDidChangeNotification
object: nil];
Then I have the areWeFocused method (which I know is wrong but I need help with it):
- (void)areWeFocused:(NSNotification *) notification {
BOOL adjusting = [self.videoDevice isAdjustingFocus];
if (!adjusting) {
//NSLog(#"I have focus");
} else {
NSLog(#"NOT");
}
}
If uncommented my NSLog always prints I have focus, but commenting that line I never get NOT. How can I detect if we are in focus? BTW: Focus mode is AutoFocus and ContinuousAutoFocus
Thank you for your help.

Stop local notification

I have a problem below:
When minimize app to background by press home button, create a local notification for pop-up every 5 minutes.
Remove app from background.
-->My expected then pup-up just only show when app exist and it's discarded when remove app from background.
My issue that local notification still active and it still showing pop-up every 5 minutes after remove it from background.
How can i stop it?
Please help me!
Thanks in advanced.
Put this in application delegate. It will remove all local notifications when the application enters background.
- (void)applicationDidEnterBackground:(UIApplication *)application
{
[[UIApplication sharedApplication] cancelAllLocalNotifications];
}
If you don't want to cancel all notifications... I've set up a unique identifier stored in the notification's userInfo dictionary. When I want to delete I fast enumerate through all notifications and pick out the correct one for deletion.
My stumbling blocks here were remembering to store the UUID I'd created for the notification and also remembering to use isEqualToString in the fast enumeration. I guess I could also have used a specific name string instead of a unique identifier. If anyone can let me know a better method than fast enumerating please let me know.
#interface myApp () {
NSString *storedUUIDString;
}
- (void)viewDidLoad {
// create a unique identifier - place this anywhere but don't forget it! You need it to identify the local notification later
storedUUIDString = [self createUUID]; // see method lower down
}
// Create the local notification
- (void)createLocalNotification {
UILocalNotification *localNotif = [[UILocalNotification alloc] init];
if (localNotif == nil) return;
localNotif.fireDate = [self.timerPrototype fireDate];
localNotif.timeZone = [NSTimeZone defaultTimeZone];
localNotif.alertBody = #"Hello world";
localNotif.alertAction = #"View"; // Set the action button
localNotif.soundName = UILocalNotificationDefaultSoundName;
NSDictionary *infoDict = [NSDictionary dictionaryWithObject:storedUUIDString forKey:#"UUID"];
localNotif.userInfo = infoDict;
// Schedule the notification and start the timer
[[UIApplication sharedApplication] scheduleLocalNotification:localNotif];
}
// Delete the specific local notification
- (void) deleteLocalNotification {
// Fast enumerate to pick out the local notification with the correct UUID
for (UILocalNotification *localNotification in [[UIApplication sharedApplication] scheduledLocalNotifications]) {
if ([[localNotification.userInfo valueForKey:#"UUID"] isEqualToString: storedUUIDString]) {
[[UIApplication sharedApplication] cancelLocalNotification:localNotification] ; // delete the notification from the system
}
}
}
// Create a unique identifier to allow the local notification to be identified
- (NSString *)createUUID {
CFUUIDRef theUUID = CFUUIDCreate(NULL);
CFStringRef string = CFUUIDCreateString(NULL, theUUID);
CFRelease(theUUID);
return (__bridge NSString *)string;
}
Most of the above has probably been lifted from StackOverflow at sometime in the last 6months. Hope this helps

Suppressing the text completion dropdown for an NSTextField

I'm trying to create the effect of an NSComboBox with completes == YES, no button, and numberOfVisibleItems == 0 (for an example, try filling in an Album or Artist in iTunes's Get Info window).
To accomplish this, I'm using an NSTextField control, which autocompletes on -controlTextDidChange: to call -[NSTextField complete:], which triggers the delegate method:
- (NSArray *)control:(NSControl *)control
textView:(NSTextView *)textView
completions:(NSArray *)words
forPartialWordRange:(NSRange)charRange
indexOfSelectedItem:(NSInteger *)index;
I've gotten this working correctly, the only problem being the side effect of a dropdown showing. I would like to suppress it, but I haven't seen a way to do this. I've scoured the documentation, Internet, and Stack Overflow, with no success.
I'd prefer a delegate method, but I'm open to subclassing, if that's the only way. I'm targeting Lion, in case it helps, so solutions don't need to be backward compatible.
To solve this, I had to think outside the box a little. Instead of using the built-in autocomplete mechanism, I built my own. This wasn't as tough as I had originally assumed it would be. My -controlTextDidChange: looks like so:
- (void)controlTextDidChange:(NSNotification *)note {
// Without using the isAutoCompleting flag, a loop would result, and the
// behavior gets unpredictable
if (!isAutoCompleting) {
isAutoCompleting = YES;
// Don't complete on a delete
if (userDeleted) {
userDeleted = NO;
} else {
NSTextField *control = [note object];
NSString *fieldName = [self fieldNameForTag:[control tag]];
NSTextView *textView = [[note userInfo] objectForKey:#"NSFieldEditor"];
NSString *typedText = [[textView.string copy] autorelease];
NSArray *completions = [self comboBoxValuesForField:fieldName
andPrefix:typedText];
if (completions.count >= 1) {
NSString *completion = [completions objectAtIndex:0];
NSRange difference = NSMakeRange(
typedText.length,
completion.length - typedText.length);
textView.string = completion;
[textView setSelectedRange:difference
affinity:NSSelectionAffinityUpstream
stillSelecting:NO];
}
}
isAutoCompleting = NO;
}
}
And then I implemented another delegate method I wasn't previously aware of (the missing piece of the puzzle, so to speak).
- (BOOL)control:(NSControl *)control
textView:(NSTextView *)textView doCommandBySelector:(SEL)commandSelector {
// Detect if the user deleted text
if (commandSelector == #selector(deleteBackward:)
|| commandSelector == #selector(deleteForward:)) {
userDeleted = YES;
}
return NO;
}
Update: Simplified and corrected solution
It now doesn't track the last string the user entered, instead detecting when the user deleted. This solves the problem in a direct, rather than roundabout, manner.