Adding a property to all of my UIControls - ios

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;

Related

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/

iOS7 compiler won't recognize method call with message

So this one has me stumped - probably something simple, but I'm clueless.
I'm defining a custom class, containing one method that receives one message (an integer). When calling that method, the compiler refuses to recognize the message I'm trying to send along with the call. ("No known class method for selector 'sendMessage:'. Removing the message from both the call and the definition - i.e. removing the :(int)mode from the definition, and the :1 from the call - allows it to compile fine (but then of course I lose the functionality).
I've tried defining it as an instance method, and as a class method - neither one works.
Many thanks in advance for your collective wisdom!
custom class "Communications.h":
#interface Communications : NSString
+(NSString*)sendMessage:(int)mode;
#end
Communications.m:
#import "Communications.h"
#interface Communications ()
#end
#implementation Communications
+(NSString*)sendMessage:(int)mode {
// Do something important
}
ViewController.h:
#import "Communications.h"
- (void) tapPanic:(UITapGestureRecognizer*)sender;
ViewController.m:
- (void) tapPanic:(UITapGestureRecognizer *)sender {
[Animations animatePanic:self.view type:0];
panicactive = 1;
NSString* tmpResponse = [Communications sendMessage:1];
UILabel* tmpServerResponsePanic = [self.view viewWithTag:10002];
tmpServerResponsePanic.text = tmpResponse;
[[self serverResponsePanic] setNeedsDisplay];
}
So, chalk it up to weirdness with Xcode... copy / pasting the contents of Communications .h and .m into new files, with a new class definition (Comms), did the trick. I think the compiler got confused and was remembering an old definition of the method.

Changing label color of a label in a different view (delegates?) [Spritebuilder]

I have two scenes that I made in SpriteBuilder, one is Shop and the other is UpgradesNew. Shop is a CCNode layer and UpgradesNew is a CCNode layer. I have two CCScrollViews in the MainScene that load Shop and UpgradesNew respectively.
When one button in Shop is tapped, the label in UpgradesNew should change colors. I have been trying to implement this using delegates but it's not working.
Here's what I did...
In shop.h I set the protocol:
#protocol changeColorProtocol <NSObject>
#required
-(void)changeColor;
#end
Then I set the id
#property (nonatomic, retain) id <changeColorProtocol> delegate;
Here is the button that when clicked, should use changeColor. This is in shop.m
-(void) buyDiggerShibe {
[self.delegate changeColor];
[self didLoadFromCCB];
}
Now in UpgradesNew.h I made it adopt the protocol like this
#interface UpgradesNew : CCNode <changeColorProtocol>
And in UpgradesNew.m
I set delegate to self in ViewDidLoad.
Shop *shop = [[Shop alloc]init];
shop.delegate = self;
.
-(void)changeColor {
if (hasDigger == YES) {
shovelRequires.color = [CCColor greenColor];
NSLog(#"HEY HEY HEY");
}
}
I probably have parts of the delegate placed in the wrong area because I was trying to switch them around when it wasn't working, I'm not sure where they are supposed to go. I've watched multiple delegate tutorials and it just seems overly complicated, at least with what I am trying to do.
Any ideas?
EDIT:
Tried this.
I created a property in UpgradesNew
#property (strong, nonatomic) Shop *shop;
Then I synthesized it in the implementation and allocated it like this in didLoadFromCCB, instead of creating a new object:
self.shop = [[Shop alloc]init];
shop.delegate = self;
EDIT: This is how I am creating objects.
Drag a label into a layer. Identify it then define it in header as CCLabelTTF *label; That's it, thats all I do to create any object on the layer.
To create a layer like Shop or UpgradesNew, I hit New -> File -> Layer. That creates a new CCNode. Then I set the class of the CCNode, as shown in the picture the CCNode that is highlighted has a class of MainScene. If I want to establish a #property to that CCNode I just type the name in the box right below custom class and set it as doc root var, and then put it in the header as CCNode *MainScene. I don't do anything other than that.
I don't know anything about SpriteBuilder, so it's a bit hard to address your question. You might want to add SpriteBuilder to the title of your post so people who use that framework are likely to read it.
You need to explain how the 2 "CCNode layer"s are created, and how you link them together. In order for one object to have another object as a delegate, the delegate property has to be set somewhere. Where is that setup being done? Have you set a breakpoint at the line
[self.delegate changeColor];
To make sure that self.delegate is not nil?
Have you set a breakpoint in your changeColor method, or added a log statement, to see if it's being called? My guess is that self.delegate is nil, so the messages is being dropped on the floor (it's legal to send messages to nil in Objective-C. It just doesn't do anything.)

How can I access the object on which a category method was called?

I'm using #implementation to add a new function to UIView.
#implementation UIView (test)
- (void)newFunction {
}
#end
Now, in the newFunction I want to "grab" the object (UIView) that was used when calling the function.
For example when I call newFunction within viewDidLoad
- (void)viewDidLoad {
[myView newFunction];
}
I want the newFunction to know what object was used to make the call (in this case, myView).
A simple solution would be to pass it along when making the call ([myView newFunction:myView]), but that is not what I am looking for.
I looked at Apple's documentation on the subject, but didn't really find an answer to my question.
#import "UIView+UIView_Category.h"
#implementation UIView (UIView_Category)
- (void)newFunction
{
NSLog(#"Object = %#",self);
}
#end
What you describe is called a category (not #implementation). It is an extension to the UIView class (in this case).
Generalcally:
#implementation __CLASS_TO_EXTEND__ (__CATEGORY_NAME__)
The category, as it is an extension, is the instance that you call the method on. So, you use self as you usually would to access the current instance.

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.

Resources