I have Location Tracking class and another class with listing. I need to check if locationService is running when entering listView. In the locationService class I've added boolean to track service start and stop. But I can't track this boolean variable from listView class.
here is my code how I am doing now, but it takes only initial value of the locationService class
//listView.m
#property (strong,nonatomic) LocationTracker * trackerClass;
- (void)viewWillAppear:(BOOL)animated{
self.trackerClass = [[LocationTracker alloc] init];
if([self.trackerClass getTrackerStatus]==1)
NSLog(#"LOCATION SERVICE STATUS IS RUNNING");
else
NSLog(#"LOCATION SERVICE STATUS IS STOPPED");
}
//locationService.h
#property (nonatomic, readwrite) int trackerStatus;
- (int)getTrackerStatus;
//locationService.m
- (void)stopLocationTracking {_trackerStatus=0;}
- (void)startLocationTracking {_trackerStatus=1;}
- (int)getTrackerStatus{
return _trackerStatus;
}
thank you in advance!
Try to set this boolean variable as a local variable and create a simple static getter to get this bool. your LocationTracker will change the value of the variable and you can get it from anywhere.
#implementation LocationTracker
BOOL isStart;
//Dont forget to delclear this methode in the .h file
+(BOOL)isStart{
return isStart;
}
your code...
Any where in your app just import your calss and wirte:
BOOL isStart = [LocationTracker isStart];
//The getting vaiable should be the updated bool in you LocationTracker class
Try deleting
- (int)getTrackerStatus{
return _trackerStatus;
}
Related
Im trying to figure out if my instance of WKInterfaceSwitch is currently in on or off position
You can't do that. You need to track with a variable the status of the WKInterfaceSwitch in your code.
Let's say your default value for a WKInterfaceSwitch is false.
In your awakeWithContext: method do this:
- (void)awakeWithContext:(id)context {
[super awakeWithContext:context];
self.switchStatus = NO;
}
In Objective-C you would declare a property with a BOOL value.
#property (nonatomic, assign) BOOL switchStatus;
Then create an action from your Switch object to your header file.
- (IBAction)valueChanged:(BOOL)value;
And in the implementation file write.
- (IBAction)valueChanged:(BOOL)value {
self.switchStatus = value;
}
You are now able to check the status of your Switch by just using self.switchStatus for example like this:
NSLog(#"Switch is now: %#", self.switchStatus ? #"true" : #"false");
I hope this helps.
I have trouble implementing a Key-Value Observer at my attempt to follow the MVC pattern. I have a controller class, a model class and a view class. I update my model from the controller class and I want to put a key value observer in my view class to monitor when a NSMutableArray changes in model (like through addObject) and then redraw itself automatically. I used answer in this thread to guide me: How to add observer on NSMutableArray?
Code so far:
From my Scene (using sprite kit if it matters). Setting of letters will be done from Ctrl class, this is just to test.
BarCtrl *barCtrl = [[BarCtrl alloc] init];
BarModel *barModel = [[BarModel alloc] init];
BarView *barView = [[BarView alloc] init];
barCtrl.barModel = barModel;
barCtrl.barView = barView;
barView.barModel = barModel;
ScrabbleDeck *sd = [[ScrabbleDeck alloc] init];
if([barModel addLetter:[sd getLetter] onSide:BarModelSideRight])
NSLog(#"Added letter");
BarModel.h
#import <Foundation/Foundation.h>
#import "Letter.h"
typedef NS_ENUM(int, BarModelSide) {
BarModelSideLeft,
BarModelSideRight
};
#interface BarModel : NSObject
#property (nonatomic, strong) NSMutableArray *addedLetters;
- (instancetype)init;
- (BOOL) addLetter: (Letter*) letter onSide: (BarModelSide) side;
#end
BarModel.m
#import "BarModel.h"
#interface BarModel ()
#property (nonatomic) int capacity;
#end
#implementation BarModel
- (instancetype)init
{
self = [super init];
if (self) {
self.capacity = letterCapacity;
_addedLetters = [[NSMutableArray alloc] init];
}
return self;
}
// We'll use automatic notifications for this example
+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key
{
if ([key isEqualToString:#"arrayLetter"]) {
return YES;
}
return [super automaticallyNotifiesObserversForKey:key];
}
- (BOOL) addLetter: (Letter*) letter onSide: (BarModelSide) side{
if([_addedLetters count] > _capacity){
return FALSE;
}
switch (side) {
case BarModelSideLeft:
[_addedLetters insertObject:letter atIndex:0];
return TRUE;
break;
case BarModelSideRight:
[_addedLetters addObject:letter];
return TRUE;
break;
default:
return FALSE;
break;
}
}
// These methods enable KVC compliance
- (void)insertObject:(id)object inDataAtIndex:(NSUInteger)index
{
self.addedLetters[index] = object;
}
- (void)removeObjectFromDataAtIndex:(NSUInteger)index
{
[self.addedLetters removeObjectAtIndex:index];
}
- (id)objectInDataAtIndex:(NSUInteger)index
{
return self.addedLetters[index];
}
- (NSArray *)dataAtIndexes:(NSIndexSet *)indexes
{
return [self.addedLetters objectsAtIndexes:indexes];
}
- (NSUInteger)countOfData
{
return [self.addedLetters count];
}
#end
BarView.h
#import <SpriteKit/SpriteKit.h>
#import "BarModel.h"
#interface BarView : SKSpriteNode
#property (nonatomic, strong) BarModel *barModel;
#end
BarView.m
#import "BarView.h"
#implementation BarView
static char MyObservationContext;
- (instancetype)init
{
self = [super init];
if (self) {
//_barModel = [[BarModel alloc] init];
}
return self;
}
-(void)setBarModel:(BarModel *)barModel{
if(_barModel != barModel)
_barModel = barModel;
[_barModel addObserver:self
forKeyPath:#"arrayLetter"
options:(NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew)
context:&MyObservationContext];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
// Check if our class, rather than superclass or someone else, added as observer
if (context == &MyObservationContext) {
// Check that the key path is what we want
if ([keyPath isEqualToString:#"arrayLetter"]) {
// Verify we're observing the correct object
if (object == self.barModel) {
[self draw:change];
}
}
}
else {
// Otherwise, call up to superclass implementation
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}
- (void) draw: (NSDictionary*) change{
NSLog(#"KVO for our container property, change dictionary is %#", change);
}
#end
When I ru this I get this "error":
2014-08-31 00:23:02.828 Testing[329:60b] Added letter
2014-08-31 00:23:02.830 Testing[329:60b] An instance 0x17803d340 of class BarModel was deallocated while key value observers were still registered with it. Observation info was leaked, and may even become mistakenly attached to some other object. Set a breakpoint on NSKVODeallocateBreak to stop here in the debugger. Here's the current observation info:
<NSKeyValueObservationInfo 0x17804eb50> (
<NSKeyValueObservance 0x1780cf180: Observer: 0x178111670, Key path: arrayLetter, Options: <New: YES, Old: YES, Prior: NO> Context: 0x100101428, Property: 0x17804eb80>
I tried to follow the instructions in error but can not find where to set break point. Please help me figure this out!
The error is pretty descriptive. You add self as an observer of a BarModel object. At some point that object gets deallocated. But you never remove self as an observer by calling removeObserver:forKeyPath:context:. You need to do that.
First, in setBarModel, make sure to remove self as an observer of the previous value of _barModel.
Next, you probably need to add a dealloc method that does the same thing.
There are multiple problems with the code. In addition to what Tom Harrington said with respect to the specific error that was logged about failing to remove the observation:
You implemented the indexed collection accessors for a (non-existent) property named "data". That is, they have "Data" in their name where the property name should be.
The indexed collection property is addedLetters. So, the indexed collection mutating accessors should be:
- (void)insertObject:(id)object inAddedLettersAtIndex:(NSUInteger)index;
- (void)removeObjectFromAddedLettersAtIndex:(NSUInteger)index;
You don't really need the non-mutating accessors, since you have an array-type public property with a normal getter (i.e. -addedLetters).
By the way, that property is of type NSMutableArray which it should not be. The property should be of type NSArray, backed by an instance variable of type NSMutableArray. That is, the mutability of the type (as opposed to the property) should not be exposed through the public interface. When you do this, you have to manually declare the instance variable (since it should differ from the type of the property and auto-synthesis will get it wrong), make the property copy instead of strong, and implement the setter yourself to do a mutable copy of the passed-in immutable array:
- (void) setAddedLetters:(NSArray*)addedLetters
{
if (addedLetters != _addedLetters)
_addedLetters = [addedLetters mutableCopy];
}
Once you have implemented the indexed collection mutating accessors with the correct names, you must use only those methods to mutate the collection (after initialization). In particular, your -addLetter:onSide: method must not directly operate on the _addedLetters instance variable. This is the part that makes the class KVO-compliant for that property. The mere presence of the indexed collection mutating accessors does not help. They must be used for all actual mutations.
Your implementation of +automaticallyNotifiesObserversForKey: is redundant. Automatic notification is the default.
The BarView class is key-value observing a key path "arrayLetter" on its _barModel object, but that's not the name of the property on BarModel. I suppose you meant to use the key path "addedLetters".
Finally, for proper adherence to MVC design, your view should not have a reference to your model. It should have a reference to the controller. The controller can reflect the model to the view (or, in theory, adapt a model of a different internal design to what the view expects). Or, in a more traditional non-KVO approach, the controller would actually tell the view when something has changed and give it the updated data it should show.
So, your controller could expose its own addedLetters property:
#property (readonly, copy, nonatomic) NSArray* addedLetters;
It could be implemented as a derived property, forwarded through to the _barModel object:
+ (NSSet*)keyPathsForValuesAffectingAddedLetters
{
return [NSSet setWithObject:#"barModel.addedLetters"];
}
- (NSArray*)addedLetters
{
return self.barModel.addedLetters;
}
Then, the view would have a reference to the controller and not the model, and it would key-value observe the "addedLetters" key path on the controller.
I'm trying to have another object call a selector. I'm attempting to define this selector from another class by defining the selector property. It doesn't seem to be working like I expect.
ComboBox.h
#property (nonatomic) SEL onComboSelect;
ComboBox.m
-(void)doneClicked:(id) sender
{
[textField resignFirstResponder]; //hides the pickerView
NSLog(#"DONE CLICKED CALLED");
[self performSelector:#selector(onComboSelect)];
}
OtherClass.h
#interface OtherClass : BaseViewController
{
ComboBox *combo;
}
-(void)comboSelector;
OtherClass.m
// in viewDidLoad
combo = [[ComboBox alloc] init];
combo.onComboSelect = #selector(comboSelector);
-(void)comboSelector
{
NSLog(#"COMBO SELECTOR");
}
I see "DONE CLICK CALLED" in the logs, but not "COMBO SELECTOR". So I know doneClicked is being called, but the selector doesn't seem to be working. What am I doing wrong? Is there a better way to do this?
A #selector is just a method name - it does not include any context about the class on which it is defined. So this [self performSelector:#selector(onComboSelect)] is just invoking the method on self. In addition to the selector, you also need a reference to the object on which you want to call it.
Notice how some built-in classes (like UIControl) take both a target object and action selector.
There are a 2 major issues in your code.
1.
onComboSelect is a SEL so no need to use the #selector again.
Instead of:
[self performSelector:#selector(onComboSelect)];
Use :
[self performSelector:onComboSelect];
2.
You are calling the selector on self from ComboBox class, so it'll call the selector on ComboBox object (if defined) not on OtherClass object
Your answers were helpful. Here is what I did:
added to ComboBox.h
#property (nonatomic, weak) UIViewController *parentViewController;
added to ComboBox.m
-(void)doneClicked:(id) sender
{
[textField resignFirstResponder]; //hides the pickerView
if ([parentViewController respondsToSelector:#selector(comboSelector)])
[parentViewController performSelector:#selector(comboSelector)];
}
added to OtherClass.m
combo.parentViewController = self;
#property (nonatomic) SEL onComboSelect <-- This property is not needed in ComboBox.h.
SEL is a point of objc_seletor,and in object_seletor runtime can find the
objc_method ,that define
objc_method {
SEL method_name OBJC2_UNAVAILABLE;
char *method_types OBJC2_UNAVAILABLE;
IMP method_imp OBJC2_UNAVAILABLE;
}
and IMP is the point of method,and you can find this in "runtime.h" file.
Any noe kow how to find objc_method by object_seletor ? and I cannot find the define of objc_seletor struct.
I'm building a game with 3 different level difficulties (Easy, Medium, & Delete) I have 2 ViewControllers and 1 NSObject class that I'm using to set and get the game difficulty value (0=easy, 1=Medium, 2=Delete(hard). Within context of how I'm doing it, I've research and tried many variations with no success. NOTE: I have tried to code this, but have since stripped out that part of the code because it wasn't working. I'm somewhat familiar with NSUserDefaults, so if that's a better solution, please advise. I may need a little direction on setting that up, though.
- ViewController 1 (RewindSettingsViewController): I have 3 buttons: Easy, Medium, Delete. User taps button, they go to ViewController 2
- ViewController 2 (RewindGameViewController): This is the actual game with a Start button. This is where I'm hung up; getting the difficulty passed into this view.
CODE:
.h - GameManager : NSObject
+(void) setDifficulty:(int)difficulty;
+(int) getDifficulty;
.m - GameManager
static int _difficulty;
+(void)setDifficulty:(int)difficulty {
_difficulty = difficulty;
}
+(int)getDifficulty {
return _difficulty;
}
(VC1) .h - RewindSettingsViewController : UIViewController
#property (nonatomic, strong) IBOutlet UIButton *easyModeButton;
#property (nonatomic, strong) IBOutlet UIButton *mediumModeButton;
#property (nonatomic, strong) IBOutlet UIButton *hardModeButton;
-(IBAction)easyMode;
-(IBAction)mediumMode;
-(IBAction)hardMode;
.m - RewindSettingsViewController
-(IBAction)easyMode {
[GameManager setDifficulty:0];
}
-(IBAction)mediumMode {
[GameManager setDifficulty:1];
}
-(IBAction)hardMode {
[GameManager setDifficulty:2];
}
(VC2) .h - RewindGameViewController : UIViewController
no code here for difficulty settings
.m - RewindGameViewController
At this point, _difficulty is of course still nil. My question marks below are my pain points
?: How to get the difficulty value stored in "setDifficulty" into "getDifficulty (_difficulty)" and properly call it in the IF statement?
?: I'm thinking _difficulty should be in the IF statements, but with everything I'm trying, syntax keeps barking at me.
-(void)placeObjectsRewind {
randomTopObjectRewind = arc4random() %350;
randomTopObjectRewind = randomTopObjectRewind - 228;
if (?) {
randomBottomObjectRewind = randomTopObjectRewind + 700; //LEVEL DIFFICULTY: EASY
objectTopRewind.center = CGPointMake(0, randomTopObjectRewind);
objectBottomRewind.center = CGPointMake(0, randomBottomObjectRewind);
} else if (?) {
randomBottomObjectRewind = randomTopObjectRewind + 665; //LEVEL DIFFICULTY: MEDIUM
objectTopRewind.center = CGPointMake(0, randomTopObjectRewind);
objectBottomRewind.center = CGPointMake(0, randomBottomObjectRewind);
} else if (?) {
randomBottomObjectRewind = randomTopObjectRewind + 635; //LEVEL DIFFICULTY: DELETE (HARD)
objectTopRewind.center = CGPointMake(0, randomTopObjectRewind);
objectBottomRewind.center = CGPointMake(0, randomBottomObjectRewind);
}
}
Instead of
static int _difficulty;
type this into your GameManager.h
#property int difficulty;
You don't have to write the Setter and Getter by yourself, the property-statement do this for you. The Getter/Setter is stored in the instance of GameManager you use. So get the instance of you GameManager and there you set like this:
[MyGameManagerInstance setDifficulty:0];
and get like
[MyGameManagerInstance difficulty];
Due to the fact that the difficulty-property is in the .h file, it's part of the Class's API which you can access from other ViewController or Classes.
EDIT:
Like you said, you could do this also with the NSUserDefaults. Just put it in like this:
[[NSUserDefaults standardUserDefaults] setInteger:1 forKey:#"Difficulty"];
and get it like this:
int myInt = [[NSUserDefaults standardUserDefaults] integerForKey:#"Difficulty"];
Since you've made the methods you want to use class methods, the syntax is going to be similar to if ([GameManager getDifficulty] == 0) etc.
(As a style point, you would usually drop the "get" in Objective-C and just name the method to return a value difficulty.)
I'm new to Objective-C and I'm trying to determine if the NSString being passed to my method is the same as the NSString previously passed to the same method.
Is there a simple way to do this?
If you're looking to do this per instance of your class (and not globally):
#interface MyClass : NSObject
- (void)myMethod:(NSString *)value;
#end
#interface MyClass ()
#property (copy, nonatomic) NSString *value;
#end
#implementation MyClass
- (void)myMethod:(NSString *)value
{
if ([self.value isEqualToString:value])
{
// Values are the same!
}
else
{
self.value = value;
}
}
#end
Store the string as an instance variable of the class, each time the method is called, compare the instances and replace with the new parameter.
Just to elaborate on what #Wain said:
Add Instance Variable:
#interface ViewController ()
{
NSString * lastString;
}
Then in your method:
- (void) methodWithString:(NSString *)string {
if ([string isEqualToString:lastString]) {
NSLog(#"Same String");
}
else {
NSLog(#"New String");
lastString = string;
}
}
A variation on the theme most answers are following: If you wish to do this per instance of your class then you can use an instance variable. However as this is something which is really specific to your method, you don't want to declare this variable in any interface and recent compilers help you with this by enabling instance variable declaration in the implementation. For example:
#implementation MyClass
{
NSString *methodWithString_lastArgument_; // make it use clear
}
- (void) methodWithString:(NSString *)string
{
if ([string isEqualToString:methodWithString_lastArgument_])
{
// same argument as last time
...
}
else
{
// different argument
isEqualToString:methodWithString_lastArgument_ = string.copy; // save for next time
...
}
}
(The above assumes ARC.)
The string.copy is shorthand for [string copy] and is there to handle mutable strings - if the method is passed an NSMutableString then this will copy its value as an (immutable) NSString. This protects the method from the mutable string changing value between calls.
If you want to do this on a global basis, rather than per instance, you can declare a static variable within your method and thus completely hide it from outside the method:
- (void) methodWithString:(NSString *)string
{
static NSString *lastArgument = nil; // declare and init private variable
if ([string isEqualToString:lastArgument])
{
// same argument as last time
...
}
else
{
// different argument
isEqualToString:lastArgument = string.copy; // save for next time
...
}
}
HTH