How to set Custom Properties in iOS? [duplicate] - ios

This question already has an answer here:
How to update an NSManagedObject whenever a specific attribute is changed?
(1 answer)
Closed 8 years ago.
I am using Core Data and would like to run some custom code when setting a property.
#interface FTRecord : NSManagedObject
#property (nonatomic) NSTimeInterval timestamp;
#implementation FTRecord
#dynamic timestamp;
-(void)setTimestamp:(NSTimeInterval)newTimestamp
{
//run custom code....
//and now how to pass the value to the actual property?
[self setTimestamp:newTimestamp];
}
In this case I have defined the setter body for the timestamp property. But how do I set the value of the property without running into a recursion loop?

There's a magical accessor generated for each property, which in your case would be called setPrimitiveTimestamp: which you can use for this. Take a look at the docs for NSManagedObject's - (void)setPrimitiveValue:(id)value forKey:(NSString *)key.
So, you want:
-(void)setTimestamp:(NSTimeInterval)newTimestamp
{
//run custom code....
//and now how to pass the value to the actual property?
[self willChangeValueForKey:#"timestamp"];
[self setPrimitiveTimestamp:newTimestamp];
[self didChangeValueForKey:#"timestamp"];
}

Related

WatchKit how to check current state of WKInterfaceSwitch

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.

Initialising a category in objective-c

I'm writing a category for UITextField to include validation. I wish to change the text field's visual according to validation state (such as having an approved icon as its right view). For this, I keep a validation state property and wish to update the visual on its setter.
Here's what I have (UITextField+Validation.h)
#interface UITextField (Validation)
// Validator registration
- (void)addValidator:(id<HyValidator>)validator;
// Validation
- (void)validate;
#end
UITextField+Validation.m
#interface UITextField (Validation_Private)
#property (nonatomic, strong) NSMutableArray * validators;
#property (nonatomic) HyValidationState validationState;
#end
#implementation UITextField (Validation_Private)
- (NSMutableArray*)validators
{
if (self.validators == nil) {
self.validators = [[NSMutableArray alloc] init];
}
return self.validators;
}
- (void)setValidators:(NSMutableArray *)validators
{
self.validators = validators;
}
- (HyValidationState)validationState
{
}
- (void)setValidationState:(HyValidationState)validationState
{
}
- (void)addValidator:(id<HyValidator>)validator
{
[[self validators] addObject:validator];
}
- (void)validate
{
}
#end
The question is: how do I initialise validators and validationState?
Don't use categories for this. Subclass instead. Or, better yet, use the UITextField's delegate to do the validation, as intended.
Using categories to extend the behavior of existing system classes is generally considered to be bad design.
By using delegation, you can decouple input validation from a specific input class and, thus, your validation can be easily re-used across other input mechanisms.
You want to add a storage to your class UITextField (simple ivar to hold the data). Since you don't have the code you can't extend the class. However in objective C you can achieve this using associated reference. ObjC Runtime comes handy helping you to attach a storage to your class and make you interact with the storage as if it was built in within the class.
An example of how to achieve this is found in Ole Begemann blog here http://oleb.net/blog/2011/05/faking-ivars-in-objc-categories-with-associative-references/

Passing multiple tags with UIButton

OK I have a specific situation. I am using a custom class to create some buttons and I can set their tag property with unique numbers like:
button.tag =[NSNumber numberWithInt:[10]];
This is very useful in another part of my program because I can access this unique tag like:
UIButton *clicked= (UIButton *) sender;
ButtonTag = [NSString stringWithFormat:#"%d", clicked.tag];
Now I want to pass one more unique property just like this. I am making this up but this is how I envision it
button.tagCREATED_BY_ME =[NSNumber numberWithInt:[9000]];
The question might be poorly worded but I don't know any better so I called it "tag".(correct wording might be element/property etc) How do I create a similar property to function just like .tag?
Thanks a lot!
arda
What do you want to achieve?
There is the possibility of adding an associative references. The good part about this, is that you don't need to sub-class it. So, start by creating a Category for the UIButton:
#interface UIButton (ExtraTag)
#property (nonatomic, retain) id extraTag;
#end
And the .m:
static char const * const ExtraTagKey = "ExtraTag";
#implementation UIButton (ExtraTag)
#dynamic extraTag;
- (id)extraTag {
return objc_getAssociatedObject(self, ExtraTagKey);
}
- (void)setExtraTag:(id)newExtraTag {
objc_setAssociatedObject(self, ExtraTagKey, newExtraTag, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
You can check the article I used.
CALayer allows Key-Value coding actually.
You can literally just do this (on any UI object):
[button.layer setValue:#(9000) forKey:#"tagCREATED_BY_ME"];
and to read it just use
[button.layer valueForKey:#"tagCREATED_BY_ME"]
Obligatory, the above is all you need to get this up and going, you're good to go.
For others, more advanced /or/ specific stuff follows:
If you need these keys to have a default value when nothing has yet been assigned to them... You can set these custom "tags" (eh) to have default return values if you name them according to a pattern. For example I start all of my layer keys name's with "customKey_". So the above would have been #"customKey_tagCREATED_BY_ME", then you can have your .m file return the default key values for any standard key like masksToBounds but then return a very specific value for your keys (aka keys that start with "customKey_") with the following method:
+(id)defaultValueForKey:(NSString *)key {
if ([key hasPrefix:#"customKey_"]) {
return #(0);
}
return [CALayer defaultValueForKey:key];
}
The reason you have to have a naming pattern (like always having the prefix "customKey_") is so you don't interfere with a CALayer's natural properties like .size and .backgroundColor, etc. Your default value you want returned will only be returned on properties (key) starting with "customKey_" or whatever naming pattern you use.
In your subclassed/custom button, you can add a string property or even an integer property whichever you feel good.
#interface CustomButton: ....
...
#property(strong) NSString *createdBy;
#end
Then you can access those as aButton.createdBy
You can also use Associated references instead of tags manipulation
#import <objc/runtime.h>
static char kThumbnailButtonAssociatedPhotoKey;
// ...
- (void)setAssociatedPhoto:(Photo *)associatedPhoto
forThumbnailButton:(UIButton *)thumbnailButton
{
objc_setAssociatedObject(thumbnailButton,
&kThumbnailButtonAssociatedPhotoKey,
associatedPhoto,
OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (Photo *)associatedPhotoForThumbnailButton:(UIButton *)thumbnailButton
{
return objc_getAssociatedObject(thumbnailButton,
&kThumbnailButtonAssociatedPhotoKey);
}
Now we can easily set/get the associated photo for a button:
- (void)configureThumbnailButtonForPhoto:(Photo *)photo
{
// ...
[self setAssociatedPhoto:photo
forThumbnailButton:thumbnailButton];
// ...
}
- (void)thumbnailButtonTapped
{
Photo *photo = [self associatedPhotoForThumbnailButton:thumbnailButton];
// ...
}
Blog post about tags and associated references
You can subclass UIButton.
In your subclass, add a new property:
#property (strong, nonatomic) NSNumber *tagCREATED_BY_ME;
You could look into KVC.
Or if you wanted to stick to the KISS principle - subclass UIButton and create a property.

Adding a property to all of my UIControls

Im trying to make it so that every single UIControl in my application (UIButton, UISlider, etc) all have special extra properties that I add to them.
I tried to accomplish this by creating a UIControl Category and importing it where needed but I have issues.
Here is my code.
My setSpecialproperty method gets called but it seems to be getting called in an infinite loop until the app crashes.
Can you tell me what Im doing wrong or suggest a smarter way to add a property to all of my UIControls?
#interface UIControl (MyControl)
{
}
#property(nonatomic,strong) MySpecialProperty *specialproperty;
-(void)setSpecialproperty:(MySpecialProperty*)param;
#end
////////
#import "UIControl+MyControl.h"
#implementation UIControl (MyControl)
-(void)setSpecialproperty:(MySpecialProperty*)param
{
self.specialproperty=param;
}
///////////////
#import "UIControl+MyControl.h"
#implementation ViewController
UIButton *abutton=[UIButton buttonWithType:UIButtonTypeCustom];
MySpecialProperty *prop=[MySpecialProperty alloc]init];
[abutton setSpecialproperty:prop];
While you can't add an iVar to UIControl via a category, you can add Associated Objects, which can be used to perform much the same function.
So, create a category on UIControl like this:
static char kControlNameKey;
- (void) setControlName: (NSString *) name
{
objc_setAssociatedObject(self, &kControlNameKey, name, OBJC_ASSOCIATION_COPY);
}
- (NSString *) controlName
{
return (NSString *)objc_getAssociatedObject(array, &kControlNameKey);
}
There's more to it than that, I guess you'll need to check if an association exists before setting a new one, otherwise it will leak, but this should give you a start.
See the Apple Docs for more details
self.specialproperty=param is exactly the same as calling [self setSpecialproperty] (see here for some totally non biased coverage of Obj-C dot notation), which makes your current usage infinitely recursive.
What you actually want to do is:
-(void)setSpecialproperty:(MySpecialProperty*)param
{
_specialproperty = param;
}
Where _specialproperty is the implicitly created ivar for your property.
I'm assuming there's some reason why you've implemented your setSpecialproperty setter? Why not just use the one that is implicitly created for you?
the problem is that you can not add a property to a category, you can add behavior (methods) but not properties or attributes, this can only be done to extensions, and you can not create extensions of the SDK classes
use your method as
change your method name to
-(void)setSpecialproperty:(MySpecialProperty *)specialproperty
-(void)setSpecialproperty:(MySpecialProperty*)specialproperty
{
if(_specialproperty!=specialproperty)
_specialproperty = specialproperty;
}
and synthesize your specialProperty as
#synthesize specialproperty=_specialproperty;

How can I change the key of an existing object in a NSMutableDictionary?

How can I change the key of an existing object in a NSMutableDictionary?
You will have to remove the object from the original key and add it back with the new key. You could turn this into a category on NSMutableDictionary.
The category would look something like this:
#interface NSMutableDictionary (RenameKey)
- (void)renameKey:(NSString *)original to:(NSString *)new;
#end
#implementation NSMutableDictionary (RenameKey)
- (void)renameKey:(id)original to:(id)new; {
id value = [self objectForKey:original];
[self removeObjectForKey:original];
[self setObject:value forKey:new];}
#end
You should probably add a pre/postfix to the method name to avoid future name conflicts in the runtime. The above is also not thread-safe, it takes three steps to perform the rename.

Resources