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

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

Related

A Question about Assign Properties

Edit 2: What I previously planned was probably a bad idea and I now changed my design: My UITableViewController has an array with all the values of my UITextFields and I am using delegation to update the values in the array. (If a value in one UITableViewCell changes, I send a message with the new value and the index of the cell).
Original Question
I would like to create a UITableViewCell subclass. To access my cells, I would like to have an NSMutableArray in my UITableViewController with all the cells. Whenever I create a new cell in - tableView:cellForRowAtIndexPath: I would add it to the array. The cells should however know about this array. I would declare a property like this for the UITableViewCell:
#property (nonatomic, assign) NSMutableArray *cellsArray;
Whenever I create a new cell, I would set its cellsArray to my array.
My (probably simple) question is: Is it correct that cellsArray will hold a pointer to the array in the UITableViewController and when I add stuff to the array of the UITableViewController, the cells will know this too, i.e. can access it?
Edit: The UITableViewCells contain UITextFields. I used to rely on the -cellForRowAtIndexPath: method and the visibleCells array, however when the cells moved out of view, the content of their UITextFields would also be lost. I then decided to store the cells in an array. When the user taps save, I iterate through the array and store the values. Also, I would like to automatically update the enabled property of the save button, depending on whether all cells contain something - for this I need all cells, too.
The cells should know about the other cells so that they can select the next cell when the return/next key on the keyboard is pressed.
If there are better approaches to this, I am glad to hear about them!
Not a direct answer of your question, but this sounds like a very bad design. Why should one cell need to know about its siblings? Any event/change that occurs in one cell and has an effect on the other cells should be handled by the table view controller. The single cells should be separate entities that should have no need to know about the state of each other.
Secondly, there is no need to introduce another array to manage the table cells. The table view already has a property visibleCells that you can access from the table view controller. And should never have to interact with invisible cells anyway because those are managed by the table view and its reuse facility.
I believe the answer is Yes.
My understanding of assign is that you can assign a value to such a variable and the retain count for the original object is not incremented. Similarly you need not release the variable in the object's dealloc method. You may run the risk, however, that the original array goes away and then cellsArray is pointing at something that is no longer there.
I use assign when I want to pass a reference to an object to another object (e.g. a view controller that is going to display or otherwise manipulate the object). And in the latter object, I do not release it's pointer to the object.
You also see assign used with properties that are id's, like
#property (nonatomic, assign) id<SomeProtocol> _myDelegate;
All that being said, with the exception of the id case, often I feel "safer" using retain for the property and being sure to release in dealloc. :-)
Anyway, I think that's the crux of the difference.

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 can one address controls dynamically in obj-c?

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.

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?