Get instance name of sender in objective-C - objective-c

I want to get the name of a sender in Objective-C. For example, below I have a method which is called by an instance of UISlider in Interface Builder, I want to know what the instance name of it is so I can later add conditional blocks to the method for which instance of UISlider called the method.
e.g.
-(IBAction)sliderChanged:(UISlider *)sender {
//labAt1TimeRequired.text = [NSString stringWithFormat:#"%.1f", [sender value]];
NSLog(#"%#",sender);
Outputs:2010-10-15 22:46:02.257 EPC[3225:207] <UISlider: 0x495b140; frame = (205 3; 118 23); opaque = NO; autoresize = RM+BM; layer = <CALayer: 0x492e340>>
I want to be able to say
if(sender==myInstanceName) {
//do this
}

You could use
.tag member
to read an write and integer ID for the slider like this:
-(IBAction)sliderChanged:(UISlider
*)sender {
switch (sender.tag) {
case 0:
//SLider 0
break;
case 1:
//SLider 1
break;
default:
break;
}
}
Tag ID's can also be set for components in IB.
If your set on a string then you would need to subclass a UISlider.

You would use the tag property of UIView for identifying the sender.
-(IBAction)sliderChanged:(UISlider *)sender {
//labAt1TimeRequired.text = [NSString stringWithFormat:#"%.1f", [sender value]];
if (sender.tag == 1)
{
// do whatever
}
else
{
// do something else
}
}

Related

Bindings does not update NSTextField

I use bindings to NSObjectController within XIB. When I set new content object of NSObjectController the only textfield value which doesn't change is the one that has first responder. Model changes without an issue.
If I don't use custom getter/setter the textfield that has firstResponder (isBeingEdited) changes without an issue.
What's wrong with my KVC, KVO?
My custom getter/setter is below pic.
PS: I don't want to make window a first responder before I change content object to make it work.
static const CGFloat MMsInOneInch = 25.4;
static const CGFloat inchesInOneMM = 0.0393700787402;
- (void)setPaperWidth:(CGFloat)paperWidth
{
[self willChange];
CGFloat newWidth = paperWidth * [self conversionKoeficientToDefaultUnitType];
if (!_isChangingPaperSize) {
if (self.paperSize == PPPaperSizeA4 && fabs(newWidth - widthSizeOfA4) > 0.001) {
[self setPaperSize:PPPaperSizeCustom];
}
if (self.paperSize == PPPaperSizeUSLetter && fabs(newWidth - widthSizeOfUSLetter) > 0.001 ) {
[self setPaperSize:PPPaperSizeCustom];
}
}
[self willChangeValueForKey:#"paperWidth"];
_paperWidth = newWidth;
[self didChangeValueForKey:#"paperWidth"];
[self didChange];
}
- (CGFloat)conversionKoeficientToDefaultUnitType
{
if ([self defaultUnitType] == [self unitType]) {
return 1;
}
if ([self defaultUnitType] == PPPrintSettingsUnitTypeMM) {
return MMsInOneInch;
}
if ([self defaultUnitType] == PPPrintSettingsUnitTypeInch) {
return inchesInOneMM;
}
return 1;
}
- (CGFloat)paperWidth
{
return _paperWidth / [self conversionKoeficientToDefaultUnitType];
}
I forgot that I use NSNumberFormatter with min/max value which where blocking NSTextField to update.

Objective C - Change System Font with Category

I want to change the default font for all UITextViews. It seems that the easiest way to do this is via custom category. I found this solution: Change the default systemFont used by controls and tried to implement it.
But my UITextViews are added programmatically so the awakeFromNib function is not called. I tried to move it to initWithFrame like this:
-(id)initWithFrame:(CGRect)frame
{
id result = [super initWithFrame:frame];
if (result) {
float size = [self.font pointSize];
NSString *stringfontstyle=self.font.fontName;
if([stringfontstyle rangeOfString:#"Bold"].location != NSNotFound) {
self.font = [UIFont fontWithName:#"Avenir-Black" size:size];
}
else if ([stringfontstyle rangeOfString:#"Italic"].location != NSNotFound) {
self.font = [UIFont fontWithName:#"Avenir-Oblique" size:size];
}
else if ([stringfontstyle rangeOfString:#"Medium"].location != NSNotFound) {
self.font = [UIFont fontWithName:#"Avenir-Medium" size:size];
}
else {
self.font = [UIFont fontWithName:#"Avenir-Roman" size:size];
}
}
return result;
}
Weird is that if my category contains initWithFrame function, the UITextView disappears. What is it that I'm missing?
Note: I'm using autoLayout so the initWithFrame is called with CGRectZero, but I suppose that isn't the problem.
EDIT:
The problem is that the font is null when the UITextView is initiated. So what method would be appropriate to place the code into?
when category contains a method, it overrides the class's method... and thats not good. subclassing would work.. method swizzling might be a way but...
why don't you just subclass UITextView - then you can keep your initWithFrame thingy or maybe override font
- (UIFont*)font {
if(!myFont) {
_myFont = xy;
}
id superFont = super.font;
if(![superFont.name isEqualTo:_myFont.name]) {
super.font = [myFont fontWithSize:superFont.pointSize];
}
return _myFont;
}
or setFont:
- (void)setFont:(UIFont*)newFont {
if(!myFont) {
_myFont = xy;
}
id thisFont = [_myFont fontWithSize:newFont.pointSize];
super.font = thisFont;

Dynamic Accessibility Label for CALayer

How do I make a CALayer accessible? Specifically, I want the layer to be able to change its label on the fly, since it can change at any time. The official documentation's sample code does not really allow for this.
The following assumes that you have a superview whose layers are all of class AccessableLayer, but if you have a more complex layout this scheme can be modified to handle that.
In order to make a CALayer accessible, you need a parent view that implements the UIAccessibilityContainer methods. Here is one suggested way to do this.
First, have each layer own its UIAccessibilityElement
#interface AccessableLayer : CALayer
#property (nonatomic) UIAccessibilityElement *accessibilityElement;
#end
now in its implementation, you modify the element whenever it changes:
#implementation AccessableLayer
... self.accessibilityElement.accessibilityLabel = text;
#end
The AccessableLayer never creates the UIAccessibilityElement, because the constructor requires a UIAccessibilityContainer. So have the super view create and assign it:
#pragma mark - accessibility
// The container itself is not accessible, so return NO
- (BOOL)isAccessibilityElement
{
return NO;
}
// The following methods are implementations of UIAccessibilityContainer protocol methods.
- (NSInteger)accessibilityElementCount
{
return [self.layer.sublayers count];
}
- (id)accessibilityElementAtIndex:(NSInteger)index
{
AccessableLayer *panel = [self.layer.sublayers objectAtIndex:index];
UIAccessibilityElement *element = panel.accessibilityElement;
if (element == nil) {
element = [[UIAccessibilityElement alloc] initWithAccessibilityContainer:self];
element.accessibilityFrame = [self convertRect:panel.frame toView:[UIApplication sharedApplication].keyWindow];
element.accessibilityTraits = UIAccessibilityTraitButton;
element.accessibilityHint = #"some hint";
element.accessibilityLabel = #"some text";
panel.accessibilityElement = element;
}
return element;
}
- (NSInteger)indexOfAccessibilityElement:(id)element
{
int numElements = [self accessibilityElementCount];
for (int i = 0; i < numElements; i++) {
if (element == [self accessibilityElementAtIndex:i]) {
return i;
}
}
return NSNotFound;
}

Cocoa binding: NSTextField with empty string for zero value

I have NSTextField with placeholder. And it's binded to some integer property. So I want to display empty text in the field (with placeholder shown) when binded integer is zero.
Is it possible to do it?
(Update)
I discovered that this can be done through NSNumberFormatter - it has —(void) setZeroSymbol: (NSString*) string method. Not tried yet this in practice...
You could use an NSValueTransformer.
(Just in case)Create a new class, subclass from NSValueTransformer. In the implementation, add something like this:
+(Class)transformedValueClass {
return [NSString class];
}
-(id)transformedValue:(id)value {
if (value == nil) {
return nil;
} else {
if ([value integerValue] == 0) {
return #"";
} else {
return [NSString stringWithFormat:#"%d", [value stringValue]];
}
}
}
In Interface Builder, select your field, go to the bindings tab, and in the Value Transformer drop down, either select or type in your class name you made. This should prevent you from having to worry about modifying it elsewhere. I'm not 100% positive about it showing the placeholder (I don't have a Mac available right now).
EDIT:
I can confirm that this does indeed work. Here is a link to a github project I made to show how to use it: https://github.com/macandyp/ZeroTransformer
check the integer value before binding, if you are binding at runtime. Try
int i;
if (i == 0)
{
txt.placeholder = #"text";
}
else
{
[txt setStringValue:[NSString stringWithFormat:#"%d",i]];
}
You can not do conditional binding.
You need to create another property that will hold the value based on condition and use that property and bind to textfield.
I am using bindedString and bindedInteger. bindedString is bound to text field.
Whenever some action is performed it is updated.
- (id)init{
self = [super init];
if (self) {
self.bindedString=#"place holder string";
}
return self;
}
- (IBAction)button:(id)sender {
if (self.bindedInteger==0) {
self.bindedString=#"place holder string";
}
else{
self.bindedString=[NSString stringWithFormat:#"%ld",self.bindedInteger];
}
}

using int selector for ccmenuitem

My CCMenuItem has two methods in selector
menuItem = [[CCMenuItemSprite
itemFromNormalSprite:[CCSprite spriteWithSpriteFrameName:#"menuItem.png"]
selectedSprite:[CCSprite spriteWithSpriteFrameName:#"menuItem.png"]
target:self
selector:#selector(methodName:anotherParam:)] retain];
I'd like to send the an int from CCMenuItem so that I'd could switch the cases based on the int of the CCMenuItem
something like this:
- (void)methodName:(id *)sender anotherParam:(int *)intNumber {
CCMenuItemSprite *menuItem = (CCMenuItemSprite *)sender;
switch (anotherParam) {
case 1:
//My case
break;
}
How can I send an int from CCMenuItem in order to switch my cases?
You can use the userData property of a CCNode for this, or you could set an associated object.
Eample:
CCNode *myNode = [CCNode node];
myNode.userData = (void *) intVal;
// in callback
CCNode *myNode = (CCNode *) sender;
int anotherParam = (int) myNode.userData;
To set an associated object, you would do something like this:
objc_setAssociatedObject(myObject, "anotherParam", (id) myInt, OBJC_ASSOCIATION_ASSIGN);
// in callback
int anotherParam = (int) objc_getAssociatedObject(myObject, "anotherParam");
Note that key is of type void * so you could put any value there (NSObject, NSString, C-string, int value, etc.)
Just tag the CCMenuItem:
menuItem.tag = 1;
Then get it from the sender parameter:
- (void)methodName:(id *)sender {
switch ([sender tag]) {
//cases depending on tag
}
}