Let's assume we have a custom lib with a class that inherits from UILabel:
//MyLibCustomLabel.h
#interface MyLibCustomLabel : UILabel
MyLibCustomLabel is linked to a UILabel in a .xib file, and text is filled in .xib.
This custom lib is integrated in a project that has a Category on UILabel class, which has a method to modify UIlabel's text
//UILabel+UILabelAdditions.h
#interface UILabel (UILabelAdditions)
//UILabel+UILabelAdditions.m
#implementation UILabel (UILabelAdditions)
- (void)awakeFromNib {
[super awakeFromNib];
[self prependText];
}
-(void)prependText {
NSString *newText = [NSString stringWithFormat:#"blabla + %#", self.text];
self.text = newText;
}
In the end, there is a modification non-desired in MyLibCustomLabel.
In a situation of both custom class and category are used in a Class, is there a way to protect MyLibCustomLabel from any Category on UILabel ?
So that MyLibCustomLabel can not be altered in an undesired way, and so that there is no modification to do in the project that integrates it.
Thanks for your help !
There is nothing that can be done to "protect" a class from a possible category being defined.
But please note that the example UILabel category you show isn't valid. A category must never attempt to override an existing method nor attempt to call a super method. Such behavior isn't defined and isn't guaranteed to work as hoped.
In other words, the category's awakeFromNib method is a bad idea and shouldn't be done. Such a thing should only be attempted in a base class, not a category.
Related
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/
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.)
I don't know if I have chosen right title for my question.
I have developed an app and now I want to process text before assigning text to UILabel or UIView text property.
instead of
myLabel.text = story.text
do this:
myLabel.text = [story.text substituteCharactersOfText];
substituteCharactersOfText is a method of Category I have added to NSString class
so if I have a lot of label or another views, it will be difficult or errorProne to manually call this category method. (maybe I forgot one for anotherLabel.text)
so is there anyway to call this method automatically before assigning text to UILabel.text?
I think maybe there is way in objective-c I don't aware of (maybe an special use of delegate)!!
Subclass UILabel and override setText:
#implementation HALabel
- (void)setText:(NSString *)text {
[super setText:[text substituteCharactersOfText]];
}
#end
What I am trying to implement is a UITextField that sees words as characters. Specifically, im trying to see the mathemathical expression sin( as one character for example. I thought to solve this problem by implementing my own UITextInputDelegate. However, the four required functions from this protocol never get called when I implement or adopt this protocol. I tried implementing it in the following ways:
By subclassing a UITextField.
#interface BIDUItextFieldDelegate : UITextField < UITextInputDelegate >
By subclassing a NSObject.
#interface BIDTextfieldInputDelegate : NSObject < UITextInputDelegate >
The corresponding .m file contains:
#implementation BIDTextfieldInputDelegate
- (void)selectionWillChange:(id <UITextInput>)textInput
{
NSLog(#"sWill");
}
- (void)selectionDidChange:(id <UITextInput>)textInput
{
NSLog(#"sDid");
}
- (void)textWillChange:(id <UITextInput>)textInput
{
NSLog(#"tWill");
}
- (void)textDidChange:(id <UITextInput>)textInput
{
NSLog(#"tDid");
}
For example for the second approach (via subclassing NSObject), I do the following in the (id) init method in an additional custom UITextfield class which is displayed within the app:
//textInputDel is an instance of the custom NSObject class that adopts the above UITextInputDelegate protocol
self.textInputDel = [[BIDTextfieldInputDelegate alloc] init];
self.inputDelegate = self.textFieldDel;
Does anyone know what I am doing wrong, or has a better solution?
UITextInputDelegate is not a protocol that you implement; rather, the system creates an object conforming to UITextInputDelegate, and assigns it as the .inputDelegate of a First Responder that conforms to UITextInput.
The methods of UITextInputDelegate are methods for your UITextInput-conforming responder to call on your .inputDelegate to inform it that you have changed your text or selection via means other than keyboard input.
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;