How can one address controls dynamically in obj-c? - iphone

For instance I have created a view with 50 sequential buttons named btn1-btn50. If I want to address these buttons and make some changes to them in a loop how can I address the controls with a string name?

The typical way to get a view in a hierarchy is to call [parentView viewWithTag:] to get the view. If you give the buttons tag values from 1 to 50 you can use that to access the buttons.
If for some reason you need strings, you will have to create a custom subclass of UIButton that has a name member, assign a name to that member, then later iterate through the view hierarchy searching for an instance of your custom class with a name matching your search criteria.

If you have given them a tag, you can access them conveniently using -viewWithTag: on the parent view.

You have no guarantee that these buttons are laid out in memory sequentially, so trying to use pointer arithmetic is probably out. But you can get all the subviews of an NSView with -subviews, and just do something with the NSButtons:
for (NSView *view in [theView subviews]) {
if (![view isKindOfClass:[NSButton class]]) continue;
/// you got a button!
}

In your View Contoller add an NSMutableDictionary *buttonViews property. In your viewDidLoad method, add each button to buttonViews using the name string as the key and the button as the object. You will have to use viewWithTag: already discussed to obtain the views. Now you can locate the button using the string and benefit from the collection methods and fast enumeration. Apple's documentation for Interface Builder indicates that the "name" in IB is used to assist identifying objects in IB which is helpful for translation.

Related

How Can I orderly arrange items in Referencing Outlet Collection automatically?

