Passing a Class as parameter and instantiating an object using this class - ios

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] == ...)

Related

nsarray method--> containobject can't find a object that is exist in array [duplicate]

I have a simple question regarding xcode coding but don't know why things are not performing as I think. I have an array of objects (custom objects). I just want to check if this one is within the array. I used the following code:
NSArray *collection = [[NSArray alloc] initWithObjects:A, B, C, nil]; //A, B, C are custom "Item" objects
Item *tempItem = [[Item alloc] initWithLength:1 width:2 height:3]; //3 instance variables in "Item" objects
if([collection containsObject:tempItem]) {
NSLog(#"collection contains this item");
}
I suppose the above checking will give me a positive result but it's not. Further, I checked whether the objects created are the same.
NSLog(#"L:%i W:%i H:%i", itemToCheck.length, itemToCheck.width, itemToCheck.height);
for (int i = 0, i < [collection count], i++) {
Item *itemInArray = [collection objectAtIndex:i];
NSLog(#"collection contains L:%i W:%i H:%i", itemInArray.length, itemInArray.width, itemInArrayheight);
}
In the console, this is what I got:
L:1 W:2 H:3
collection contains L:0 W:0 H:0
collection contains L:1 W:2 H:3
collection contains L:6 W:8 H:2
Obviously the tempItem is inside the collection array but nothing shows up when I use containsObject: to check it. Could anyone give me some direction which part I am wrong? Thanks a lot!
The documentation for [NSArray containsObject:] says:
This method determines whether
anObject is present in the receiver by
sending an isEqual: message to each of
the receiver’s objects (and passing
anObject as the parameter to each
isEqual: message).
The problem is that you are comparing references to objects rather than the values of the objects. To make this specific example work, you will either need to send [collection containsObject:] an instance of a variable it contains (e.g. A, B, or C), or you will need to override the [NSObject isEqual:] method in your Item class.
This is what your isEqual method might look like:
- (BOOL)isEqual:(id)other {
if (other == self)
return YES;
if (!other || ![other isKindOfClass:[self class]])
return NO;
if (self.length != other.length || self.width != other.width || self.height != other.height)
return NO;
return YES;
}
For a better implementation, you may want to look at this question.

How to create methods at runtime with Objective-C

I would like to declare / create 20 methods dynamically with Objective-C. Those methods will be added to action listeners. The methods will have almost the same implementation, there will be only a few differences. But I don't wanna have to write these methods 20 times. I know how to store blocks of methods into an array, however I am having trouble passing those methods to action listeners. That is what I have:
NSMutableArray *arr = [NSMutableArray new];
[arr addObject:^(){NSLog(#"my block");}];
id (^ myblock)() = [arr objectAtIndex:0];
sel_registerName("myblock");
[numPad addTarget:self action:#selector(myblock) forControlEvents:UIControlEventTouchUpInside];
notice that the action parameters expects a selector, but I got an error because 'myblock' inside the #selector won't return anything, as 'myblock' has not been declared yet.
Does anyone have a solution?
If you really, really need to create a target/action target at runtime, the simplest solution is to use an NSBlockOperation. E.g.
NSMutableArray *blockOperations = [NSMutableArray new];
for(int i = 0; i < 20; i++) {
NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
NSLog(#"I am listener %d", i);
}];
[blockOperations addObject:blockOperation];
[numPad addTarget:blockOperation action:#selector(start) forControlEvents:UIControlEventTouchUpInside];
}
Though if your target is called numPad, what you probably want to do is wire all the individual buttons into the same target and just give them a tag that represents their value. E.g.
- (void)numPadButtonAction:(UIView *)sender {
NSLog(#"user pressed button with tag %#", #(sender.tag));
}

Obj C and makeObjectsPerformSelector - am I overlooking something?

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.

How to load a JSON onto a NSArray

I need to pass to a Object a NSArray. In order it can show that array as Tags on the Interface, It works properly when using a manually added NSArray, But not i need to load a NSArray With a JSON Array Called Subjects I've done some code but it's not working out.
Also gives an error:
/Users/eddwinpaz/Documents/iOS-apps/mobile-app/mobile-app/UserDetailViewController.m:86:26: No visible #interface for 'NSArray' declares the selector 'insertObject:atIndex:'
This is the Code I'm Using
NSArray *subject_tag;
for (NSString* subject in [responseObject valueForKey:#"subjects"]) {
[subject_tag insertObject:subject];
}
CGRect frame = Scroller.frame;
frame.origin.y = 100;
frame.origin.x = 5;
frame.size.height = 150;
UIPillsView *pillsView = [[UIPillsView alloc] initWithFrame:frame];
[pillsView generatePillsFromStringsArray:subject_tag];
[Scroller addSubview:pillsView];
You have 3 problems here.
You never initialize your array, you only declare it. (This one is not actually causing the error and would just cause the code to fail silently once you fixed the next 2)
NSArrays are immutable. Elements cannot be added or removed after initialization. You need to use an NSMutableArray for this.
The method you are using does not exist in NSMutableArray anyway.
Here is what you should be doing:
NSMutableArray *subject_tag = [NSMutableArray new];
for (NSString* subject in [responseObject valueForKey:#"subjects"]) {
[subject_tag addObject:subject];
}

Constructing a NSArray of UIView objects with unique center

I have a array of custom UIView objects with 2 or more objects having same center and I have to construct another array from it with distinct centers. What is the best way to do it?
I tried with below piece of code but it does not work.
self.distinctObjects = [NSMutableArray arrayWithCapacity:iAllObjects.count];
for (MyCustomView *customView in iAllObjects)
{
BOOL hasDuplicate = [[self.distinctObjects filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"SELF.center == %#", customView.center]] count] > 0;
if (!hasDuplicate) {
[self.distinctObjects addObject:customView];
}
}
You can't use struct's (in your case center is a CGPoint) in NSPredicate. Also comparing floating-point values directly isn't a good idea.
You should follow this answer. Just replace [annotation coordinate].latitude with myView.center.x, [annotation coordinate].longitude with myView.center.y, and so on. It should be easy.
BTW your code has O(n^2) complexity, but maybe that's not a problem if the array is small.
I fixed this with below piece of code:
NSMutableArray *uniqueCenters = [NSMutableArray arrayWithCapacity:iAllObjects.count];
self.distinctObjects = [NSMutableArray arrayWithCapacity:iAllObjects.count];
for (MyCustomView *customView in iAllObjects)
{
if (![uniqueCenters containsObject:[NSValue valueWithCGPoint:customView.center]]) {
[uniqueCenters addObject:[NSValue valueWithCGPoint:customView.center]];
[self.beacons addObject:customView];
}
}

Resources