I have a UITableView that displays a custom XIB cell repeated for all the data in an array I have. I also have a UIBarButton item that when pressed, adds a blank entry into that data array, which then creates a new blank cell. (Because numberOfRowsInSection returns [self.array count])
The XIB cell has a clear UITextField, so all cells are editable. The textfields, for each cell, displays that data in the array, for the appropriate index.
Now, what I'm trying to do is, when the UIBarButton item 'Save' is pressed, it gathers all of the data in the cells textfields, and basically saves it all into an array. I can't find a solution anywhere, so any help is appreciated. Thanks in advance!
EDIT: I solved my own problem. For those wondering what to do:
for (int i = 0; i < [self.dataArray count]; i++) {
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForItem:i inSection:0]];
for (id subview in [cell.contentView subviews]) {
if ([subview isKindOfClass:[UITextField class]]) {
UITextField *textField = (UITextField *)subview;
self.updatedArray = [NSMutableArray arrayWithObject:textField.text];
NSLog(#"Cell Text: %#", self.updatedArray);
}
}
}
You could just loop through all the cells in a table view and add the contents of the text field to an array.
The example is assuming all the cells are in section 0 but you could change it to a nested loop with the number of sections playing a factor.
var saveData = [String]();
for i in 0..<tableView.numberOfRowsInSection(0){
let cell = tableView.cellForRowAtIndexPath(NSIndexPath(forRow: i, inSection: 0)) as! NameOfYourSubclassedCell;
saveData.append(cell.textField.text);
}
Related
I have two UICollectionViewLayout, one for showing stacked cells and another one to show individual cells on full screen whe they're selected.
I need to animate the cell's subviews to certain positions each time the cell is selected, and then move again those subviews when the cell is deselected.
STEP 1: Cell is selected. Subview is animated from its original position to a new one. No problem here.
STEP 1: Cell is deselected. Subview is animated to another position, BUT the animation starts from the original position, not the new position I indicated in the previous step.
Is there any way to save the modified status, and start the animations from there?
UPDATE
Here's how I change layouts on cell selection (ViewController.m):
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath{
__weak CustomCellCollectionViewCell *cell = (CustomCellCollectionViewCell *)[self.collectionView cellForItemAtIndexPath:indexPath];
// Hide single view
if ([indexPath isEqual:self.exposedItemIndexPath]) {
self.exposedItemIndexPath = nil;
[cell deselectedAnimations];
[self animateCollectionView:NO];
}
// Select single view
else if (self.exposedItemIndexPath == nil) {
self.exposedItemIndexPath = indexPath;
[cell selectedAnimations];
[self animateCollectionView:YES];
}
}
- (void)setExposedItemIndexPath:(NSIndexPath *)exposedItemIndexPath
{
if (![exposedItemIndexPath isEqual:_exposedItemIndexPath]) {
if (exposedItemIndexPath) {
self.audiosContentOffset = self.collectionView.contentOffset;
SelectedLayout *singleLayout = [[SelectedLayout alloc] initWithExposedItemIndex:exposedItemIndexPath.item];
[self.collectionView setCollectionViewLayout:singleLayout animated:YES];
}else{
self.stackedLayout.overwriteContentOffset = YES;
self.stackedLayout.contentOffset = self.audiosContentOffset;
[self.collectionView setCollectionViewLayout:self.stackedLayout animated:YES];
[self.collectionView setContentOffset:self.audiosContentOffset animated:YES];
}
_exposedItemIndexPath = exposedItemIndexPath;
}
}
selectedAnimations moves a UIView inside the cell, and deselectedAnimations moves it somewhere else when the cell is deselected. BUT, deselectedAnimations starts from the UIView's original position, not from where it was left at the end of selectedAnimations.
The cells and its subviews are based on a nib file. It seems to me that, on layout change, the cell is being rendered again following the "instructions" on the nib file, and that's why the subview is animated starting from its original position instead of where i left it on selectedAnimations.
My question is: is there any way to "save" the cell's subviews positions and start deselectedAnimations from there?
I have a collectionView that displays an array of images in a single, horizontal layout. I'm trying to add a UILabel under each cell. I assumed the best/easiest way to do this would be to add a footer view/supplementary view for each cell. Therefore, I'm overriding viewForSupplementaryElementOfKind. I have an array of images names that correlate to the array of images. So I'm just hoping the see the image name as the label for each each cell. However, all labels and names are overlapping on the first cell. Also, the footer view doesn't come into view until I scroll to the last image/cell, so I don't see the overlapping labels on the first cell until I scroll to the end. I know the issue is in my viewForSupplementaryElementOfKind implementation, but I'm not sure how to correct it. Can someone please advise? Thanks in advance!!
- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath {
NSLog(#"viewForSupplementaryElementOfKind");
static NSString *FooterCellIdentifier = #"FooterView"; // string value identifier for cell reuse
FooterViewCell *footerView;
if (kind == UICollectionElementKindSectionFooter) {
NSLog(#"*******Element Kind is a footer!*******");
footerView = [collectionView dequeueReusableSupplementaryViewOfKind:UICollectionElementKindSectionFooter
withReuseIdentifier:FooterCellIdentifier forIndexPath:indexPath];
for (int i = 0; i < [self.imagesArray count]; i++) {
UILabel *testLabel = [[UILabel alloc] initWithFrame:CGRectMake(self.myCollectionView.frame.origin.x/2, self.myCollectionView.frame.origin.y/2, CellWidth, 20)];
//testLabel.text = [self.imageNames objectAtIndex:indexPath.row];
testLabel.text = self.imageNames[i];
[self.myCollectionView addSubview:testLabel];
}
return footerView;
} else {
NSLog(#"*******Element Kind is NOT a footer!*******");
return nil;
}
}
Header and footer views on UICollectionView are only relative to sections.
Since the labels are supposed to identify the images (the model), you should build your UICollectionViewCell with the label and image view inside, as subviews. The easiest way is doing it inside a storyboard, with prototype cells on the collection view.
I´m loading a bunch of countries to a table and allowing the user to delete. The rows directly correspond to the countries array, by using objectAtIndex:indexPath.row and I set the delete button´s tag to the same index.
When I delete the first couple of rows it´s ok but afterwards the wrong rows are getting deleted and the app crashes.
Here's how I create rows:
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell;
NSString *currentText;
// fill in country cell content
if ([tableView tag] == 400) {
cell = [tableView dequeueReusableCellWithIdentifier:#"SettingsCountryCell"];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:#"SettingsCountryCell"];
NSLog(#"is nil");
} // it´s never nil
currentText = [arrSelectedCountries objectAtIndex:indexPath.row];
int currentRow = indexPath.row;
UILabel *countryLabel = (UILabel *)[cell viewWithTag:401];
countryLabel.text = currentText;
// create button
UIButton *countryButton = (UIButton *)[cell viewWithTag:402];
[countryButton setTag:currentRow];
[countryButton setTitle:[NSString stringWithFormat:#"row%d", currentRow]
forState:UIControlStateNormal];
NSLog(#"%# - tag %d - title %# - button tag %d",
currentText,
currentRow,
countryButton.titleLabel.text,
countryButton.tag);
// on load: Bulgaria - tag 2 - title row2 - button tag 2
// after one deletion (and ever after):
// Bulgaria - tag 1 - title (null) - button tag 0
[countryButton addTarget:self
action:#selector(removeCountry:)
forControlEvents:UIControlEventTouchUpInside];
}
Here's how I remove them (i.e. countries):
- (void)removeCountry:(UIButton *)countryButton
{
// removed country
NSString *currentText = [arrSelectedCountries objectAtIndex:countryButton.tag];
NSLog(#"removed country %# at tag %d", currentText, countryButton.tag);
// ok: removed country All CEE Countries at tag 0
// ok: removed country Bulgaria at tag 0
// not ok: removed country Czech Republic at tag 1
// add to picker selection and remove from selected
[arrCountries addObject:currentText];
[arrSelectedCountries removeObject:currentText];
// refresh picker and country table
[countryPicker reloadAllComponents];
[countryTable reloadData];
[countryTable reloadSections:[NSIndexSet indexSetWithIndex:0]
withRowAnimation:UITableViewRowAnimationFade];
// position table and elements below it
[self repositionForCountryChange];
}
At first the table is showing: row0, row1, row2, etc.
After a couple of deletions: row1, row2, row3, etc.
Once this happens, the label is no longer corresponding properly to the tag. When I click delete on Croatia, delete button labeled row 1, NSLog says:
removed country Czech Republic at tag 1
... and Croatia row is not deleted although its delete button now says row2.
And why am I getting from NSLog:
title (null) - button tag 0
...when I can see in the table that there is a title and removeCountry can access button tag, although incorrectly?
You have set up the button with a tag to allow you to identify it as a subview in the cell (402) you then change the tag so it identifies the row the tag is in. When the cell is re-used, there is no view with tag 402, so your button is still identified with a tag number of its original row.
You're using tags for two purposes. Ideally, don't use them at all as they are fragile and do not make your code readable.
create a UITableViewCell subclass and identify its subviews using outlets
identify the row of the pressed button using the method I describe here which is much more flexible than tags and keeps working if you delete rows.
In a UITableView I add a UIView as subview but ONLY for section 1. Section 1's content is loaded from a plist and the plist contains mutable content. If there are enough rows to allow scrolling, then the following happens: I scroll to the bottom, and back up, and the UITextField appears randomly on some of section 0's cells. I have no clue why this is happening! So what i do is this (in ´cellForRowAtIndexPath´):
if (indexPath.section == 0) {
//do stuff
}
else if (indexPath.section == 1) {
d = [UIView alloc] init];
[cell.contentView addSubview:d];
}
and this gets totally messed up when I scroll. The subviews appear in section 0 where they shoudnt, and on didSelectRowAtIdexPath I reload for section 1 and then subviews even appear twice (over each other)... Its a complete MESS! Please, Please help.......
Without seeing any code this seems to be an issue pertaining to reusable cells. What happens is that the cells that have scrolled off the screen are reused for the new content that is to be shown. So i reckon you need to make a distinction in cellForRowAtIndexPath for section 0 and 1 and basically use different set of cells for them.
EDIT: Ok ima give a shot to your problem here
UITableViewCell *cell;
if (indexPath.section == 0) {
cell = [tableView dequeueReusableCellWithIdentifier:#"CellWithoutSubview"];
if (cell ==nil ) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewStylePlain reuseIdentifier:#"CellWithoutSubview"] autorelease];
}
//do stuff with cell like set text or whatever
}
else if (indexPath.section == 1) {
cell = [tableView dequeueReusableCellWithIdentifier:#"CellWithSubview"];
if (cell ==nil ) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewStylePlain reuseIdentifier:#"CellWithSubview"] autorelease];
d = [[UIView alloc] init];
[cell.contentView addSubview:d];
[d release];
}
}
return cell;
So now you'll have two types of cells for the tableview that'll be reused one without the subview and one with the subview.
You must be using dequeueReusableCellWithIdentifier. The purpose of dequeueReusableCellWithIdentifier is to use less memory. If the screen can fit 4 or 5 table cells, then with reuse you only need to have 4 or 5 table cells allocated in memory even if the table has 1000 entries.
So the subviews in UITableViewCell are also cached. So when the cell is reused, you need to clean out the old view & then put in the new content.
UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier: #"your-id"];
if (cell)
{
//reusing old cell. reset all subviews before putting new content.
}
else
{
//Create a fresh new cell
}
You should use switch instead:
switch ( indexPath.section )
{
case 0:
{
/* do soemthing */
}
break;
case 1:
{
d = [UIView alloc] init];
[cell.contentView addSubview:d];
}
break;
}
In my app I change the value of a text label from an initial value of 0 and increment it on a touch event. Sometimes, but not all the time, the new value will be overlayed over the 0, which is not cool..
This is the relevant code:
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
UILabel *label = (UILabel *)[cell viewWithTag:1000];
label.text = qtyString;
I've tried removing the label from the view, then adding another with the new value, but it didn't affect the problem at all. When I scroll the cell (the labels are part of a table cell) out of the screen and back in, the labels display correctly. Oh, and I've also tried doing
[tableView reloadData];
And it works better, but if I select a cell and then scroll while it is higlighted it poops out on that cell.
Please help :(
check the "clears graphics context" works for me, i carelessly deselected it at first~~
Under your UILabel options UNCHECK 'opaque', that will fix it.
I had the same problem with UILabel's text getting all jumbled, I just read the other solutions, went to IB, then checked "Clear context before drawing", and that solved it!
Changing opaque doesnt change it altho it would have been logic. What you can do if giving your label an exact height .. This height will match your well height. If you are using
(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 80; //returns floating point which will be used for a cell row height at specified row index
}
Like i did then make the height of the label 80 as well that will stop it ..
imho, the best way to go around this is to add an else to:
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease];
}else {
NSArray *cellSubs = cell.contentView.subviews;
for (int i = 0 ; i < [cellSubs count] ; i++) {
[[cellSubs objectAtIndex:i] removeFromSuperview];
}
}
Hope this helps.
In my case, changing Opaque to NO solve the problem of updating the UILabel.text
I create the uilabel in the awakeFromNib.