being still kinda new to obj-c, I was playing around with the makeObjectsPerformSelector method.
I have two arrays containing UISteppers and UITextfields respectively:
_stepper = [NSArray arrayWithObjects:
_stepMa, _stepMafree, _stepDe, _stepDefree, _stepFl, _stepFlfree,
_stepEn, _stepEnfree, _stepEnBl, _stepEnBlfree, _stepVo, _stepVofree,
_stepVe, _stepVefree, _stepIn, _stepInfree, _stepOt, _stepOtfree,
_stepIn170, _stepIn170free, _stepZy, _stepZyfree,
nil];
_fields = [NSArray arrayWithObjects:
_MaFeld, _MaFeldfree, _DeFeld, _DeFeldfree, _FlFeld, _FlFeldfree,
_EnFeld, _EnFeldfree, _EnBlFeld, _EnBlFeldfree, _VoFeld, _VoFeldfree,
_VeFeld, _VeFeldfree, _InFeld, _InFeldfree, _OtFeld, _OtFeldfree,
_InFeld170, _InFeld170free, _ZyFeld, _ZyFeldfree,
nil];
In some method I want to reset them:
- (void) resetFields
{
[_stepper enumerateObjectsUsingBlock: ^(UIStepper* stepper, NSUInteger idx, BOOL *stop)
{
stepper.value = 0;
}];
[_fields enumerateObjectsUsingBlock: ^(UITextField* field, NSUInteger idx, BOOL *stop)
{
field.text = #"0";
}];
}
which works as expected.
trying to shorten that code a bit I tried my luck with the mentioned method:
- (void) resetFields
{
[_stepper makeObjectsPerformSelector:#selector(value) withObject:0];
[_fields makeObjectsPerformSelector:#selector(text) withObject:#"0"];
}
which had no effect... I guess there is something I did not consider, but what?
Thanks!
To bypass the problem of int to be a C type and not an object, use KVC (Key-Value Coding). If you call setValue:forKey: on a NSArray object, the method setValue:forKey: is call on each of the objects of the array. And with a bonus, KVC is managing all the primitive stuff.
[_stepper setValue:#0 forKey:#"value"];
[_fields setValue:#"0" forKey:#"text"];
performSelector calls (all kinds of them) can only take Objective-C objects (ones that can be represented by id type). C types like double, BOOL, int etc. will not work, so you can not set value this way unless you change its type to NSNumber*.
For setting text property, you need to use setText: selector; text is the getter. Since this property type is Objective-C class NSString, performSelector will work.
Related
I have my own class with several properties and I have them in NSArray. I need to use them for method which takes NSArray of strings. So I am asking what is best aproach to get array with strings from my array which has custom classes. I can create second array and use it but I think there could be better way. I need to have it for different custom classes (from one, I want to use for example name property to new NSArray, in second title property and so).
I hope I explained well but I tried it once more on example:
NSArray *arrayWitCustomClasses = ... fill with custom classes;
// setValues method takes NSArray with NSStrings
// when arrayWithCustomClasses used it returns copyWithZone: error on custom class
[someObject setValues:[arrayWithCustomClasses toArrayWithStrings]];
As long as your object exposes the required values as NSString properties you can use the valueForKey method of NSArray.
For example
NSArray *arrayOfTitles=[arrayWithCustomClasses valueForKey:#"title"];
NSArray *arrayOfNames=[arrayWithCustomClasses valueForKey:#"name"];
Or
[someObject setValues:[arrayWithCustomClasses valueForKey:#"title"]];
and so on
NSMutableArray *strings = [NSMutableArray array];
for (NSObject *item in arrayWithCustomClasses) {
/* You can use a different property as well. */
[strings addObject:item.description];
}
[someObject setValues:strings.copy];
Like #Tim says, but you could shorten it by just using:
[someObject setValues:[arrayWithCustomClasses valueForKey:#"description"]];
Same result. One line of code.
Then implement the description method of your custom classes to return whatever properties and formatting you want.
I am doing a tuturial on Lynda.com for objective-c, and ran accross this example code. This is a part of the ViewController.m file. The idea behind the exercise was to create a picker object with custom elements in it.
The following code works just fine and gives me a picker with "happy" and "sad" as the options:
#implementation ViewController
-(NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView{
return 1;
}
-(NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component{
return [[self moods] count];
}
-(NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component{
return self.moods[row];
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.moods = #[#"happy",#"sad"];
}
However, I prefer square brackets to dot syntax and, as you can see I experimented in a few different places with it. Thereturn [[self moods] count was written as return [self.moods count] in the tutorial, but I wanted to use square brackets to verify that it still worked and I understood what was going on, so I changed it and it worked just fine. HOWEVER, I have been trying to do the same thing with the self.moods = #[#"happy",#"sad"]; because I don't like how it looks. I tried:
[[self moods] initWithObjects: #"happy",#"sad", nil];
But I just got a blank picker and a warning "expression result unused". I tried putting _moods = before that expression, and still got a blank picker. What is wrong here?
The reason that [[self moods] initWithObjects: #"happy",#"sad", nil]; is not doing what you expect is due to a misunderstanding in what is happening with regards to dot syntax and how it relates to message sending using square brackets.
Dot syntax is the "syntactic sugar" and recommended way of accessing properties of classes, such as the mood property from your question. Dot syntax is simply a shorthand for accessors (setters / getters) in Objective-C. A quick example might help clear this up.
When dot syntax finds itself on the right hand side of an assignment operator OR as the receiver of a message, the getter method is invoked.
// These two are equivalent
NSArray *allMoods = self.moods
NSArray *allMoods = [self moods]
// These two are equivalent
NSUInteger count = [self.moods count];
NSUInteger count = [[self moods] count];
When dot syntax finds itself on the left hand side of an assignment operator, the setter method is invoked.
// These two are equivalent
self.moods = #[#"happy", #"sad"];
[self setMoods:#[#"happy", #"sad"];
Using dot syntax is not only a nice shorthand, it makes your intentions clearer and newcomers to your code immediately aware that moods is a property of your class.
Also, the reason that [[self moods] initWithObjects: #"happy",#"sad", nil]; is not valid is because -initWithObjects: is an initializer of NSArray that should be called immediately following +alloc. In the piece of code above, [self moods] is returning an NSArray that already exists or lazily instantiating one. For completeness, -initWithObjects should be used as follows:
NSArray *myArray = [[NSArray alloc] initWithObjects:#"happy", #"sad", nil];
I assume you declared #property (strong, nonatomic) NSArray *moods; in the interface since self.moods works.
Setter and getter methods setMoods and getMoods are created automatically.
Here's how the dot syntax boils down to
// These are equivalent
self.moods = #[#"happy",#"sad"];
[self setMoods:#[#"happy",#"sad"]]; // Literal declaration
[self setMoods:[[NSArray alloc] initWithObjects:#"happy",#"sad",nil]]; // Full declaration
This works because you were using the "literal" way of declaring an NSArray* which includes both "allocation" and "initialization".
- (instancetype)initWithObjects: is an instance method which should be called on an instance variable already allocated with alloc. You tried to initialize a variable which has never been allocated in memory.
An slightly cleaner alternative would be:
[self setMoods:[NSArray arrayWithObjects:#"happy",#"sad",nil]];
arrayWithObjects: include both allocation and initialization.
the [self moods] way of referencing it can only be used on the right hand side of an expression, it's calling the getter for the property. self.moods = ... is actually syntactic sugar for [self setMoods:...]
so try [self setMoods:#[#"happy",#"sad"]]
You'll want to read up on the #property declaration and how it "synthesizes" getter and setter methods. What you want to do is "set" the moods property:
[self setMoods: #[#"happy",#"sad"]];
Is this possible, I searched the net and found no answer to this. My senior also said that this is not possible.
I'm trying to add this as a category, so I want to extract 4 types of objects out of it, all of them uses the same code, it's just the classes that differ so I thought of this:
- (NSDictionary *) getObjectsOfClass:(Class)class
{
NSMutableDictionary *objDict = [NSMutableDictionary dictionary];
[self.subviews enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
if ([obj isKindOfClass:class]) {
/*
Is there a way to do this?
class *label = (class *)obj;
*/
}
}];
return objDict;
}
So is there a way to make this work? It's ugly to see 4 functions with almost the same codes, you agree right?
What about passing class name as string & creating object out of it. May be like this
-(NSArray *)arrayOfObjectsForClass:(NSString *)className{
NSMutableArray *objectArray = [[NSMutableArray alloc]init];
CGFloat yAxis = 10;
for(int i =0; i<5; i++){
id object = [[NSClassFromString(className) alloc]initWithFrame:CGRectMake(0, yAxis, 100, 50)];
[object setTitle:[NSString stringWithFormat:#"Button %d", i+1]];
[objectArray addObject:object];
yAxis+= 60;
}
return objectArray;
}
Because of you said "But I'm adding it to uiview, to get the textfields, labels, pickerviews etc, so that I can just call [self.view getObjectsOfClass:[UILabel class]"
For this code
[self.view getObjectsOfClass:[UILabel class]];
it will return all its UILabel immediate children of it.
- (NSMutableArray *) getObjectsOfClass:(Class)class
{
NSMutableArray *objArray = [NSMutableArray array];
[self.subviews enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
// All visible things are inherited from UIView. Tag property is belongs to UIView
// UILabel are inherited from UIView
if ([self isKindOfClass:[UIView class]] && [obj isKindOfClass:class]) {
UIView *aView = (UIView*)obj;
if (aView.tag == 100) {
//This is the view with tag 100
}
[objArray addObject:obj];
}
}];
return objArray;
}
If all of them are derived from a common base class, you can cast them into that common base class. If few functions are not available then create a category of that common base class and add those common functions into it. This will allow you to have single code block rather than 4 different one.
You can instantiate your class argument like this:
id newInstance = [class new];
What you cannot syntactically do is using class * as a way to tell the compiler which type your local variable is. But this is also not required, thanks to Objective C dynamics typing capabilities.
In other words, there is not reason to cast to class (and you cannot do that; class is only known at runtime, casting has effect at compile time).
EDIT:
If you know a base class common to all of your classes, e.g. UIView, then you could do:
UIView* newInstance = obj;
then access its properties, e.g.:
if (newInstance.tag ==…)
Or you could use message sending instead of properties and do:
if ([obj tag] == ...)
So i got a question how to do the following:
I got an NSArray with Objects.
NSArray *array = [[NSArray alloc] initWithObjects:object1, object2, nil];
object1 and object2 are Subclasses of UIButton. This subclass has a BOOL property (BOOL prop;) which is working fine.
object1.prop = YES;
NSLog (#"What does the BOOL say? %#", object1.prop ? #"YES" : #"NO");
-> Console outputs YES.
Sothis is all working fine, but how can i change the value of prop within the NSArray? For sure things like [[array objectAtIndex:0] anf]; = YES; won't work because of the Syntax, but i don't really know the syntax how to do it.
I'd love if anybody helped me!
Thanks in advance.
There are a number of related ways designed to avoid the compiler warnings. The simplest 'inline' approach is (assume your subclass of UIButton is called MyButton):
((MyButton *) array[0]).prop = YES.
or (using older syntax):
((MyButton *) [array objectAtIndex: 0]).prop = YES.
or
MyButton *button = (MyButton *) array[0];
button.prop = YES.
[(YourSubclass *)[array objectAtIndex:0] setProp:YES];
In general the cast is optional, since NSArray holds objects of the type id. But for code clearness it can harm to cast, so you will see later what you where intending to do.
I'm writing some code that will be using NSMutableArray and storing int values within it, wrapped within NSNumbers.
I would like to confirm that querying an iOS NSArray or NSMutableArray using new NSNumbers with same values is legal, of if I need to explicitly iterate over the array, and check if each int value is equal to the value I want to test against?
This appears to work:
NSMutableArray* walkableTiles = [NSMutableArray array];
[walkableTiles addObject:#(1)];
[walkableTiles addObject:#(2)];
[walkableTiles addObject:#(3)];
if([walkableTiles containsObject:#(1)])
{
DLog(#"contains 1"); //test passes
}
if([walkableTiles containsObject:[NSNumber numberWithFloat:2.0]])
{
DLog(#"contains 2");//test passes
}
if([walkableTiles containsObject:[NSNumber numberWithInt:3]])
{
DLog(#"contains 3");//test passes
}
What you are doing is fine. Why wouldn't it be?
The containsObject: method actually iterates over the array and calls the isEqual: method on each object passing in the object you are checking for.
BTW - there is nothing special here about using NSNumber. It's the same with an array of any object type. As long as the object's class has a valid isEqual: method, it will work.
Per the Apple's NSNumber documentation, you should use isEqualToNumber:
isEqualToNumber: Returns a Boolean value that indicates whether the
receiver and a given number are equal.
- (BOOL)isEqualToNumber:(NSNumber *)aNumber