Can two UIPickerView objects share the same datasource array? - ios

I have used Interface Builder in Xcode 4.02 to add two UIPickerViews in a View. They are connected to the same delegate and datasource (UIViewController). In my .h file I have also declared UIPickerViews and connected them as reference outlets, as in many examples.
In viewForRow I use the same NSMutable data array of UILabels to return the appropriate values (this array was populated in viewDidLoad).
However, I find that no Label appears in both UIPickerViews at the same time. For example, when the app starts each UIPickerView should show elements 0, 1 and 2. viewForRow is invoked 3 times for each UIPickerView but only the view which invokes viewForRow second will display the first 3 rows. The other UIPickerView is blank. If I scroll the first view down to, say, the 6th element and then back to the first, the view will then show the first 3 elements but the second view (the one that did show the first 3 rows) now shows nothing. Specifically, no data array element will appear in both UIPickerViews at the same time.
Is this expected? Should each UIPickerView have its own backing array - if you're using them? Aren't all these returned views just pointers? It's as if each array element can only be displayed (pointed to) at most once at any time.
If I use two separate data arrays then there appear to be no problems. But it does mean extra memory and extra coding.
Note: in viewForRow I have code to set the label's size:
UILabel *xx = (UILabel *)[self.array1 objectAtIndex:row];
CGSize rowSize = [thePickerView rowSizeForComponent:component];
CGRect labelRect = CGRectMake (0, 0, rowSize.width, rowSize.height);
[xx setFrame:labelRect];
The values set here do not change even when the row later appears to be blank.

Multiple picker views can share the same data source but a view (UILabel in this case) can only have one superview.
You cannot use the same labels in both pickers and there's probably little reason to store them in an array of your own anyway. A better approach would be to create a separate label in the viewForRow method and have just an array with the labels' content (e.g. an NSString).
In your current implementation, when you return the label, the picker adds it to its own view hierarchy which implicitly removes it from any view it was in before (your other picker).

Related

How to pass data between two cells (swift)?

I have table view with multiple different cells, and one of the cells depends on property of another one. I.e. I have FirstCell, which has dynamic property count, and I have SecondCell, which contains several UITextFields. I want the number of these UITextFields to match the count property. How can I get count property from FirstCell considering that this property can be changed?
I.g. FirstCell contains count = 3, SecondCell shows three UITextFields. Property count changes to value 4 and another one UITextField appears in SecondCell.
The value of count should be in your data model. You add an observer, or put code into the setter, so when the value of count is changed, the two cells depending on it (the one displaying count, and the one showing the text fields) are both reloaded; that's reloadCellAtIndexPaths or something like that from memory.
Obviously the code that loads cells must be written correctly, and code changing count in the first cell must change the model property.
Thinks like this should be managed by the UI(Table)ViewController. The view controller should be notified when the text field changes (for example using target-action) and then update the data source and reload the relevant cells.

How to distinguish 3 objects in a custom cells

