Reloading data of tableview - ios

In my firstViewcontroller , I build a tableview.Its data are coming from a data base.In secondviewcontroller I insert data in the database,and I call function from firsviewcontroller to build the dictionnary data as I did in firstviewcontroller to extract data .All data are recovered from database but the tableview can't be reladed.I have no access to cellForRowAtIndexPath even numberofrowsinsection>0
This what I did :
Secondviewcontroller:
//I insert data in database and I instanciate class where my tableview is and call refresh method
first = [[FirstviewController alloc]initWithNibName:#"FirstviewController" bundle:nil];
[first refreshList];
//in Firstviewcontroller
-(void)refreshList{
self.tableview= [[[UITableView alloc] initWithFrame:self.view.bounds] autorelease];
tableview.dataSource = self;
tableview.delegate = self;
NSMutableArray *array = [[NSMutableArray alloc] init];
//I recover my data from data base
IPADAGRIONEActivityList *arrayActivities = [IPADAGRIONEActivity findAll];
if ([arrayActivities length] > 0)
{
for (IPADAGRIONEActivity * oneRec in arrayActivities)
{
[array addObject:oneRec];
}
}
//activities is NSMutablearray that contains all my data
self.activities = array;
//I build dictionnary
[self buildObjectsDictionnary:activities
NSLog(#"self.act%#",self.tableview);
[array release];
[self.tableview reloadData];
}
//numberofrowsinSection:
NSLog(#"rows%d",[[objects objectForKey:[objectsIndex objectAtIndex:section]] count]);
return [[objects objectForKey:[objectsIndex objectAtIndex:section]] ;
//numberOfSection:
NSLog(#"nbre of section%d",[objectsIndex count]);
return [objectsIndex count];}
//CellforRowatInddexPath: It dosen't access to this method
if (cell== nil) {
cell = [[MHCActivityListCell alloc]init];
}
IPADAGRIONEActivity *activite ;
cell.activityCategory.text = [NSString stringWithFormat:#"%#", [activite EMAIL]];
}
}

It looks like self.tableView is not actually visible. You re-initialize it in refreshData but you do not add it as a subview again with [self.view addSubview:self.tableView].
As I said before, IT IS INCORRECT AND BAD USAGE to re-initialize the tableView, but it looks like thats the problem.
cellForRowAtIndexPath does not get called because the tableview is not visible so it doesn't try to display cells.

Related

iOS table view diffable data source and prefetch

What is the correct way of using NSDiffableDataSourceSnapshot and - (void)tableView:(nonnull UITableView *)tableView prefetchRowsAtIndexPaths:(nonnull NSArray<NSIndexPath *> *)indexPaths.
It seems that every time prefetch reloads table view, table view asks for more prefetching, after calling apply snapshot, creating infinite loop.
- (void)reloadViews {
//[self.tableView reloadData];
NSMutableArray *items = [NSMutableArray new];
for (TCHChannel* channel in self.channels) {
[items addObject:channel.sid];
}
if ([items count] == 0) {
return;
}
NSDiffableDataSourceSnapshot<ConversationSectionType*, NSString*> *snapshot =
[[NSDiffableDataSourceSnapshot<ConversationSectionType*, NSString*> alloc] init];
ConversationSectionType *main = [ConversationSectionType new];
main.section = kMain;
[snapshot appendSectionsWithIdentifiers:#[main]];
[snapshot appendItemsWithIdentifiers:items intoSectionWithIdentifier:main];
[self.diffDataSource applySnapshot:snapshot animatingDifferences:NO];
}
And here is prefetch method:
- (void)tableView:(nonnull UITableView *)tableView prefetchRowsAtIndexPaths:(nonnull NSArray<NSIndexPath *> *)indexPaths {
for (NSIndexPath *indexPath in indexPaths) {
TCHChannel *channel = [self channelForIndexPath:indexPath];
NSMutableSet *currentChannelIds = [NSMutableSet new];
for (ConversationListViewModelUpdateOperation *op in self.modelQueue.operations) {
[currentChannelIds addObject:[op channelId]];
}
if ([currentChannelIds containsObject:channel.sid]) {
continue;
}
NSParameterAssert(channel != nil);
ConversationListViewModelUpdateOperation *op = [[ConversationListViewModelUpdateOperation alloc] initWithChannel:channel cache:self.channelViewModelsCache];
op.completionBlock = ^{
dispatch_async(dispatch_get_main_queue(), ^(void){
[self reloadViews];
});
};
[self.modelQueue addOperation:op];
}
}
Model queue is just operation queue:
- (NSOperationQueue*)modelQueue {
if (_modelQueue == nil) {
_modelQueue = [[NSOperationQueue alloc] init];
_modelQueue.maxConcurrentOperationCount = 4;
}
return _modelQueue;
}
Is there a way to use prefetching with diffable data sources without apply asking for more indexes?
EDIT:
So calling reloadData in prefetch methods makes infinite loop.. According to https://andreygordeev.com/2017/02/20/uitableview-prefetching/
WARNING: do not call tableView.reloadData() or
tableView.reloadRows(...) from tableView(_ tableView: UITableView,
prefetchRowsAt indexPaths: [IndexPath]) method! These methods provoke
UITableView to call prefetchRowsAt... and thus lead to infinity loop.
Soo.. how has Apple intended for prefetching to be used with Diffable Data Sources? ... -.-

Tableview is not displaying the NewsFeed using objective-c

News feed are not shown in table viewcontroller.BuyerSocialPage that is linked with Newsfeed Viewcontroller has BuyerSocialPage.h file
#interface BuyerSocialPage : UIViewController <UITableViewDataSource,UITableViewDelegate>
#end
#implementation BuyerSocialPage
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.tableView.delegate=self;
UINib * firstNib = [UINib nibWithNibName:#"BSPFirstCell" bundle:nil];
[self.tableView registerNib:firstNib forCellReuseIdentifier:#"BSPFirstCell"];
UINib * secondNib = [UINib nibWithNibName:#"BSPSecondCell" bundle:nil];
[self.tableView registerNib:secondNib forCellReuseIdentifier:#"BSPSecondCell"];
UINib * thirdNib = [UINib nibWithNibName:#"BSPThirdCell" bundle:nil];
[self.tableView registerNib:thirdNib forCellReuseIdentifier:#"BSPThirdCell"];
UINib * fourthNib = [UINib nibWithNibName:#"BSPFourthCell" bundle:nil];
[self.tableView registerNib:fourthNib forCellReuseIdentifier:#"BSPFourthCell"];
self.view.backgroundColor = [UIColor whiteColor];
[self getBuyerSocialPage];
if (self.revealViewController) {
[_sidebarButton addTarget:self.revealViewController action:#selector(revealToggle:) forControlEvents:UIControlEventTouchUpInside];
[self.view addGestureRecognizer:self.revealViewController.panGestureRecognizer];
}
}
-(void)getBuyerSocialPage {
}
}
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
int row = (int)indexPath.row;
if (row == 0) {
return 324;
}
else if (row == 1)
{
return 152;
}
else if (row == 2)
{
return 152;
}
else
{
return 152;
}
}
#end
After login you will see home screen. From side menu bar at the top select the "news feed" and it should display the news feed.but it is not displaying newsfeeds.Api is running correctly on postman
How I can get news feed in the table view ?
Page Number is missing in your url &pageno=1.
Your url looks like this:
http://api.shoclef.com/api/NewsFeed?user_id=1164
It should be like this:
http://api.shoclef.com/api/NewsFeed?user_id=1164&pageno=1
try this in you API Manager class. Working fine for me.
NSString * url = [NSString stringWithFormat:#"%#NewsFeed?user_id=%I&pageno=1",API_BASE_URL,userID];
Update For Image
Update this code in your cellForRowAtIndexPath function in NewsFeedNew class
NSString *strUrl = [self.images objectAtIndex:indexPath.row].image;
strUrl = [strUrl stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLFragmentAllowedCharacterSet]];
[cell.image sd_setImageWithURL:[NSURL URLWithString:strUrl]];
You need to set tableview delegate and dataSource.
Also put a break point in numberOfRowsInSection method to verify that tableview is set with datasource and delegate.
You need to reload data of tableview when you get response - [self.tableView reloadData];
-(void)getBuyerSocialPage {
NSLog(#"getBuyerSocialPage");
UserDao * profileID = [[DatabaseManager sharedManager]getLoggedInUser];
ApiManager * manager = [ApiManager sharedManager];
[manager socialPageWithProfileID:profileID.userID withCompletionBlock:^(BOOL error, NSDictionary *socialPage) {
// NSMutableArray * details = [[NSMutableArray alloc]init];
for (NSDictionary * temp in socialPage ) {
[self.socialPageArray addObject:temp];
}
[self.tableView reloadData];
if (!error) {
self.profileImages=self.socialPageArray;
}
}];
}
Few things to debug here,
Your API response might be nil or having error check and log appropriate response.
If the server response is correct then get on the main thread and reload the TableView
Set the UITableViewDataSource and UITableViewDelegate to self if you haven't done in storyboard.
- (void)getBuyerSocialPage {
NSLog(#"getBuyerSocialPage");
UserDao *profileID = [[DatabaseManager sharedManager] getLoggedInUser];
ApiManager *manager = [ApiManager sharedManager];
__weak BuyerSocialPage *weakSelf = self;
[manager socialPageWithProfileID:profileID.userID withCompletionBlock:^(BOOL error, NSDictionary *socialPage) { [weak self]
if (error) {
NSLog("Error fetching data");
return;
}
for (NSDictionary *temp in socialPage ) {
[weakSelf.socialPageArray addObject:temp];
}
if ([weakSelf.socialPageArray count] > 0) {
// update UI on the main thread
dispatch_async(dispatch_get_main_queue(), ^{
weakSelf.tableView.reloadData()
}
}
}]
}

UITable won't refresh even with reload called on main thread

I have a main view controller(SecondViewController) with a UITable and a navigation controller. When a navigation bar button is pressed, a menu drops down from the navigation bar on top of the table. This menu is created by adding a view controller as a subview like so:
//SecondViewController.m
self = sortMenu.secondVC;
[self addChildViewController:sortMenu];
[self.view addSubview:sortMenu.view];
[sortMenu didMoveToParentViewController:self];
sortMenu contains a button that changes the order the cells are displayed in by calling a class method of the main view controller.
//SortMenuViewController.m
- (IBAction)sortButtonPressed:(id)sender {
[_secondVC sortButtonPressed:[sender tag]];
}
In sortButtonPressed, it calls a method to make a fetch request with updated sort filter value.
//SecondViewController.m
-(void)sortButtonPressed:(NSInteger)sortDescriptor{
_sortDescriptor = sortDescriptor;
currentPredicate = [NSPredicate predicateWithFormat:#"dataset & %d > 0", 4];
[self fetchResultsUsingSegmentedControlIndex];
}
The fetch request is performed and returns the data in a new order.
//SecondViewController.m
- (IBAction)fetchResultsUsingSegmentedControlIndex
{
NSString* sectionNameKeyPath = nil;
NSArray* sortDescriptors = nil;
NSSortDescriptor *scientificNameDescriptor = [[NSSortDescriptor alloc] initWithKey:#"scientificName" ascending:YES];
NSSortDescriptor *commonNameFirstDescriptor = [[NSSortDescriptor alloc] initWithKey:#"commonNameFirst" ascending:YES];
NSSortDescriptor *commonNameLastDescriptor = [[NSSortDescriptor alloc]
initWithKey:#"commonNameLast"
ascending:YES
selector:#selector(localizedCaseInsensitiveCompare:)];
if (_sortDescriptor == kSortByCommonNameFirst )
{
sortDescriptors = [[NSArray alloc] initWithObjects:commonNameFirstDescriptor, commonNameLastDescriptor, scientificNameDescriptor, nil];
sectionNameKeyPath = #"commonNameFirst";
}
else if (_sortDescriptor == kSortByCommonNameLast )
{
sortDescriptors = [[NSArray alloc] initWithObjects:commonNameLastDescriptor, commonNameFirstDescriptor, scientificNameDescriptor, nil];
sectionNameKeyPath = #"commonNameLast";
}
else if (_sortDescriptor == kSortByScientificName )
{
sortDescriptors = [[NSArray alloc] initWithObjects:scientificNameDescriptor, commonNameFirstDescriptor, commonNameLastDescriptor, nil];
sectionNameKeyPath = #"scientificName";
}
NSError *error;
NSLog(#"current predicate: %#", currentPredicate);
[[self fetchedResultsControllerWithsectionNameKeyPath:sectionNameKeyPath sortDescriptors:sortDescriptors predicate:currentPredicate] performFetch:&error];
[scientificNameDescriptor release];
[commonNameLastDescriptor release];
[commonNameFirstDescriptor release];
[sortDescriptors release];
NSUInteger sectionsCt = [[speciesFetchedResultsController sections] count];
int sum = 0;
for (int i=1; i < sectionsCt; i++){
id <NSFetchedResultsSectionInfo> sectionInfo = [[speciesFetchedResultsController sections] objectAtIndex:i];
NSUInteger numOfObj = [sectionInfo numberOfObjects];
NSLog(#" in section %d number of objects is %lu ", i, (unsigned long)numOfObj);
sum = sum + numOfObj;
}
[_table performSelectorOnMainThread:#selector(reloadData)
withObject:nil
waitUntilDone:NO];
}
When I call fetchResultsUsingSegmentedControlIndex from the main view controller (before dropping down the sort menu), it works correctly. However, when called from sortMenu, numberOfRowsInSection, numberOfSectionsInTableView, and cellForRowAtIndexPath are not called. I have tried to call reloadData on the main thread with performSelectorOnMainThread and also dispatching it to the main queue, but neither works.
I originally created a sort menu by adding a pickerview to the main view controller on pressing the navigation bar button, and my table reloaded correctly. Since creating a separate view controller for the menu (to have greater design control), it doesn't work.
Ended up using delegation.
// SortMenuViewController.h
#import <UIKit/UIKit.h>
#protocol SortMenuViewControllerDelegate <NSObject>
-(void)sortButtonPressed:(NSInteger)sortDescriptor;
-(void)viewButtonPressed:(NSInteger)viewDescriptor;
#end
#interface SortMenuViewController : UIViewController{
}
//SortMenuViewController.m
- (IBAction)changeSort:(id)sender {
[_delegate sortButtonPressed:[sender tag]];
}
//SecondViewController.h
#interface SecondViewController : UIViewController <NSFetchedResultsControllerDelegate, SortMenuViewControllerDelegate>{
}
-(void)sortButtonPressed:(NSInteger)sortDescriptor{
_sortDescriptor = sortDescriptor;
currentPredicate = [NSPredicate predicateWithFormat:#"dataset & %d > 0", dataset];
[self fetchResultsUsingSegmentedControlIndex];
}

Calling UITableView datasource methods beforehand when the view is not present

I have dropped my code into a situation where I need to call UITableView data source methods written in some UIViewController class before a particular view is presented so that the cells get prepopulated and I can set a BOOL that the data in the not present viewController class is valid or not. I may explain it in more detail if required, but I wanted to know if its possible to do that. If yes, then how to do it? .. as a particular set of my code written after [tableView reloadData] is dependent on running the dataSource methods of UITableView. Please throw some light on this, if needs to be handled in a specific thread?
Following is the case where I call reloadData. Note: This is happening in another class when basicFactsViewController's viewWillAppear method has not been called yet:
- (BOOL) isComplete {
dispatch_async(dispatch_get_main_queue(), ^{
[basicFactsViewController.tableView reloadData];
});
return basicFactsViewController.isComplete && selectedVehicleId && selectedMakeId && selectedModelId && selectedYearId && selectedTrimId;
}
Now basicFactsViewController.isComplete is checked in this method:
- (BOOL) isComplete {
[self collectKeyHighlights];
return _isComplete;
}
Now the dictionary "tableCells" in the method below uses the cells population to check whether all features have been completed or not:
- (NSDictionary *) collectKeyHighlights {
NSMutableDictionary *key_highlights_update = [NSMutableDictionary new];
NSMutableDictionary *cell_highlight_update = [NSMutableDictionary new];
if(visible_key_highlights.count == 0) _isComplete = YES;
_isComplete = YES;
__block NSMutableArray *reloadCellAtIndexPathSet = [[NSMutableArray alloc] init];
[visible_key_highlights enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
NSDictionary *feature = (NSDictionary *)obj;
UITableViewCell *cell = [self.tableCells objectForKey:[NSIndexPath indexPathForRow:idx inSection:0]];
if(cell) {
if([cell isKindOfClass:[DRColorSelectionTableViewCell class]]) {
NSInteger selectedIndex = ((DRColorSelectionTableViewCell *)cell).selectedIndex;
NSInteger numberOfSegments = ((DRColorSelectionTableViewCell *)cell).numberOfSegments;
if(selectedIndex > -1 ) {
NSArray *dataValues = [[visible_key_highlights objectAtIndex:idx] objectForKey:#"data_values"];
NSDictionary *colorData;
BOOL reloadCellForIndexPath = NO;
if (numberOfSegments == selectedIndex) {
colorData = #{ #"normalized" : #"user_defined", #"isother" : #YES, #"hexcode":#"#FFFFFF", #"actual":((DRColorSelectionTableViewCell *)cell).otherColorTextField.text};
reloadCellForIndexPath = YES;
}
else{
colorData = [dataValues objectAtIndex:selectedIndex];
}
[key_highlights_update setObject:colorData forKey:[feature objectForKey:#"name"]];
[cell_highlight_update setObject:colorData forKey:[feature objectForKey:#"name"]];
if (![colorData isEqual:[prevSelections objectForKey:[feature objectForKey:#"name"]]]) {
[reloadCellAtIndexPathSet addObject:((DRColorSelectionTableViewCell *)cell).indexPath];
}
//if (reloadCellForIndexPath) {
//}
} else {
_isComplete = NO;
}
} else if([cell isKindOfClass:[DRInputTableViewCell class]]) {
NSString *textInput = ((DRInputTableViewCell *)cell).inputTextField.text;
if([textInput length]) {
[key_highlights_update setObject:[NSString toSnakeCase:textInput] forKey:[feature objectForKey:#"name"]];
[cell_highlight_update setObject:textInput forKey:[feature objectForKey:#"name"]];
}else {
_isComplete = NO;
}
} else if([cell isKindOfClass:[DRPickerTableViewCell class]]) {
NSString *textInput = ((DRPickerTableViewCell *)cell).inputField.text;
if([textInput length]) {
[key_highlights_update setObject:[NSString toSnakeCase:textInput] forKey:[feature objectForKey:#"name"]];
[cell_highlight_update setObject:textInput forKey:[feature objectForKey:#"name"]];
} else {
_isComplete = NO;
}
} else if([cell isKindOfClass:[DRSwitchTableViewCell class]]) {
// send this everytime for now
BOOL isSelected = ((DRSwitchTableViewCell *)cell).toggleButton.selected;
[key_highlights_update setObject:[NSNumber numberWithBool:isSelected] forKey:[feature objectForKey:#"name"]];
[cell_highlight_update setObject:[NSNumber numberWithBool:isSelected] forKey:[feature objectForKey:#"name"]];
}
}
else{
_isComplete = NO;
}
}];
prevSelections = cell_highlight_update;
if ([reloadCellAtIndexPathSet count]) {
[self.tableView beginUpdates];
[self.tableView reloadRowsAtIndexPaths:reloadCellAtIndexPathSet withRowAnimation:UITableViewRowAnimationNone];
[self.tableView endUpdates];
}
return key_highlights_update;
}
Now here since
[tableView reloadData]
is not calling cellForRowAtIndePath:, hence, tableCells is not getting populated, hence, I am always getting _isComplete = NO.
If I understand correctly, there is processing being done when the tableview loads (calls it's dataSource methods) and you want to trigger that early to use its results. Calling [basicFactsViewController.tableView reloadData]; early won't work if the basicFactsViewController hasn't been displayed yet. If basicFactsViewController is a UIViewController and has the default view and the tableView property is a subview of that standard view, then (if I remember correctly) the tableView property will be nil until the basicFactsViewController has been displayed. A shortcut around that is to access the viewController's view property and cause it to initialize (viewDidLoad and all that). You can do that by simply messaging the viewController: [basicFactsViewController view].
If I've been right so far I'm fairly confident that will initialize the tableView property. But I'm not sure if it will cause the table view to load its data. And even if it does work, it's definitely not the best solution to the piece of code you're trying to architect. Apple's design for UIKit has been focused on the model/view/controller pattern and it's easier to go with the flow and do the same. I imagine that you could move the processing that is in the data source methods for the tableView out into another class (or maybe even the same class), and call that method to get everything ready for both the tableView and any other checks that you have, storing the data in dictionaries and arrays in such a way that you can easily load them by index into the tableView when cellForIndex is called.

Push parameter in controller is always null

I'm having a tableview. And in my didselectrow I get the right object that I want to pass to the next controller.
This is my code:
id<NavigateDelegate> strongDelegate = self.delegate;
NSDictionary * dictionary = [_dataArray objectAtIndex:indexPath.section];
NSArray * array = [dictionary objectForKey:#"data"];
POI * poi = [array objectAtIndex:indexPath.row];
NSLog(#"%#", poi);
NSLog(#"Category %#", poi.rank.name);
[tableView deselectRowAtIndexPath:indexPath animated:YES];
DetailsViewController * detailsController = [[DetailsViewController alloc] init];
detailsController.details = [[NSDictionary alloc] initWithObjectsAndKeys:poi.name, #"name", poi.address, #"address", poi.details, #"details", poi.title, #"title", poi.url, #"url", nil];
if ([strongDelegate respondsToSelector:#selector(navigateFromTo:to:)]) {
[strongDelegate navigateFromTo:self to:detailsController];
}
Then in my delegate receiver I push the controller like:
- (void) navigateFromTo:(POITableViewController *)viewController to:(UIViewController *)toController
{
[self.navigationController pushViewController:toController animated:YES];
}
But still my DetailsController his details NSdictionary is null.
What am I doing wrong?
I found the problem. The viewdid load is called before my setter is called.
So I changed the init method like this:
-(instancetype) initWithPoi:(NSDictionary *) poi
{
self = [super init];
if(self)
{
NSLog(#"Init DetailsViewController");
_details = poi;
[self initializeViews];
}
return self;
}
that way the controller has the poi details from the start.

Resources