table view data loading issue - objective-c

I have a doubt in table view. I have placed a label & switch in table view cell. I set the switch value as NO by default. Now when table view is loaded in simulator the table view displays switch with NO value. Now I selected switch value as YES. But table view uses dequeue reusable cell method Property when table view is scrolled objects will reload every time now what will be the switch value will it be NO or YES?

It will be YES.
One more thing on scrolling tableview it not call reload method. Its just reusing already created tableview cells if you are using deque reusable cell method Property.

TableViewCells get destroyed and created where necessary when the table view gets scrolled.
When a cell gets destroyed as it goes out of the visible screen area, your switch control being a subview of the cell also gets destroyed.
So when you scroll back to one of your previously set switches, what you're actually seeing is another instance of a UITableViewCell with a switch view added to it, and so it looks like the switch value changed back to NO.
What you need is a third thing that remembers what the value of each switch should be in each row. If you're display a table of Core Data entity information, then perhaps you can define a property for your entity like "active". In that case, every time you change a switch value, you set your core data entity "active" property to the switch value e.g.:
-(UITableViewCell *)tableView:(UITableView)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
...
if(cell == nil)
{
// set switch control tag number (don't use 0)
}
// get switch control by tag number
// pseudocode
Engine *myEngine = [arrEngine objectAtIndex:indexPath.row];
mySwitchControl.active = myEngine.active;
...
}
// when you change switch value, remember to update your core data entity value
-(void)updateSwitchValue:(BOOL) newValue
{
// update value
}
Otherwise, you can simply use a NSMutableArray of bool values to identify which row each switch should be YES or NO:
// my header file (.h file)
#interface
{
NSMutableArray *arrSwitchValues;
}
// my implementation file (.m file)
-(UITableViewCell *)tableView:(UITableView)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
...
if(cell == nil)
{
// set switch control tag number (don't use 0)
}
// get switch control by tag number
// pseudocode
BOOL switchVal = [arrSwitchValues objectAtIndex:indexPath.row];
mySwitchControl.active = switchval;
...
}
// when you change switch value, remember to update your array
-(void)updateSwitchValue:(BOOL) newValue
{
// update value
}
Hope that helps.

to guarantee not have duplicated cells:
use this code in
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
as follows :
for(UIView *v in [cell subviews])
{
if([v isKindOfClass:[UILabel class]] ||[v isKindOfClass:[UISwitch class]])
[v removeFromSuperview];
}

Related

UITableView checkmark in some particular row in every section

I have a UITableView containing 'N' number of sections with 'N' no of rows in each section.
My requirement is:
When the table view page loads the 1st row in each section must be check-marked. The user then will have the option to select his choice in each section and that row in that particular section is check-marked.
How do I implement this functionality?
The information displayed in any particular row of a table is determined by the table's data source. Make sure that the data structure that you use for your data has some way of indicating that a given row has a check mark. Then just implement -tableView:cellForRowAtIndexPath: such that it determines whether a check mark should be displayed for the cell in question and adjusts the cell accordingly.
For example, let's say that the data is represented as an array of sections, and each section is an array of dictionaries. Each row, then, has its own dictionary. If a row is to have a check mark, its dictionary will have a checked entry set to YES; if it doesn't, that entry is NO. You can display the check mark as an image. Then you have:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
MyTableViewCell *cell = ... // code to get the cell
if (myData[indexPath.section][indexPath.row][#"checked"].boolValue == YES) {
cell.checkedImageView.image = self.checkmarkImage;
}
else {
cell.checkedImageView.image = nil;
}
return cell;
}
Putting the check mark in the first row of each section is just a matter of initializing your data so that the first entry in each section's array is checked and the others aren't.
You can change the checked cell by implementing -tableView:didSelectRowAtIndexPath: so that it scans through the array for the given section and unchecks any checked row, and then checks the selected row.
Of course, you don't have to represent your data using an array of arrays of dictionaries, and there's a good chance that you don't. That's fine -- the point here is just that you'll implement the functionality that you're after by implementing the table's delegate and data source such that they support the check mark, and that the presence or absence of the check mark in any particular row will be determined by some aspect of your table's data.
You can set your cell's accessoryType to a checkmark:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = ... // code to get the cell
// isCellSelectedAtIndexPath: is your custom method
// which encapsulates cell selection state logic
if ([self isCellSelectedAtIndexPath:indexPath])
{
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
else
{
cell.accessoryType = UITableViewCellAccessoryNone;
}
return cell;
}

Updating subviews in cells on a UITableView