I am making an iPhone app.
In this application I have to make a look like below.
I am not allowed to use collection view.
I am using tableview, and custom cells. Which I am easily able to incorporate. Means taking 3 subviews in Custom cells. And making a look.
Here the problem is In a cell, how do I distinguish each object. so that I can call each object, to set an image on image view.
Is there any Object oriented mechanism to distinguish all 3 objects in a cell ?
Try to get the data as NSArray of NSDictionary containing an array of 3 objects that you want to display on cell.
Assign tag to UImageView in the custom cell.
In cellForRowAtIndexPath, get the 3 objects and apply image using switch case.
Well everything depends on how you get the data from the server
You can use outlet collections which will give you an array of UIImageView and you can assign different tags to the imageview so you can assign to them.
You can use these guides to understand how outlet collections works:
http://nshipster.com/ibaction-iboutlet-iboutletcollection/
http://useyourloaf.com/blog/interface-builder-outlet-collections/
All the standard procedures should work to achieve this but then it only depends on how nice you want to do this.
The straight forward procedure is to expose the outlets of the image views and labels in the cell and assign the correct values to those when dequeuing/creating table view cell.
The first upgrade would be to rather expose 3 setters on the cell to simply set your model to each of them which will then internally set the images and texts inside the cell.
The next thing you may do is to rather insert an array of objects (always sending up to 3 in your case) instead of having 3 setters.
At this point you may actually rather use a collection view INSIDE the cell and make the cell a data source for the collection view. But this is totally optional.
Now since you may still dislike the table view data source you may create another model which contains an array of objects (again up to 3 in your case) and make a system that will distribute your original array of objects into the array of these containers.
If then you need to handle buttons or other touch events they may be handled with collection view delegate or 3 buttons and in both cases I advise you to handle those in the cell and create a custom delegate for the cell which will report the event with the appropriate model.
This together generates the following:
When you receive the data call a container class to distribute your array of objects (into groups of 3) and assign it to your table view data source (view controller usually)
Number of rows is the same as number of containers in the array
Cell for row assigns the container with row index to the cell. It assigns self as a delegate
Cell internally handles the object distribution either via collection view, separated outlets or outlet collections.
Cell handles actions and reports them back to the delegate (- (void)myCell:(MyCell *)cell selectedItem:(MyObject *)item;)
The cell delegate can again handle what to do upon reported actions
Also if you want to avoid a collection view inside the cell you can create a custom view using xib so you do not copy the labels, image views and such. Then simply create 3 of these custom views inside the cell. Also by using inspectable and designable these views will be visible inside the storyboard.
First I'll say that a restriction against using UICollectionView is silly. Are you still targeting ios5?
I'd look at it like this.
make your own view class for the 'subcell' let's use this term for any single instance of the 3 views per cell. I'd subclass UIImageView, adding the label for the name down the bottom and a 'setSelected:' kind of method to highlight when selected via user interaction by drawing differently.
make a UITableViewCell subclass to host and layout up to three of these subcells. I say up to 3 because the last cell may contain 1 or 2 subcells and not 3 if the total people to represent is not divisible by three.
Selection Logic: You'll need to override 'setSelected:' because you want to deselect and select only subcells, you don't want the whole cell to highlight on selection, only a third of it.
You'll also want to implement touchesEnded: in this cell so that you can figure out which of the three subcells was last touched, and you'll need to be able to query or communicate this back to the controller, probably using delegation. If the cell can communicate back whether selection was in subcell 0,1 or 2 then this together with the UITableViewDelegate didSelectAtIndexPath should map to your model nicely - selectedPerson = myArrayOfPeople[ (indexPath.row * 3) + subcellIndex ]
You'll be able to decorate your cells in cellForRowAtIndexPath: in similar fashion..
personOne = model.arrayOfPeople[indexPath.row*3]
personTwo = model.arrayOfPeople[indexPath.row*3 +1 ]
personThree = model.arrayOfPeople[indexPath.row*3 + 2 ]

iOS Dynamic cell content

I have searched for this a lot and haven't found what I am looking for.
I want to have UITableViewCell such that each have dynamic content. For example, one cell can have a place name, address, phone number, image, etc. Another cell can have some or all of these attributes.
I know that I should have functions cellForRowAtIndexPath: and heightForRowAtIndexPath: return stuff appropriately.
The problem I am facing is that I have my cells designed in nib files, and am loading the layout from there. I can calculate the heights for each cell based on conditions(though this is tedious), also I need to arrange stuff in that cell if one thing is missing. For example, if place name is missing then move all the elements up via setFrame:.
There should be an easy way out I believe?
In such dynamic cases I would not bother with using storyboards to accomplish this. It should be very easy to do this programatically...
If you really do need to create this in a storyboard you could generate a full cell which could consist of (for instance) 5 labels. Now in the table view data source I presume you have some array of objects where each element represents a single row in the table view. If so then in your case each of this object can have a method that will return an array of non-empty strings (the strings that will actually be displayed) which can be a dynamical count from 1 up to 5 strings.
If you do this you will request this array in method requesting the height for row at index path and by using this count you can then return the height of the row (for instance numOfStrings*40.0f). Inside the method requesting your cell you can simply fill those labels with the same array you use to calculate the height.
As for non storyboard approach I suggest you to override an UITableViewCell, generate a static method that will return you the cell height for string count and generate an initializer with the string array which should then iterate through the array generating the labels and setting the text to it.

Appropriate way to add multiple UIPicker controls on page

