I am getting the battery level of my bluetooth le device (batteryLevel) which is a float in one view. I want to pass it to another view to display it in a textfield.
code in view one
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:
(CBCharacteristic *)characteristic error:(NSError *)error
{[self.testPeripheral readValueForCharacteristic:mycharacteristic];
char batlevel;
[characteristic.value getBytes:&batlevel length:1];
self.batteryLevel = (float)batlevel;
NSLog(#"level;%f",batteryLevel);}
this gives me a value like 80.00000
I want to put this into another view to display.
I have tried in view2.h file I have placed
view1 *t
and then
- (void) batteryIndicatorTimer:(NSTimer *)timer {
TIBLEUIBatteryBar.progress = t.batteryLevel / 100;
[t readBattery:[t testPeripheral]]; // Read battery value of keyfob again
NSLog(#"t.batterylevel:%f",t.batteryLevel);
}
but I don't get a value for t.batteryLevel
what am I doing wrong and how can I do this?
There's a myriad number of ways to accomplish this using delegates or assigning properties in presentingViewControllers/destinationViewControllers, but since I don't know your app flow I'll give you a way that should work with whatever setup you have. Just update batteryLevel in your viewController that is the peripheralDelegate then simply save the value to the NSUserDefaults:
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:
(CBCharacteristic *)characteristic error:(NSError *)error
{
//Grab your battery value and store in float
//Then just save to defaults so you can access wherever.. Keep overwriting this value each time you update
[[NSUserDefaults standardUserDefaults]setFloat:batteryVal forKey:#"battery_level"];
}
And then inside your other viewController just assign the value inside viewWillAppear or something.
-(void)viewWillAppear:(BOOL)animated
{
float batteryLevel = [[NSUserDefaults standardUserDefaults]floatForKey:#"battery_level"];
self.yourTextField.text = [NSString stringWithFormat:#"%f",batteryLevel];
}
Related
In iOS app widget, I can see on only some devices, doubled data (see figure below). I have tried to identify device, iOS version, but it seems to be "random". Plus, I am unable to debug this by myself, because on every of my devices, all is rendered correctly and doing blind debugging is not working (several updates on AppStore but still with the same error).
In widget, I download (in background thread) new data from web and put them (in dispatch_get_main_queue()) into labels, images etc. All is working OK, but sometimes the old data are not "cleared". In my design file for widget, I have cleared all "default" texts, so this is not this problem.
Doubled icon & texts 4.1°C and 7.9°C are overlapping
Main part of my widget code is (shortened by removing other labels, tables and geolocation):
- (void)viewDidLoad
{
[super viewDidLoad];
if ([self.extensionContext respondsToSelector:#selector(widgetLargestAvailableDisplayMode)])
{
//this is iOS >= 10
self.extensionContext.widgetLargestAvailableDisplayMode = NCWidgetDisplayModeExpanded;
}
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(FinishDownload:) name:#"FinishDownload" object:nil];
self.preferredContentSize = CGSizeMake(320, 160);
[self updateData];
}
-(void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[self updateData];
}
-(void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self updateData];
}
-(void)updateData
{
[[[DataManager SharedManager] settings] Reload];
[[CoreDataManager SharedManager] reset];
if ([[DataManager SharedManager] DownloadDataWithAfterSelector:#"FinishDownload"] == NO)
{
//no need to download update - refill data now
//if downloading - wait for download
[self FillData];
}
}
}
-(void)FinishDownload:(NSNotification *)notification
{
dispatch_async(dispatch_get_main_queue(), ^{
[self FillData];
});
}
-(void)FillData
{
//a lot of code - example of setting temperature
NSString *str = [NSString stringWithFormat:#"%# °C", act.temp_act];
self.lblTemp.text = str;
[self.lblTemp sizeToFit];
if (self.completionHandler != nil)
{
self.completionHandler(NCUpdateResultNewData);
}
}
- (void)widgetPerformUpdateWithCompletionHandler:(void (^)(NCUpdateResult))completionHandler
{
// Perform any setup necessary in order to update the view.
// If an error is encountered, use NCUpdateResultFailed
// If there's no update required, use NCUpdateResultNoData
// If there's an update, use NCUpdateResultNewData
//completionHandler(NCUpdateResultNewData);
NSLog(#"=== widgetPerformUpdateWithCompletionHandler === ");
self.completionHandler = completionHandler;
[self updateData];
}
- (UIEdgeInsets)widgetMarginInsetsForProposedMarginInsets:(UIEdgeInsets)defaultMarginInsets
{
return UIEdgeInsetsMake(0, 0, 5, 5);
}
- (void)widgetActiveDisplayModeDidChange:(NCWidgetDisplayMode)activeDisplayMode withMaximumSize:(CGSize)maxSize
{
if (activeDisplayMode == NCWidgetDisplayModeExpanded)
{
self.preferredContentSize = CGSizeMake(320, 160);
}
else if (activeDisplayMode == NCWidgetDisplayModeCompact)
{
self.preferredContentSize = maxSize;
}
}
View Lifecycle
Do not duplicate the work in viewDidLoad and viewWillAppear/viewDidAppear.
A view that was loaded will hit all three methods. Use viewDidLoad for operations that must be performed exactly once for the life of the UIViewController.
Potential problem:
Triggering 3 invocations, possibly conflicting, to [self updateData] back to back, possibly with competing NCUpdateResult completion handlers3.
Balance Observers
It appears that addObserver is never balanced by a removeObserver. A good location for these registration methods is a set of balanced messages, such as the view___Appear and view___Disappear methods, as outlined in this StackOverflow answer.
Potential problem:
Lasting registration to notifications on objects that may go out of scope.
Do not cache OS handlers
Possible misuse of NCUpdateResultNewData completion handler: the NCUpdateResult is passed to widgetPerformUpdateWithCompletionHandler to be used for that specific invocation, not stored for multiple reuse. It should probably be handed down to updateData as a parameter rather than stored in a global, in turn passed to FillData, and eventually cleared after a one-time use.
if (nil != self.completionHandler) {
self.completionHandler(NCUpdateResultNewData);
self.completionHandler = nil; // One time use
}
Every invocation to widgetPerformUpdateWithCompletionHandler has its own cycle, as outlined in this StackOverflow answer.
Layout & Autolayout
Be aware that the iOS is making a snapshot of your widget ; in Interface Builder, make sure that you use proper layering of views. Pay special attention to transparency and drawing flags. Leverage Autolayout to resize/size/snap objects
Check the UILabel's options in Interface Builder, make sure 'opaque' is unchecked. If the label is set as opaque, it might not be properly clearing the entire view when you change the text. You probably want to check on the 'clears graphics context' property as well, which should be checked.
In the code you add a Notification observer. You do not remove the observer.
I suspect that the notification will be fired multiple times which will result jn a race condition or something.
Solution:
- check hoe often the addObserver is executed. (Including screen changes like back-forward etc)
remove the observer when the notification is caught.
clear / remove the observer when leaving the VC
Besides: check / reduce the action in the ViewWillAppear and ViwDidAppear.
I've got a BOOL that seems to change value without being assigned too! However, it only happens when switching between view controllers. Basically I have a TableViewController going to a DetailViewController, the BOOL is on the DetailViewController. It is used to determine whether a timer is currently running. When just staying on the DetailViewController the BOOL works as expected, however when switching between DetailViewController and TableViewController, that's when the problem occurs. In no place on TableViewController is it being assigned to.
I have NSLogged all over the place, I've used lots of breakpoints and also watchpoints on the BOOL itself, however I still can't figure out what's making it change!
Okay, on the viewDidLoad of DetailViewController, the BOOL is updated from a Core Data entity.
- (void)viewDidLoad {
[super viewDidLoad];
// MORE CODE
_running = [[_timerObject valueForKey:#"running"] boolValue];
****_running is NO on initial load**** - expected
****_running is YES when returning to DetailViewController**** - expected
// MORE CODE
}
I then have a button to start/cancel the timer and use the BOOL to toggle the button.
- (IBAction)start:(id)sender {
****_running is NO on first press**** - expected
****_running is YES on second press**** - NOT EXPECTED!?!?!?!
// if timer is running
if (_running) {
// CANCEL TIMER CODE
}
else {
// START TIMER CODE
}
// Update BOOL
_running = !_running;
****_running is YES**** - expected
}
When the timer completes, this method is called.
- (void)timerComplete {
****_running is YES**** - expected
// OTHER CODE HERE
// Cancel timer
[_appDelegate.timerManager stopTimer:_timerID];
// Update BOOL
_running = NO;
****_running is NO**** - expected
// Save to ensure booleans are correct
[self save];
****_running is NO**** - expected
}
My save method looks like this. It is called at the end of timerComplete and also in the viewDidDisappear method.
- (void)save {
// Create variable of managed object context AppDelegate
NSManagedObjectContext *context = [self managedObjectContext];
if (_timerObject) {
// Update existing timer
if ([_nameTextField.text isEqualToString:#""]) {
[_timerObject setValue:#"Timer" forKey:#"name"];
}
else {
[_timerObject setValue:_nameTextField.text forKey:#"name"];
}
[_timerObject setValue:_date forKey:#"timeStart"];
[_timerObject setValue:[NSNumber numberWithBool:_running] forKey:#"running"];
[_timerObject setValue:[NSNumber numberWithBool:_paused] forKey:#"paused"];
//----------------
// Update title of view
self.title = [_timerObject valueForKey:#"name"];
}
NSError *error = nil;
// Save the object to persistent store
if (![context save:&error]) {
NSLog(#"Can't Save! %# %#", error, [error localizedDescription]);
}
****_running is NO**** - expected
}
The behaviour required to break it is:
- Load DetailViewContoller
- Press Start Timer
- Go back to TableViewController
- Go to DetailViewController
- Wait for Timer to Complete
- Press Start Timer again and nothing happens because _running is YES, not NO!
I've annotated the code segments with the values of "_running" in various different places.
As far as I can tell, nothing happens between timerComplete and pressing start again so I don't know why the BOOL changes from NO to YES in that time! Any help would be greatly appreciated! :)
Just an extra note
NSLog(#"timerObject: %#", [_timerObject valueForKey:#"running"]);
outputs a 0 when placed in the same placed as where _running returns YES in the start timer method! So the core data value appears to be correct, just not _running!
I can't get didReadRSSI to call back on my peripheral!!
I'm developing on an iPad mini - iOS 8.1.2
I set the peripheral to an NSMutable array and I can call connect, disconnect etc. fine from that array so the peripheral object attached to that array is valid.
My code is below, what is wrong? Why don't I get a didReadRSSI callback on my peripheral??
- (void)updateConnectedRSSITimerFunc {
for(TheCustomPeripheral *arrayItem in self.peripherals) {
if(arrayItem.connected) {
//This is called every 4 seconds from an NSTimer succesfully
[arrayItem.peripheral readRSSI];
NSLog(#"RSSI request");
}
}
}
-(void) peripheral:(CBPeripheral *)peripheral didReadRSSI:(NSNumber *)RSSI error:(NSError *)error {
//This never get's called!!!!!
NSLog(#"RSSI returned %#", [RSSI stringValue]);
}
Solved!!! In the timer I added this... I'm still new to iOS so I missed the delegate bit to get callbacks...
arrayItem.peripheral.delegate = self;
As a supplementary question, why do I get this warning? I'm ignoring it because the app runs fine.
Assigning to 'id' from incompatible type 'xxxListTableViewController *const __strong'
Answer: My class had to be a CBPeripheralDelegate, e.g. #interface myInterface ,CBPeripheralDelegate>
I am wondering if there is a way to maintain user input when navigating back and forth to my main menu using the UINavigationBar back arrow using NSCoder. I am currently using NSCoder to maintain this data during background/terminate/restart and NSUserDefaults to maintain data between views, but the combination of NSCoder and NSUserDefaults is giving unpredictable results. Sometimes, both types of restoration work, but sometimes the BG/Term/RS restoration does not work, and I am talking about loading the same identical code on my device. When the NSUserDefaults code is commented out, the BG/Term/RS restoration works every time.
I would like to know if it is possible to use NSCoder for all of my restoration needs, and if so, what that code would look like.
This is what I am using for BG/Term/RS restoration:
-(void)encodeRestorableStateWithCoder:(NSCoder *)coder {
// start level text
[coder encodeObject:_startLevel.text forKey:#"startText"];
// stop level text
[coder encodeObject:_stopLevel.text forKey:#"stopText"];
}
-(void)decodeRestorableStateWithCoder:(NSCoder *)coder {
// start level text
_startLevel.text = [coder decodeObjectForKey:#"startText"];
// stop level text
_stopLevel.text = [coder decodeObjectForKey:#"stopText"];
}
This is the NSUserDefaults code that I am currently using to persist data back and forth between my menu and main view, and which I would ideally like to replace with an NSCoder solution:
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
// start level
[[NSUserDefaults standardUserDefaults] setObject:_startLevel.text
forKey:#"startLevelRestore"];
// stop level
[[NSUserDefaults standardUserDefaults] setObject:_stopLevel.text
forKey:#"stopLevelRestore"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
// start level
[_startLevel setText:[[NSUserDefaults standardUserDefaults]
objectForKey:#"startLevelRestore"]];
// stop Level
[_stopLevel setText:[[NSUserDefaults standardUserDefaults]
objectForKey:#"stopLevelRestore"]];
[[NSUserDefaults standardUserDefaults] synchronize];
}
I have been battling this issue for a couple of days with no luck. Any help would be greatly appreciated! Thanks.
Instead of setting UI objects in decodeRestorableStateWithCoder: use a member variable.
Then in viewWillAppear: use the member variable if it has been set otherwise use the value from NSUserDefault.
I'm trying to make a multiplayer memory game with game center.
I followed the tutorial in this link, connected two devices to a match, got the match started.
In the game, when one device flip a card, the other must flip that card too.To do this, I have to send an id type value to the other device, so I can flip the card in two devices.
Here is the code;
- (void)gameTouchBegin:(id)target {
[target flipTheCard];
[self sendCardMove:target];
}
- (void)sendCardMove:(id)target {
MessageCard message;
message.message.messageType = kMessageTypeCard;
message.target = target;
NSData* data = [NSData dataWithBytes:&message length:sizeof(MessageCard)];
[self sendData:data];
}
- (void)match:(GKMatch *)match didReceiveData:(NSData *)data fromPlayer:(NSString *)playerID {
if (message->messageType == kMessageTypeCard) {
NSLog(#"Other player flipped the card.");
// I can successfully write this line.
// So I can reach inside this function.
MessageCard * messageInit = (MessageCard *) [data bytes];
id gTarget = (id)messageInit->target;
[gTarget flipTheCard];
// At the three code lines above, I got an error.
}
}
As I commented at the end of the code, I got EXC_BAD_ACCESS error and the app crashes.
I know this is a complicated stuff, anyone have an idea?
Thanks in advance.
I think that may cause the problem.
I define the message body like this;
typedef struct {
Message message;
id target;
} MessageCard;
I suppose xCode doesn't let me define "id" type variables in struct. So I can't send id type to other device. There is no solution about that, I must find another way to find out which card it is.
Thank you.