I have Referencing Outlet Collection of UITextfields arranged in Xib as one below another. When I print Referencing Outlet Collection I found that its orderless. I need to automatically arrange text fields, ie text fileld comes 1st in UI should appear 1st in Outlet Collection Array. Any idea?
This is an unfortunate fact of IBOutletCollection. You may want to dupe rdar://12121242.
You can set a tag for each view in IB, and then sort the collection by tag. You can also sort the collection by frame origin. In either case, you will have to do this by hand in viewDidLoad.
Assuming you have an IBOutletCollection called labelsArray, and you have set the tag property of each to the desired order, then the code is:
-(void)viewDidLoad {
self.labelsArray = [self.labelsArray sortedArrayUsingDescriptors:#[[NSSortDescriptor sortDescriptorWithKey:#"tag" ascending:YES]]];
}
Yes, apple has provided facility for this by help of IBOutletCollection
declare property like this
#property (nonatomic,retain) IBOutletCollection(UILabel) NSArray *labelsArray;
Connect all controls with above property.
labelsArray automatically instantiated for you when the NIB file is loaded
Now just use labelsArray in your program for oulets. for maintaining order set the tag value of controls as you wish

eliminate switch statement in foreach loop

I have question regarding about finding subview using '.tags' in one UIView.
for (UIView *viewObj in [self.view subviews])
{
// want to find viewObject by its tag.
// but I heard for-switch or for-case paradigm are bad pattern design by WTF.
// there are total 9 of tags will be use for finding view object
switch (viewObj.tag)
{
case 0:
.
..
...
}
}
How can I make this to be non-switch or non-if statement? Should I just add into one array then fetch from there? For example, you add views with desired tags and fetch from this array.
Thank you.
From your comments, it's a very finite set of items that will exist - 9 items. If that is expected to be static, there is nothing really wrong with a switch statement. Perhaps you can functionally decompose each switch into a separate method. That said, perhaps the command pattern would be one approach to consider, in addition to your array idea. For reference: http://en.wikipedia.org/wiki/Command_pattern
If it is only going to be a fixed number of views and each view has different methods that need to be performed you could access the views directly.
UIView* someView = [self.view viewWithTag:0];
//Operations on view 0
UIView* someOtherView = [self.view viewWithTag:1];
//Operations on view 1
//...
One thing you could do is have the UIViews in your subviews be of the same type by having each conform to the same protocol. Then you could cast each to id<YourProtocol> and call the same method within each UIView.
And to further Shan's answer, if you can decompose them into functions and put them (function pointers) into an array, then you may be able to make it a little simpler, or you can use the new "Blocks" language feature.
There's a new IBOutlet type, IBOutletCollection. You use that for an NSArray, and then in IB you can add views to that outlet - at runtime, they will all be put into the array (unordered).
To get out a specific one you want, in viewDidLoad you could map all of the NSArray entries into a dictionary keyed by tag value, then just use "objectForKey" to get them out.

How do I reference an instance of a class using its tag?

I have several instances of a UIControl class Foo being instantiated, one instance corresponding to each cell in a UITableView. The Class has:
BOOL selected;
UIImageView *imageView;
UIImage *imageOne;
UIImage *imageTwo;
I've assigned each instance a tag:
foo.tag = indexPath.row;
I would now like to reference the UIImageView.image for a (or several) specific instance(s) by its tag to switch it to the other image.
In my search I've seen things like classes being assigned tags using initWithTag (I assume they're assigning tags)...
SomeClass *someClass = [[SomeClass alloc]initWithTag:1 ...
[someArray addObject: [[SomeClass alloc]initWithTag:2 ...
[someArray addObject: [[SomeClass alloc]initWithTag:3 ...
...but I haven't seen how they are later referenced by that tag.
I have seen a reference to getChildByTag which had promise, but I can't find it in the documentation or examples (maybe not iphone).
Does anyone know how reference the imageView.image within an instance using the instance's tag? (the imageView doesn't have a tag)
Thanks
Let me be a bit more specific. As each instance of Foo is set up in the UITableViewCells I use addTarget
[self addTarget: self action: #selector(switchImage:) forControlEvents: UIControlEventTouchDown];
Then I have this method to switch the images:
- (void) switchImage:(id)sender
{
selected = !selected;
imageView.image = (selected ? imageOne : imageTwo);
// self.tag in here is the indexPath.row from the foo.tag initially assigned
// NSLog(#"switchImage:%#",sender);
}
This works perfectly. I click on the image and the image switches. But in the entire tableView, I only want 1 imageOne all the rest to be imageTwo so I want a way to first turn off all images and then turn on the one. I therefore felt that I could to loop thru all of the instances of Foo using the tag to either somehow directly change the imageView.image or run switchImage in each of the instances to turn off each cell's image.
Lastly, when I look at sender via NSLog, I see that each Foo has a different address so I was wondering if something like allTargets (Foo is a UIControl) would allow me to get to all of the switchImage methods.
I'm pretty deep into this rabbit hole but I'll certainly start over if necessary.
I agree that there are other ways to deal with this, but you could add all your Foo objects to an array or set and iterate through them until you find the one with the tag you want, then access the imageview in the usual way.
If I'm getting you right, you try to access Foo objects (globally) by the tag you assigned to them before. There's no way to do that and it's not what the tag property of UIViews was designed for.
You have to use another way to access your Foo objects. There are uncountable ways of setting up a way to access objects. Since you would usually access them from their data source or view controller, I’d add a dictionary with there. You could also use UITableView's visibleCells property to only change images on cells that are actually displayed.
I feel a bit lost, but how about [UIView viewWithTag:]?
UIView instances have a tag property. You can access them from their superview using:
UIView *view = [superview viewWithTag:n];

Setting ivar in objective-c from child view in the iPhone

Maybe a FAQ at this website.
I have a TableViewController that holds a form. In that form I have two fields (each in it's own cell): one to select who paid (single selection), and another to select people expense is paid for (multiple selection).
Both fields open a new TableViewController included in an UINavigationController.
Single select field (Paid By) holds an object Membership
Multiple select field (Paid For) holds an object NSMutableArray
Both vars are being sent to the new controller identically the same way:
mySingleSelectController.crSelectedMember = self.crPaidByMember;
myMultipleSelectController.crSelectedMembers = self.crSelectedMembers;
From Paid for controller I use didSelectAtIndexPath method to set a mutable array of Memberships for whom is paid:
if ([[tableView cellForRowAtIndexPath:indexPath] accessoryType] == UITableViewCellAccessoryCheckmark) {
[self.crSelectedMembers removeObject:[self.crGroupMembers objectAtIndex:indexPath.row]];
//...
}
else {
[self.crSelectedMembers addObject:[self.crGroupMembers objectAtIndex:indexPath.row]];
//...
}
So far everything goes well. An mutable array (crSelectedMembers) is perfectly set from child view.
But...
I have trouble setting Membership object.
From Paid By controller I use didSelectAtIndexPath to set Membership:
[self setCrSelectedMember:[crGroupMembers objectAtIndex:indexPath.row]];
By NSlogging crSelectedMember I get the right selected member in self, but in parent view, to which ivar is pointed, nothing is changed.
Am I doing something wrong? Cause I CAN call the method of crSelectedMembers, but I can't change the value of crSelectedMember.
If I understand your question, the most likely cause is an improper property declaration.
If you want to pass values from one object to another using each objects properties, then you need to make sure to use assign to ensure the properties in one object are pointing at the same instances as the property in the other object.
So in your topViewController you have a property:
#property (nonatomic,retain) NSString crSelectedMember;
Then in your child view controllers you have:
#property (nonatomic,assign) NSString crSelectedMember;
This forces the value into the exact object in the parent controller.
However, this is a very fragile way to pass data between viewControllers. As your app becomes more complicated, it will be impossible to track all the passed data. (Worse, if you run into memory limitations, the parent view controller may unload and not even exist when you try to pass data to it.)
Instead, you should have a single custom object devoted to holding your data. Each view controller should query that object for the data it needs and should write any changes back to that object. The view controllers never communicate directly. This technique allows you to control the data in one specific location instead of spreading it out all over your code and it scales well. You can add an arbitrary number of view controllers to you app without having to worry about tying them all together.
See this post for details: iPhone: How to Pass Data Between Several Viewcontrollers in a Tabbar App

What UIView is returned when using -viewWithTag: when several views have same .tag?

Say I have 4 UIViews, made in IB, all with the tag property = 2
When I get a view with:
UIView *thisView = (UIView*)[self.view viewWithTag:2];
What is the criterion for retrieving that UIView since several have the same .tag value?
Is it:
random
the first one created
the view with the lowest index in it's superview
something else
Its weird but the view that is added first will be returned if you try to get the views among the views with same tag. You can check it in this way too.
NSLog(#"%#",[[self.view viewWithTag:custTag] class]);
Hope this helps.
If you use Interface Builder, it depends which order you use. In my case, UIActivityIndicator will be my result, and not UIWebView or UIButton with the same tag.
if I had to guess, I would assume that it would be almost-random. as in, you'll probably get some amount of consistency, but every so often it'll be something completely different (heh).
I'd say use different tags for them?