iOS Proficiency: Beginner
If I have a Xib with multiple fields that all need their own Picker View, what's an appropriate/canonical way to add multiple picker views on the page without getting the Design View all cluttered up?
1) Only add the PickerView programmatically and not via the XIB?
2) Use only 1 Picker object and populate it with different values based on the field
selection? (Possible memory benefits?)
3) Place the UIPickers on the View with a tiny height/width and then programmatically adjust height when necessary? Not even sure if the height is adjustable.
4)Combination of some of the above?
You can see in the image below, how cluttered it looks already even with just one picker view:
The view that you have with the text fields and picker views would lend itself to be part of a UITableView.
See the calendar app when you add an event.
You can set this up fairly easily by using a static UITableView.
I'm replying on my phone at the moment but will look for a tutorial if you would like.
If only one pickerView will be visible at once, then you should consider using only one pickerView and configure it's delegate/datasource so that you can give it a different datasource for each field.
Unless more than one is visible at once, there really isn't any reason to use more than one in your nib file. And even if you used more than one, you would still have to configure you delegate/datasource methods to handle each individual picker.
Hope that helps.
EDIT: It would be a little bit of work, but if you wanted the pickerView to animate in and out of the view whenever you need and if you wanted to clean your Xib up even more you could do the following:
Create a subview containing your pickerView
Set up a protocol on the subview to allow you to pass the selected value back to the view controller.
Set up your viewController to conform to the protocol on your picker subview.
Set the pickerView to be each textField's inputView.
Set the textField's delegate methods to configure the dataSource of your subview when editing begins.
By doing this, you have set your textField so that when it receives firstResponder that it will display the pickerView instead of a keyboard.

strong pointer to UIlabel is not pointing to correct label when offscreen

I have a UITableView with a prototype cells that I have created using IB. I have a UILabel in this prototype cell. I have set the tag value of 5 to this UILabel so that I can identify it using viewForTag: method.
I have also defined 20 private property like this
#property(nonatomic, strong) UILabel *label_1;
#property(nonatomic, strong) UILabel *label_2;
.
.
.
#property(nonatomic, strong) UILabel *label_20;
Now, while creating cell from cellForRowAtIndexPath: I am assigning UILabel of each row to the corresponding property (label_1 for row 1, label_2 for row 2,... etc.) using self.label_1 = (UILabel*)[cell viewForTag:5];//5 is the Tag value i have setup using IB.
Everything is fine till now. Now, when I run this app, i see that properties till 16 has proper values, rest are null. This is because only the first 16 cell has been drawn so remaining properties are still not have been assigned the corresponding UILabel.
Everything is fine till here. Now when I scroll the table so that remaining 4 cells are on screen and first 4 cells goes offscreen. now, when i print (using NSLog(#"%#",self.label_1.text).) the value of each property, I found, the properties whose corresponding labels are on screen outputs correct value, whereas, those which are offscreen outputs random values (not null, just random values, can be off other properties too.)
In normal scenario the first thing came to my mind is as soon as the rows goes offscreen they are deallocated and hence the properties are now pointing to some random location (dangling pointer). But here in my case, properties are strong so the corresponding UILabel should not b deallocated till this pointer exist. Can anybody please explain whats happening here ?
In order to save memory, UITableView recycles UITableViewCells. You are recycling cells when you call tableView dequeueReusableCellWithIdentifier:.
My guess is that label_1 == label_17, label_2 == label_18, etc.
If you need to update these labels, you need to rethink your design. All of the updates need to be done in tableView:cellForRowAtIndexPath:. When you need to update the data in the cells, you need to call [tableView reloadData], and set the correct value of the label within the tableView:cellForRowAtIndexPath: method of your UITableViewDataSource.
Cells are being reused. Let's say 10 cells can fit one screen. When you scroll down, first cell will disappear, eleventh will appear, and so on. Instead of creating new view for each cell, tableview keeps a queue and dequeues the cells from it. It means that your cell in fifth row could also be used to display the sixteenth row or any other row. You don't actually know, or have control of the cell view(and it's subviews) when it goes off screen, and you shouldn't keep strong pointers to views in dynamic cells. If your cells were static, it'd be completely fine. Instead, move your logic to tableView:cellForRowAtIndexPath:. That way, you know that cell with certain indexPath has appeared on screen and you can act accordingly.

Resources