UITableView Back Button - ios

I have two UItableViews and the problem i am having is in going back from the second UITableView to the first UITableView. I have applied the back button using:
- (IBAction)BackButtonClicked:(id)sender
{
[self dismissModalViewControllerAnimated:YES];
}
The issue is that it takes me back to the previous UITableView but when I click on the row of that UITableView it returns an empty UITableView where there should be data in it.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
int i = 0;
if(indexPath.row == 0)
{
i = 0;
}
else
{
i = (indexPath.row * 4);
}
NSLog(#"%#", [ParsedData objectAtIndex:i]);
SubList *SubListNib = [[SubList alloc] initWithNibName:#"SubList" bundle:nil];
self->newSubList = SubListNib;
newSubList.folderid = [ParsedData objectAtIndex:i];
[self presentModalViewController:SubListNib animated:YES];
}
The Data gets entered in the Array the first time.
On going to the previous screen and coming back the data does not enter the array.

You should reload first tableview, when you returns from second tableview to first by
[firstTableViewName reloadData];
This method must also be include in backbutton click.

Related

How come my array loads but my tableView will crash with index beyond bounds for empty array?

This is my error:
-[__NSArrayM objectAtIndex:]: index 12 beyond bounds for empty array
I know this error means I'm trying to access an "empty array".
This error only happens in viewX when it is popped back from viewY. When you press 'back button' on navigation bar in viewY and scroll the tableView immediately, it will crash and cause this error. I am using the RETableViewManager to load my tableView.
In viewX's viewDidLoad:
[[RACSignal combineLatest:#[RACObserve(self, record), RACObserve(self, restaurant)]] subscribeNext:^(id x) {
[self setupItems];
}];
in setupItems:
RecordManager *recordManager = [[EZRecordManager alloc] initWithRecord:self.record restaurant:self.restaurant sender:self.navigationController];
self.items = [recordManager items];
self.section = [RETableViewSection section];
[self.items each:^(id data) {
if ([data isKindOfClass:[NSString class]]) {
self.navigationItem.title = (NSString *)data;
} else {
[self registerItem:[data class]];
[self.section addItem:data];
}
}];
[self.manager addSection:self.section];
[self.tableView reloadData];
I NSLogged my array 'self.items'. and this is what logs according to the method:
viewDidAppear - (
"\U5df2\U8a02\U4f4d\Uff0c\U5c1a\U672a\U7528\U9910",
"<REReservationHeaderItem: 0x14015b0b0>",
"<REAttributedStrItem: 0x14015b1b0>",
"<REAttributedStrWithNextItem: 0x140191a70>",
"<REAttributedStrItem: 0x140193f60>",
"<RESpacerItem: 0x140194870>",
"<REAttributedStrWithNextItem: 0x14019ce10>",
"<REAttributedStrItem: 0x140199230>",
"<RESpacerItem: 0x1401a04e0>",
"<REActionItem: 0x14019e490>",
)
The NSLog logs the same array in setupItems so I know the array is still there because self.item is saved as a property:
#property (nonatomic, strong) NSArray *items;
So this algorithm works as expected when I'm loading viewX for the first time, but as soon as I go into another view(viewY) and press the 'back button' on viewY to pop to viewX and then immediately scroll, it crashes with the above error. If I wait for a second (maybe even half a second), viewX will work properly and have no issue. I know this is minor but my PM is stressing that this shouldn't happen. How can I solve this problem?
The method the error occurs in (part of the RETableViewManager library):
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
RETableViewSection *section = [self.mutableSections objectAtIndex:indexPath.section];
RETableViewItem *item = [section.items objectAtIndex:indexPath.row];
UITableViewCellStyle cellStyle = UITableViewCellStyleDefault;
if ([item isKindOfClass:[RETableViewItem class]])
cellStyle = ((RETableViewItem *)item).style;
NSString *cellIdentifier = [NSString stringWithFormat:#"RETableViewManager_%#_%li", [item class], (long) cellStyle];
Class cellClass = [self classForCellAtIndexPath:indexPath];
if (self.registeredXIBs[NSStringFromClass(cellClass)]) {
cellIdentifier = self.registeredXIBs[NSStringFromClass(cellClass)];
}
if ([item respondsToSelector:#selector(cellIdentifier)] && item.cellIdentifier) {
cellIdentifier = item.cellIdentifier;
}
RETableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
void (^loadCell)(RETableViewCell *cell) = ^(RETableViewCell *cell) {
cell.tableViewManager = self;
// RETableViewManagerDelegate
//
if ([self.delegate conformsToProtocol:#protocol(RETableViewManagerDelegate)] && [self.delegate respondsToSelector:#selector(tableView:willLoadCell:forRowAtIndexPath:)])
[self.delegate tableView:tableView willLoadCell:cell forRowAtIndexPath:indexPath];
[cell cellDidLoad];
// RETableViewManagerDelegate
//
if ([self.delegate conformsToProtocol:#protocol(RETableViewManagerDelegate)] && [self.delegate respondsToSelector:#selector(tableView:didLoadCell:forRowAtIndexPath:)])
[self.delegate tableView:tableView didLoadCell:cell forRowAtIndexPath:indexPath];
};
if (cell == nil) {
cell = [[cellClass alloc] initWithStyle:cellStyle reuseIdentifier:cellIdentifier];
loadCell(cell);
}
if ([cell isKindOfClass:[RETableViewCell class]] && [cell respondsToSelector:#selector(loaded)] && !cell.loaded) {
loadCell(cell);
}
cell.rowIndex = indexPath.row;
cell.sectionIndex = indexPath.section;
cell.parentTableView = tableView;
cell.section = section;
cell.item = item;
cell.detailTextLabel.text = nil;
if ([item isKindOfClass:[RETableViewItem class]])
cell.detailTextLabel.text = ((RETableViewItem *)item).detailLabelText;
[cell cellWillAppear];
return cell;
}
Usually when "waiting a little fixes the problem", it's because you have an async problem.
Something to check first :
Make sure your reload code is called when you move back. Maybe your tableview didn't get emptied, but the array did. Moving back would let you scroll the old content (still loaded) but the delegate methods won't be able to create new cells because the array is now empty.
If you wait, your async method does it's job and the array is now full again, which makes everything work fine.
Possible solution :
Empty then reload the tableview in viewWillAppear. This will cause a visual flash of the tableview going empty and then full again. It will also scroll you to the first element. That being said, it's really easy and fast, and with a spinner it will appear much smoother.
Other possible solution :
Keep the data loaded after leaving the page, so when you come back it's still there. You can use anything that will keep the data loaded while in the app. It could be a singleton class that stays instantiated, or save in a database and reload from it (it's much faster than straight up loading from the internet), or anything that you can think of.

Confusion about the para indexPath of cellForRowAtIndexPath in iOS

I'm new to iOS dev so this might be a simple question to you but I check the apple dev guide just could not find it.
I have many UITableViews using one dataSourceDelegate,this is how I create them:
- (void)createSomeTableview:(NSInteger)numberOfTableViews{
tableViewsArray = [[NSMutableArray alloc] initWithCapacity:numberOfTableViews];
for (int i = 0; i < numberOfTableViews; i++) {
UITableView *itemTableView = [[UITableView alloc] initWithFrame:CGRectMake(90, 0, 235, self.view.bounds.size.height) style:UITableViewStylePlain];
[itemTableView setDataSource:self];
[itemTableView setDelegate:self];
[itemTableView registerClass:[KKYItemListCell class] forCellReuseIdentifier:[NSString stringWithFormat:#"%ld", (long)currentSection]];
[tableViewsArray addObject:itemTableView];
[[self view] addSubview:itemTableView];
}
}
I call createSomeTableview when I finish got my data back from my server,then I use a cycle to reload the data of each tableView (I call this in a block of AFNetwoking POST after got the response successfully):
for (int i = 0; i < sectionNum;i++){
currentSection = i;
[[tableViewsArray objectAtIndex:i] reloadData];
}
This is where the disaster came!(T T)There is always a null cell return from the reloading process so I track the reloading process and found a strange thing:
Once I call the method reloadData,the method numberOfRowsInSection is called (I have just one section for each table),after this then go back to call the reloadData of the next Tableview in tableViewsArray.(I used to think the reloadData will call cellForRowAtIndexPath next).
After all the views in tableViewsArray calling numberOfRowsInSection,the method cellForRowAtIndexPath had been called by the last view in tableViewsArray.Well that's fine,it starts to build the data for my tableview~,I do this to specify my tableViews in array:
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
.....
if (tableView == sectionTableView) {
...
} else if (tableView == [tableViewsArray objectAtIndex:(long)currentSection]) {
...//data handle
if (itemsNum == 0) { //itemsNum is the count of current table's items
currentSection--; //if loaded end then go to the next tableView
return cell;
}
itemsNum--;
return cell;
}
return cell;
}
But something wrong with it!!! I got 18 items in the current data from server (itemsNum = 18),and I did get 18 return from numberOfRowsInSection.But when the indexPath.row == 6,the tableView's index is currentSection - 1,which means it's no longer the current tableView (actually it was the next tableView)is calling the method `cellForRowAtIndexPath'.
So (tableView == [tableViewsArray objectAtIndex:(long)currentSection]) is false,method return a nil cell,then crashed.
I'm wondering where does the method 'tableView:cellForRowAtIndexPath' get the para row in indexPath?Isn't the return of 'numberOfRowsInSection'?(not seemed like in my program).

Get the list of selected cells in different sections

The user can select/deselect cells in each section, so to track all the selected cells in the didSelectRowAtIndexPath is not useful:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSLog(#"row = %i, section = %i", indexPath.row, indexPath.section);
}
What i want instead is to save time and resources, so to let the user check/uncheck whatever he wants, and when he click on a button (to go to next view), i need to collect all selected cells in the whole table view. I tried to do like this:
-(IBAction)nextView:(id)sender{
themesChoosed=[self.tView indexPathsForSelectedRows];//tView is the outlet of my UITableView and themesChosed is an NSArray declared in the .h file
for (int i=0; i<=[themesChoosed count]; i++) {
NSIndexPath *thisPath = [themesChoosed objectAtIndex:i];
NSLog(#"row = %i, section = %i", thisPath.row, thisPath.section);
}
}
But this doesn't return me the selected cells, it shows me always: row = 0, section = 0 as if i am only selecting the first cell in the first section.
Any thoughts on how to do so?
Take in .h file
NSMutableArray *selectedArray;
in .m
viewDidLoad
selectedArray=[[NSMutableArray alloc]init ];
in tableviewDidSelect
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
//According to section take value from table and get string selectedString.
if([selectedArray containsObject:selectedString])
{
[selectedArray removeObject:selectedString];
}
else
{
[selectedArray addObject:selectedString];
}
}

IBAction selection of row in TableView

I have a TableView each row will open the same screen but with different locations for the views.
I want the images to open up on each view in a random way.
i have a random method which takes care of that but i am not sure how to call this method upon selection of a row in the TableView - This same method should be called for every row.
At this moment i am randomizing each view with a button - after the view loads i need to press the button and then the images are randomized.
Is there a way to call this method upon a selection of a row in the TableView?
-(IBAction) randomizeLocations:(UIButton*)sender {
for (MyView* myv in rndArry)
{
int x;
x = generateRandomNumber();
while (usedRnd[x] == 1)
{
x = generateRandomNumber();
}
myv.frame = rndArryloc[x];
usedRnd[x] = 1;
}
int i;
for (i=0; i<8; i++)
{
usedRnd[i] = 0;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
switch (indexPath.row) {
case 0:
{
OneViewController* ovc = [[OneViewController alloc] initWithNibName:#"OneViewController" bundle:nil];
[self.navigationController pushViewController:ovc animated:YES];
// ???? WHAT SHOULD I PUT HERE ????
[ovc release];
}
break;
ect....
Thanks for the great and fast answers -
i have read again and again and finally figured out what i needed to do.
As Holger wrote u did call the method in the ViewDidLoad and everything is working now.
Thanks again - This Forum Rules :)

Setting scroll position in UITableView

I have an application that works some what similar to how iPhone's Contact application works. When we add a new Contact user is directed to a view only screen with Contact information. If we select "All Contacts" from the navigation bar, user is navigated to list of all contacts where the recently added contact is in view.
We can move the view to a particular row using:
[itemsTableView selectRowAtIndexPath:indexPath animated:NO scrollPosition:UITableViewScrollPositionBottom];
... but it's not working. I'm calling this right after calling:
[tableView reloadData];
I think I'm not suppose to call selectRowAtIndexPath:animated:scrollPosition method here. But if not here, then where?
Is there any delegate method that gets called after the following method?
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;
I think I've got a similar app: A list of item., tap '+' -> new screen, come back and see the updated list, scrolled to show the added item at the bottom.
In summary, I put reloadData in viewWillAppear:animated: and scrollToRowAtIndexPath:... in viewDidAppear:animated:.
// Note: Member variables dataHasChanged and scrollToLast have been
// set to YES somewhere else, e.g. when tapping 'Save' in the new-item view.
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
if (dataHasChanged) {
self.dataHasChanged = NO;
[[self tableView] reloadData];
} else {
self.scrollToLast = NO; // No reload -> no need to scroll!
}
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
if (scrollToLast) {
NSIndexPath *scrollIndexPath = [NSIndexPath indexPathForRow:([dataController count] - 1) inSection:0];
[[self tableView] scrollToRowAtIndexPath:scrollIndexPath atScrollPosition:UITableViewScrollPositionBottom animated:YES];
}
}
I hope this helps. If you add something in the middle of the list, you could easily scroll to that position instead.
You can try this , my application in some kind of similar to you when i click on a button scroll of uitableview is up.
[tableviewname setContentOffset:CGPointMake(0, ([arrayofcontact count]-10)*cellheight) animated:YES];
10 is for how many cell you want to scroll up
Hope this will help you:-)
Scrolling animation is inevitable when offset is set from viewDidAppear(_:). A better place for setting initial scroll offset is viewWillAppear(_:). You'll need to force the layout of a table view, because it's content size is not defined at that point.
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
tableView.setNeedsLayout()
tableView.layoutIfNeeded()
if let selectedIndexPath = selectedIndexPath, tableView.numberOfRows(inSection: selectedIndexPath.section) > 0 {
tableView.scrollToRow(at: selectedIndexPath, at: .top, animated: false)
}
}
Do you have enough dummy contacts added to test the scrolling? It seems that only when your tableView is of a certain size the iPhone finds the inputus to scroll.
This is the code that works for me in my project. I use a previous button and therefore I scroll the tableview a little bit further down that it usually would go with UITABLEVIEWSCROLLPOSITION. (my previous button won't work if it can't "see" the previous cell.
Ignore some of the custom method calls.
- (void)textFieldDidBeginEditing:(UITextField *)textField {
//Show the navButtons
[self navButtonAnimation];
DetailsStandardCell *cell = (DetailsStandardCell *)textField.superview.superview.superview;
self.parentController.lastCell = cell;
//Code to scroll the tableview to the previous indexpath so the previous button will work. NOTE previous button only works if its target table cell is visible.
NSUInteger row = cell.cellPath.row;
NSUInteger section = cell.cellPath.section;
NSIndexPath *previousIndexPath = nil;
if (cell.cellPath.row == 0 && cell.cellPath.section != 0) //take selection back to last row of previous section
{
NSUInteger previousIndex[] = {section -1, ([[self.sections objectForKey:[NSNumber numberWithInt:section - 1]]count] -1)};
previousIndexPath = [[NSIndexPath alloc] initWithIndexes:previousIndex length:2];
}
else if (cell.cellPath.row != 0 && cell.cellPath.section != 0)
{
NSUInteger previousIndex[] = {section, row - 1};
previousIndexPath = [[NSIndexPath alloc] initWithIndexes:previousIndex length:2];
}
[self.theTableView scrollToRowAtIndexPath: cell.cellPath.section == 0 ? cell.cellPath : previousIndexPath atScrollPosition:UITableViewScrollPositionTop animated:YES];
}

Resources