CLBeacon - how to change rssi, major and minor? - objective-c

My question is basically - how do I get to modify iBeacon's default settings like major, minor and RSSI?

There are different ways to set these values depending on what you mean by an iBeacon:
Hardware iBeacons
Each Beacon vendor has different ways of setting these values. Some are changed via a bluetooth service that is typically managed with a proprietary iOS or Android app. (Examples include Radius Networks' battery-powered and USB beacons and TwoCanoes beacons.) Radius Networks' PiBeacon includes an SD card with an editable file containing the identifiers. Other vendors like Estimote create beacons with fixed UUIDs that cannot be changed. Because there is no standard mechanism, there is no universal tool for setting identifiers on all beacon types.
iOS Software iBeacons:
You set these values with code like below:
CLBeaconRegion *region = [[CLBeaconRegion alloc] initWithProximityUUID:[[NSUUID alloc] initWithUUIDString:#"2F234454-CF6D-4A0F-ADF2-F4911BA9FFA6"] major:1 minor:1 identifier:#"com.radiusnetworks.iBeaconExample"];
NSDictionary *peripheralData = [region peripheralDataWithMeasuredPower:-55];
[_peripheralManager startAdvertising:peripheralData];
The iOS CLBeacon class
The CLBeacon class is not designed to be created or modified by the user -- it is supposed to be constructed by CoreLocation when it detects iBeacons. That said, you can force writing to its read-only properties using KVO syntax like so:
CLBeacon * iBeacon = [[CLBeacon alloc] init];
[iBeacon setValue:[NSNumber numberWithInt:1] forKey:#"major"];
[iBeacon setValue:[NSNumber numberWithInt:1] forKey:#"minor"];
[iBeacon setValue:[NSNumber numberWithInt:-55] forKey:#"rssi"];
[iBeacon setValue:[[NSUUID alloc] initWithUUIDString:#"2F234454-CF6D-4A0F-ADF2-F4911BA9FFA6"] forKey:#"proximityUUID"];
NSLog(#"I constructed this iBeacon manually: %#", iBeacon);
However, if you are forcing the CLBeacon class to be used in ways it was not designed to be used that might mean you are doing something wrong.
Full disclosure: I work for Radius Networks.

When you initialize a CLBeaconRegion object you can specify Major and Minor variables. Took a look at initWithProximityUUID:major:minor:identifier method.
As far as am aware ones a beacon active your cannot change it value unless you recreate that object. Rssi represents signal strength of the beacon which is only read-only and depends on the environment.
Here the link for the (documentation](https://developer.apple.com/library/iOs/documentation/CoreLocation/Reference/CLBeaconRegion_class/Reference/Reference.html#//apple_ref/doc/uid/TP40013054)

Related

Is It Possible To Customise CNContactPickerViewController?

I'm opening a CNContactPickerViewController for use in an application, but I want to alter the way it presents to suit my apps needs, preferably without rolling my own and doing a lot of re-inventing of the wheel. This is how I am opening it using Objective-C ....
self.contactPicker = [[CNContactPickerViewController alloc] init];
self.contactPicker.delegate = self;
//Only enable contacts to be selected that have atleast one email address
NSArray *propertyKeys = #[CNContactGivenNameKey, CNContactFamilyNameKey, CNContactEmailAddressesKey];
NSPredicate *enablePredicate = [NSPredicate predicateWithFormat:#"emailAddresses.#count != 0"];
self.contactPicker.displayedPropertyKeys = propertyKeys;
self.contactPicker.predicateForEnablingContact = enablePredicate;
[self presentViewController:self.contactPicker animated:YES completion:nil];
When it opens it currently looks like this:
However due to a bug in the SDK the search for people on this kind of view doesn't work, as you can not select from the search results. I'm going to file a bug for this but in the mean time firstly I want to hide the Search Bar. I found some old questions on removing the SearchBar but they related to the ABPeoplePickerNavigationController and are not relevant to CNContacts.
I also don't wish to use Groups and if I could remove that button and move the Cancel button over to the left that would be great, and would make the selection interface in my app much cleaner looking. This is how I would like it to look:
Could anyone tell me if this is possible and maybe point me in the right direction? I have the delegate method to receive the contacts array after selection, my problem is the way it looks in the app.
Thanks in advance!
Plasma
The real answer is the one you don't want to hear:
No, you can't modify the UI that Apple presents (at least in a meaningful way). You might be able to modify the tint color and other minor details, but you can't change whatever you like. If there's a bug in the search selector, you certainly can't fix that unfortunately. I'd suggest that just hiding the search bar isn't a great option, since for large contact lists, search is usually the primary way users will navigate to contacts.
Side note - a lot of Apple's framework view controllers are implemented
as a special sort of 'remote view controller'. The view controller
doesn't actually run as part of your app, but in the sandbox of the
parent application. This prevents any sort of trickery like traversing
and modifying the UI hierarchy of these presented controllers. I
wouldn't be surprised if that is the case here.
I would suggest that recreating the contact selection view isn't too hard, and gives you total flexibility in terms of customisation. I've done it myself, and there weren't any major hurdles to cross. Even if you're new to iOS, it would be a great learning exercise. For a good solution, you probably want to fetch all of the contacts on a background thread and display a loading spinner, since large contact databases could take a little while to fetch. (Even better, prefetch the contacts on the previous view, and only display a loading spinner if that fetch hasn't finished).
If you don't feel like doing that, I've seen a few contact selection frameworks around the place on GitHub. I'm not sure what the quality is like, but at the very least, these might provide a great starting point.
You can get an array of all contacts, and then display and work with it as you like:
- (NSMutableArray<CNContact *> *)allContacts {
NSMutableArray <CNContact *> *result = [NSMutableArray array];
NSError *error = nil;
NSArray *keysToFetch = #[CNContactEmailAddressesKey, CNContactPhoneNumbersKey, CNContactImageDataKey,
[CNContactFormatter descriptorForRequiredKeysForStyle:CNContactFormatterStyleFullName]];
CNContactStore *contactStore = [[CNContactStore alloc] init];
NSArray <CNContainer *> *allContainers = [contactStore containersMatchingPredicate:nil error:&error];
for (CNContainer *container in allContainers) {
NSPredicate *predicate = [CNContact predicateForContactsInContainerWithIdentifier:container.identifier];
NSArray *fetchedContacts = [contactStore unifiedContactsMatchingPredicate:predicate keysToFetch:keysToFetch error:&error];
[result addObjectsFromArray:fetchedContacts];
}
return result;}

how to refresh localizable.strings or NSLocalizedString(key, comment) ios during runtime

I'm working on a app that supports multiple language. Everything is going well. But I need to change the language during runtime.
I'm doing it this way -
NSArray* languages = [NSArray arrayWithObjects:#"es",#"en" nil];
[[NSUserDefaults standardUserDefaults] setObject:languages forKey:#"AppleLanguages"];
[[NSUserDefaults standardUserDefaults] synchronize];
It is working well.
NSString *langID = [[NSLocale preferredLanguages] objectAtIndex:0];
This langID is showing the correct language. But the app is not changing its language until its been restarted.
Is there any way to refresh localizable.strings file or NSLocalizedString(key, comment)? Or any other way to do it without restarting?
This has been asked many times already here on StackOverflow (search for things like iOS change language at runtime).
You can't change the language at runtime when using NSLocalizedString. If you really, really need that, you need to use a custom localization system instead. Better yet: simply don't do that: it's unexpected. After all, the user can set his/her preferred languages (and their order) in the system settings.
Someone even asked an Apple engineer and this is the response:
In general, you should not change the iOS system language (via use of the AppleLanguages pref key) from within your application. This goes against the basic iOS user model for switching languages in the Settings app, and also uses a preference key that is not documented, meaning that at some point in the future, the key name could change, which would break your application.
If you want to switch languages in your application, you can do so via manually loading resource files in your bundle. You can use NSBundle:pathForResource:ofType:inDirectory:forLocalization: for this purpose, but keep in mind that your application would be responsible for all loading of localized data.
To change the app language during runtime, I have done this manually by creating two different storyboards. I saved the language preference in NSUserDefaults but NOT with the key AppleLanguages and then called AppDelegate's didFinishLaunchingWithOptions method to select storyboard.
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainEnglish" bundle:nil];
UINavigationController *navigationcontroller=[[UINavigationController alloc]init];
RegistorViewController *registor=[storyboard instantiateViewControllerWithIdentifier:#"Registor"];
[self.window setRootViewController:navigationcontroller];
[self.window makeKeyAndVisible];
[navigationvontroller pushViewController:registor animated:NO];

Are there iOS devices that can monitor iBeacons but not range them?

Or asked another way will
[CLLocationManager isMonitoringAvailableForClass:[CLBeaconRegion class]]
and
[CLLocationManager isRangingAvailable]
ever return different values?
Short answer: No, there are not any iOS devices that can monitor iBeacons but not range them. Both methods will return the same value if isMonitoringAvailableForClass is given a CLBeaconRegion instance.
The reason the API looks this way is because the isMonitoringAvailableForClass method can be called with classes other than a CLBeaconRegion class. CLCircularRegion is used for monitoring geofence regions. The method might return NO when passed CLBeaconRegion on a device without LE Bluetooth, and return YES when passed a CLCircularRegion on the same device.
I believe there is one case when [CLLocationManager isMonitoringAvailableForClass:[CLBeaconRegion class]] will return NO and [CLLocationManager isRangingAvailable] will return YES.
If Background App Refresh is turned off [CLLocationManager isMonitoringAvailableForClass:[CLBeaconRegion class]] should return NO.
In Apple's Location and Maps Programming Guide under the "Determining the Availability of Region Monitoring" section:
Before attempting to monitor any regions, your app should check
whether region monitoring is supported on the current device. Here are
some reasons why region monitoring might not be available:
The device doesn’t have the necessary hardware to support region monitoring.
The user denied the app the authorization to use region monitoring.
The user disabled location services in the Settings app.
The user disabled Background App Refresh in the Settings app, either for the device or for your app.
The device is in Airplane mode and can’t power up the necessary hardware.
(I've bolded the fourth bullet point, because it's the case I'm talking about.)
Ranging is, however, only a foreground activity so Background App Refresh settings aren't important.
In this one case region monitoring will not be available, but ranging will be available.
Note: Currently, when Background App Refresh is turned [[CLLocationManager isMonitoringAvailableForClass:[CLBeaconRegion class]] returns YES, but when you start monitoring you'll never get notification and if you call requestStateForRegion: then locationManager:(CLLocationManager *)manager monitoringDidFailForRegion:(CLRegion *)region withError:(NSError *)error will be called on the CLLocationManager's delegate.
The error message will be "The operation couldn't be completed." with an error code of 4, which translates via CLError.h to kCLErrorRegionMonitoringDenied.
I hope Apple will fix the false positive in [[CLLocationManager isMonitoringAvailableForClass:[CLBeaconRegion class]] in one the next few updates.

How to use custom video resolution when use AVFoundation and AVCaptureVideoDataOutput on mac

I need to process each frame of captured video frame, although AVCaptureDevice.formats provided so many different dimension of frame sizes, it seems AVCaptureSession only support those frame sizes defined in presets.
I've also tried to set AVCaptureDevice.activeFormat before AVCaptureInputDevice or after, no matter what setting I set, if I set AVCaptureSessionPresetHigh in AVCaptureSession, it always give me a frame of 1280x720. Similar , If i set AVCaptureSessionPreset 640x480, then I can only get frame size of 640x480.
So, How can I set a custom video frame size like 800x600?
Using Media Foundation under windows or V4L2 under linux, it's easy to set any custom frame size when capture.
It seems not possible to do this under mac.
AFAIK there isn't a way to do this. All the code I've seen to do video capture uses the presets.
The documentation for AVCaptureVideoDataOutput for the video settings property says
The only key currently supported is the kCVPixelBufferPixelFormatTypeKey key.
so the other answers of passing in video settings won't work and it will just ignore these parameters.
Set kCVPixelBufferWidthKey and kCVPixelBufferHeightKey options on AVCaptureVideoDataOutput object. Minimal sample as below ( add error check ).
_sessionOutput = [[AVCaptureVideoDataOutput alloc] init];
NSDictionary *pixelBufferOptions = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithDouble:width], (id)kCVPixelBufferWidthKey,
[NSNumber numberWithDouble:height], (id)kCVPixelBufferHeightKey,
[NSNumber numberWithUnsignedInt:kCVPixelFormatType_32BGRA], (id)kCVPixelBufferPixelFormatTypeKey,
nil];
[_sessionOutput setVideoSettings:pixelBufferOptions];
Note: This width/height will override session preset width/height (if different).
Use videoSettings property of AVCapturwVideoDataOutput to describe the pixel format, width and height In a dictionary.

Re-Position MKMapView's userLocationView?

I'm making an app that has a series of trails in the forest. GPS is going to be off now and then by few dozen meters, so I'm trying to write some code that will "snap" the userLocationView to the trail (which is where the user likely is).
I've got some code working that scans the nearby trails, determines the probable location of the user, and gets the CLLocationCoordinate2D for it.
Now I need to apply that location to the userLocationView. I thought that this would work, but it doesn't seem to:
userLocationAnnotation.coordinate = myDerivedCoordinate;
I realize I could probably create my own annotation, but I would like to avoid that if possible.
MapView gets the user location directly from CLLocationManager, and there isn't a documented way to intercept this interaction. Doing so would be more work than adding your own annotation, and a cause for app rejection. Also, it doesn't make sense to redefine correct information provided by the system. That's why mapView.userLocation is a MKUserLocation with a readonly location property.
So you have to create your own, roughly:
Set mapView.showsUserLocation = false;
Create and start a location manager
self.locationManager = [CLLocationManager new]; self.locationManager.delegate = self; [self.locationManager startUpdatingLocation];
Add/replace your own blue dot annotation from locationManager:didUpdateToLocation:fromLocation: of the CLLocationManagerDelegate.
You can get the blue dot graphic (and probably the pulsating circles) with the UIKit-Artwork-Extractor. For the pulsating effect add to the MKAnnotationView a UIImageView with the different frames, or take one circle and use UIView animateWithDuration with a CGAffineTransformMakeScale for the view.transform property.