I'm developing an application in iPad 6.0 using Storyboards.
Let me first explain my goal. I'm trying to achieve a Master-Detail (SplitViewController-like) View Controller using 2 UITableViewControllers.
The first UITableView("Master"), let's call this HeaderTableView, as the name implies, lists down the Headers for the...
...Second UITableView("Detail"), let's call this the EncodingTableView, which contains a programmatically changing CustomTableViewCell (subviews contained within each cell may be a UITextField, UIButton or UISwitch).
See EncodingTableView.m
- (void)updateEncodingFields:(NSArray *)uiViewList
{
// Add logic for determining the kind of UIView to display in self.tableView
// Finally, notify that a change in data has been made (not working)
[self.tableView reloadData];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *encodingFieldsTableId = #"encodingFieldsTableId";
CustomTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:encodingFieldsTableId];
if (cell == nil) {
cell = [[CustomTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:encodingFieldsTableId];
}
// Change text in textView property of CustomTableViewCell
cell.encodingFieldTitle.text = uiViewList.title;
// added methods for determining what are to be added to [cell.contentView addSubView:]
// data used here is from the array in updateEncodingFields:
}
My HeaderTableView.m, contains the didSelectRowAtIndexPath to update the EncodingTableView
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if (![selectedIndexPath isEqual:indexPath]) {
selectedIndexPath = indexPath;
[self updateDataFieldTableViewForIndexPath:indexPath];
}
}
- (void)updateDataFieldTableViewForIndexPath:(NSIndexPath *)indexPath {
[self.encodingTableView updateEncodingFields:self.uiViewList];
}
Question
- Data is all ok but why doesn't EncodingTableView "redraw"ing the fields? My
suspicion is that reusing cells has something to do with this but I just can't figure out why.
Screenshots on the result:
Initial Selection in HeaderTableView
Second Selection in HeaderTableView
What I've tried :
I kept seeing suggestions such as [UITableView setNeedsDisplay],
[UITableView reloadData] and [UITableView setNeedsLayout] but none of
them worked.
Removing the reuse of tableViewCells works fine but this causes parts of my
CustomTableView.encodingFieldTitle to disappear. Not to mention that this might cause performance issues if I were to drop reusing cells.
Restrictions:
I know that a good idea is to use a SplitViewController but this is just a subpart of my app (hence not the RootViewController).
Finally, thanks for reading such a long post. ;)
It looks like you are most likely adding subviews inside tableView:cellForRowAtIndexPath:.
The issue is that if you use cell reuse then are not always starting from a blank slate inside tableView:cellForRowAtIndexPath: instead you can possibly be given a cell back that has already been configured once. This is what you are seeing, a cell that has previously had labels added to it is handed back to you and then you add some more labels over the top.
There are a few way to deal with this:
(My preferred option) Create a subview of UITableViewCell with these extra sub views available as properties.
Ensure the cell setup is only done once
A great place to do this is when you actually create a cell when one does not already exist e.g. inside the if (cell) check
if (cell == nil) {
cell = [[CustomTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:encodingFieldsTableId];
// add subview's here and give them some way to be referenced later
// one way of doing it is with the tag property (YUK)
UILabel *subView = [[UILabel alloc] initWithframe:someFrame];
subView.tag = 1;
[cell.contentView addSubview:subView];
}
UILabel *label = (id)[cell.contentView viewWithTag:1];
label.text = #"some value";
One problem i can see in your code is that the cell identifiers used are different in tableView cellForRowAtIndxPath function.
While dequeueing you are using this identifier - > "encodingFieldsTableId"
&
while creating a cell you are using this identifier - > "dataFieldUiGroupTableId".
Ideally these two identifiers should be same !!!
Try adding,
cell.encodingFieldTitle.text = nil;
Before if(cell == nil)
So that whenever your cellForRowAtIndexPath method is called, the string already present in the cell you are going to reuse will get deleted and the new text in uiViewList.title will be displayed.

Assigning a separate user interface for every table view item

Can someone tell me how to assign a interface to a table view element, using storyboards? I'm making a medical calculator that has different calculators for every equation, and I need help making code that points a element to push to another interface. This is because for every equation, there are different fields to fill out (such as age, oxygen levels, whether someone has diabetes or not, height, etc.) Not every equation needs the same fields.
I have tried doing this:
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// Deselect row
[tableView deselectRowAtIndexPath:indexPath animated:YES];
// Declare the view controller
UIViewController *anotherVC = nil;
// Determine the row/section on the tapped cell
switch (indexPath.section) {
case 0:
switch (indexPath.row) {
case 0: {
// initialize and allocate a specific view controller for section 0 row 0
anotherVC = [[BmiViewController alloc] init];
break;
}
case 1: {
// initialize and allocate a specific view controller for section 0 row 1
/anotherVC = [[AaOxygenGradientViewController alloc] init];
break;
}
}
break;
}
}
But after doing this, it refers back to what was originally in the storyboard document (which is empty because I have created the interface programmicatally), instead of showing my test alert popup.
Also, is it possible to maybe make a bunch of table view cells, then have every one segue to every other view controller in the storyboard?
Thanks a lot in advance!
First, you are running deselectCellAtIndexPath? What is the reason for this? If you are just trying to remove the blue highlight then it's better to change the UITableViewCellSelectionStyle (or something like this) of the cell.
I'm not sure what you're asking for the first part but for the segue part then yes.
In Storyboard set up the segues from the tableViewController to each other VC that you want to segue to and give them all sensible identifiers.
i.e. medicalCalulatorSegue
graphSegue
userDetailsSegue
etc...
Then in your didSelect method you will have something like...
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
//some stuff...
switch(indexPath.row) {
case 0:
[self performSegueWithIdentifier:#"medicalCalulatorSegue"];
break;
case 1:
[self performSegueWithIdentifier:#"graphSegue"];
break;
case 2:
[self performSegueWithIdentifier:#"userDetailsSegue"];
break;
}
}
This will then segue to each of the different view controllers depending on which cell is selected.
The reason for not deselcting the cell is that in your method prepareForSegue you can then still access the indexPath of the selected cell...
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];

View Based NSTableView with Sections

I am looking for a way to create the iOS like sections in NSTableView (like in iTunes 11 - Attached).
As you can see in the screenshot, "Albums" is one section and "Songs" is second. Any help would be appreciated.
Thanks!
I see this is an old question, but the answer is to use a View based NSTableView then implement tableView:viewForTableColumn:row:.
This is code based on how I do it. It hasn't been compiled in Xcode.
-(NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
NSTableCellView *cell = nil;
// get your row from your array of objects.
// determine if it's a section heading or not.
SomeClass *someObject = [self.myObjects objectAtIndex:row];
if (someObject.isSectionHeading) {
cell = [tableView makeViewWithIdentifier:#"HeaderCell" owner:self];
cell.textField.objectValue = someObject.headingName;
} else {
cell = [tableView makeViewWithIdentifier:#"DataCell" owner:self];
cell.textField.objectValue = someObject.rowValue;
}
return cell;
}
And also tableView:isGroupRow will put a grey background on your section headings
-(BOOL)tableView:(NSTableView *)tableView isGroupRow:(NSInteger)row {
BOOL isGroup = NO;
// Choose some way to set isGroup to YES or NO depending on your records.
return isGroup;
}
Make sure in Interface Builder you have set the identifiers for your NSTableCellViews to "HeaderCell" and "DataCell". Or use whatever names you want. As long as it matches your code. You can have as many of these cells as you want.
If you make a subclass of NSTableCellView then you can easily add your own text fields, checkboxes, images, etc. to the view and set their values accordingly.
If you want sections you basically have to roll your own (recognize that row x is supposed to be a section cell and provide a section view. TwUI has TUITableView which enables this (and massively improves scroll performance, in my experience).
There is a very good and simple tutorial showing how to implement a NSTableView with sections with sample code on github. Just watch it here and in the video description there is a link to download the code.
I believe the proper way to deal with this these days is to implement the table view delegate method tableView(rowViewForRow:). If you detect that the specified row is not a header, simply return nil. Otherwise, the process is similar to making a view for a specific table row and column. For example, if you store all your row data in a single array of Any called tableRows, and determine the difference between a header and an ordinary row by using different classes within that array, it might look something like this:
override func viewDidLoad() {
super.viewDidLoad()
let headerNib = NSNib(nibNamed: "HeaderRow", bundle: nil)
tableView.register(headerNib, forIdentifier: NSUserInterfaceItemIdentifier(rawValue: "SectionHeader"))
}
func tableView(_ tableView: NSTableView, rowViewForRow row: Int) -> NSTableRowView? {
guard let headerData = tableRows[row] as? SectionHeader else { return nil }
let header = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "SectionHeader"), owner: nil) as? HeaderRow
header?.titleLabel.stringValue = headerData.title
return header
}
Note that this only works if your table is view-based!

Cell selection issue with EasyTableView

I have what must be a simple problem with EasyTableView (https://github.com/alekseyn/EasyTableView)
I have a number of horizontally scrolling tables that function properly.
I am able to select a cell and perform a segue, however, once the new view controller is dismissed, I am no longer able to select that cell and perform the same action until I have selected another cell in the same table.
My question is: How can I deselect previously selected the cell programmatically to renable this particular action.
Thanks in advance!
The selectedIndexPath is intentionally persistent in case a user scrolls the selected tableview cell offscreen and then back again. If you don't want this persistence please add the line shown below, after the delegate method (in EasyTableView.m):
- (void)setSelectedIndexPath:(NSIndexPath *)indexPath {
if (![_selectedIndexPath isEqual:indexPath]) {
NSIndexPath *oldIndexPath = [_selectedIndexPath copy];
_selectedIndexPath = indexPath;
UITableViewCell *deselectedCell = (UITableViewCell *)[self.tableView cellForRowAtIndexPath:oldIndexPath];
UITableViewCell *selectedCell = (UITableViewCell *)[self.tableView cellForRowAtIndexPath:_selectedIndexPath];
if ([delegate respondsToSelector:#selector(easyTableView:selectedView:atIndexPath:deselectedView:)]) {
UIView *selectedView = [selectedCell viewWithTag:CELL_CONTENT_TAG];
UIView *deselectedView = [deselectedCell viewWithTag:CELL_CONTENT_TAG];
[delegate easyTableView:self
selectedView:selectedView
atIndexPath:_selectedIndexPath
deselectedView:deselectedView];
// Add this line here!
_selectedIndexPath = nil;
}
}
}