[self.tableView reloadData] not updating numberOfRowsInSection when called.
Delegate and datasource set programmatically in viewDidLoad
Also, put in a UIRefreshControl even though I'm using the tableView in a normal UIViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
UIRefreshControl *refreshControl = [[UIRefreshControl alloc]init];
[refreshControl addTarget:self action:#selector(refresh:) forControlEvents:UIControlEventValueChanged];
self.tableView = [[UITableView alloc]initWithFrame:self.view.bounds];
self.tableView.dataSource = self;
self.tableView.delegate = self;
[self.view addSubview:self.tableView];
[self.tableView addSubview:refreshControl];
}
Refresh Control calls location object to get location. Self gets notified when location object has received the location. Notification calls selector getLocation to make API call with location as parameters
- (void)refresh:(UIRefreshControl *)refreshControl {
[self.loadTableArray removeAllObjects];
LPLocationManager *locationObject = [LPLocationManager sharedManager];
[locationObject.locationManager startUpdatingLocation];
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
[center addObserver:self selector:#selector(getLocation:) name:#"locationNotification" object:nil];
[refreshControl endRefreshing];
}
GET request parameters sent to API request object and request object calls method on self that loads mutableArray. MutableArray is copied to NSArray and [self.tableView reloadData] is called but numberOfRowsInSection doesn't update.
-(void)getLocation:(NSNotification*)notifications{
self.latitude = notifications.userInfo[kSetLat];
self.longitude = notifications.userInfo[kSetLong];
self.requestObject = [[LPRequestObject alloc]init];
[self.requestObject getLocationMediaWithLat:self.latitude andLong:self.longitude];
}
-(void)updateMutableArray:(NSArray*)array{
self.loadTableArray = [[NSMutableArray alloc]init];
for (NSDictionary *dictionary in array) {
[self.loadTableArray addObject:dictionary];
}
self.loadTableArrayCopy = [self.loadTableArray copy];
[self.tableView reloadData];
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return [self.loadTableArrayCopy count];
}
The tableView is added to a subview in a custom container view with code to achieve in mainContentVC
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
self.locationViewController = [[LPLocationViewController alloc]init];
self.locationViewController.view.frame = self.view.bounds;
self.currentViewController = self.locationViewController;
[self addChildViewController:self.currentViewController];
[self.currentViewController didMoveToParentViewController:self];
[self.containerView addSubview:self.currentViewController.view];
self.diaryViewController = [[LPDiaryViewController alloc]init];
self.diaryViewController.view.frame = self.view.bounds;
self.settingsController = [[LPSettingsViewController alloc]init];
self.settingsController.view.frame = self.view.bounds;
if ((self.currentViewController = self.locationViewController)) {
self.locationButton.enabled = NO;
}
}
Related
I am trying to implement a UISearchController in a TableViewController. After i enter the text in the search bar I am getting the count in the console of how many records to display but the tableview is not getting reloaded. Please find the code for the Update Search Results Controller.
- (void)viewDidLoad {
[super viewDidLoad];
self.searchController = [[UISearchController alloc] initWithSearchResultsController:nil];
self.searchController.searchResultsUpdater = self;
self.searchController.dimsBackgroundDuringPresentation = NO;
self.searchController.searchBar.delegate = self;
self.tableView.tableHeaderView = self.searchController.searchBar;
self.definesPresentationContext = YES;
_db=[[Database alloc]init];
if(_vehicles==nil){
_vehicles=[[NSMutableArray alloc]init];
}
_vehicles=[_db getTheData:nil];
NSLog(#"%lu",(unsigned long)_vehicles.count);
}
- (void)updateSearchResultsForSearchController:(UISearchController *)searchController
{
NSString *searchText = searchController.searchBar.text;
NSMutableArray<Vehicle*> *searchResults = [[NSMutableArray<Vehicle*> alloc]init];
for(Vehicle *v in self.vehicles){
if([v.make containsString:searchText]){
[searchResults addObject:v];
}
}
NSLog(#"%lu",searchResults.count);
VehicleTableViewController *tableController = (VehicleTableViewController *)self.searchController.searchResultsController;
tableController.vehicles = searchResults;
[tableController.tableView reloadData];
}
VehicleTableViewController *tableController = (VehicleTableViewController *)self.searchController.searchResultsController;
This line is wrong I think. searchResultsController is not tableviewctrler. You cannot force it to be one. You already have tableView reference so use that.
[self.tableView reloadData];
Make sure you replace the Table datasource with the searchresult before reloading the table.
[dataSourceArr removeAllObjects];
[dataSourceArr = [NSMutableArray arrayWithArray: searchResults];
My code is:
- (instancetype)init {
self = [super initWithNibName:nil bundle:nil];
if (self) {
_tableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 20, self.view.frame.size.width, self.view.frame.size.height - 20)
style:UITableViewStylePlain];
for (int i = 0; i < 10; i++) {
[[LHItemSharedStore sharedStore] createItem];
}
}
return self;
}
- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
return [self init];
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self.tableView reloadData];
}
- (void)viewDidLoad {
[super viewDidLoad];
self.tableView.delegate = self;
self.tableView.dataSource = self;
[self.view addSubview:self.tableView];
[self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:#"UITableViewCell"];
}
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
return 1;
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return [[[LHItemSharedStore sharedStore] items]count];
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"UITableViewCell" forIndexPath:indexPath];
cell.textLabel.text = #"test";
return cell;
}
As the code show, the datasource methods are not called. but when I put
[self.view addSubView:self.tableView];
In viewWillAppear, the methods can be called correctly, also works when I change [self.view addSubView:self.tableView]; to self.view = self.tableView;
The problem with your code is, that init is probably called after viewDidLoad , thus your table view is still nil when your setting the delegates.
problem solved!
seems that i use self.view.fram in my init method, then loadview is called, then call viewDidLoad, in viewDidLoad the init process of _tableView is not end, so _tableView is nil. if I set self.view = self.tableView in viewDidLoad, it will be called util self.tableView is not nil.
benifit i get it is that put view init into viewDidLoad.
I have a problem with my tableview.
It has a space on top like this:
When I open the TasksTableViewController, the problem doesn't show. But when I open another viewcontroller from TaskTableVC like this:
FilterTasksTableViewController * fttvc = [self.storyboard instantiateViewControllerWithIdentifier:#"FilterTasksTableViewController"];
fttvc.delegate = self;
UINavigationController * navVC = [self.storyboard instantiateViewControllerWithIdentifier:#"PopoverNavigationController"];
[navVC setViewControllers:#[fttvc]];
[self presentViewController:navVC animated:YES completion:nil];
and go back to TaskTableVC, the problem occurs.
When I "pull down to refesh", it goes back to normal.
in my code for TaskTableVC:
- (void)viewWillAppear:(BOOL)animated {
//other code
[self populate];
}
- (void)viewDidLoad {
dispatch_async(dispatch_get_main_queue(), ^{
self.refreshControl = [[UIRefreshControl alloc] init];
self.refreshControl.attributedTitle = [[NSAttributedString alloc] initWithString:#" "];
[self.refreshControl addTarget:self action:#selector(refresh) forControlEvents:UIControlEventValueChanged];
[self setRefreshControl:self.refreshControl];
});
[self populate];
}
- (void)populate {
TaskHandler *handler = [[TaskHandler alloc] initWithContext:_context];
NSArray *allTasks = [handler getAll];
_tasks = [self filterTasks:allTasks];
NSSortDescriptor *descriptor = [[NSSortDescriptor alloc] initWithKey:#"startDate" ascending:NO];
NSArray *descriptors = [NSArray arrayWithObjects:descriptor, nil];
_tasks = [[NSMutableArray alloc] initWithArray:[_tasks sortedArrayUsingDescriptors:descriptors]];
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableView reloadData];
[self.refreshControl endRefreshing];
});
}
I hope you can help me with this weird problem.
I solved my problem with the code.
I thought there was something wrong with the Refresh control so I moved it out of the dispatch_aysnc(dispatch_get_main_queue() and added [self.tableview reloadData . That fixed my problem. Thanks everyone for answering. :)
In view viewWillAppear set .
if ([self respondsToSelector:#selector(edgesForExtendedLayout)]) {
[self setEdgesForExtendedLayout:UIRectEdgeBottom];
}
hope it help you .
You Need to make Sure that your UITableViewCell has correct Height.
For doing that you can simply implement the UITableViewDelegate method :
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 90.0f; //CHANGE TO YOUR DESIRED HEIGHT.
}
I hope this helps.
I am trying to use refreshControl doing everything by Code the problem appears when I pull the tableView and I call the server. It tells me this error :
Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayM objectAtIndex:]: index 6 beyond bounds for empty array'
When I run this simple application I watch the data in the table view. I don't know why I have this problem.
Here is the code:
#property (nonatomic, strong) UITableView *tableView;
#property (nonatomic, strong) NSMutableArray * data;
#property (nonatomic, strong) UIRefreshControl *spinner ;
#end
#implementation YPProjectListViewController
#synthesize tableView;
#synthesize data;
#synthesize spinner;
- (void)viewDidLoad {
[super viewDidLoad];
spinner = [[UIRefreshControl alloc]initWithFrame:CGRectMake(130, 10, 40, 40)];
[self loadProjectsFromService];
[spinner addTarget:self action:#selector(loadProjectsFromService) forControlEvents:UIControlEventValueChanged];
[tableView addSubview:spinner];
}
-(void)loadProjectsFromService{
[spinner beginRefreshing];
self.data = [[NSMutableArray alloc] init];
[self.view addSubview:self.tableView];
__weak typeof(self) weakSelf = self;
successBlock = ^(NSMutableArray *newData) {
if ([newData count] > 0) {
[weakSelf refreshData:newData];
}
};
[spinner endRefreshing];
[ypNetManager getProjectListWithSuccessBlock:successBlock error:NULL];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - Custom getter
- (UITableView *)tableView {
//custom init of the tableview
if (!tableView) {
// regular table view
tableView = [[UITableView alloc] initWithFrame:UIEdgeInsetsInsetRect(self.view.bounds, tableViewInsets) style:UITableViewStylePlain];
tableView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
tableView.delegate = self;
tableView.dataSource = self;
tableView.backgroundColor = [UIColor clearColor];
return tableView;
}
return tableView;
}
#pragma mark - Private methods
- (void)refreshData:(NSMutableArray *)newData {
NSLog(#"data %#", newData);
self.data = newData;
[self.tableView reloadData];
}
I don't think there is anything wrong with your fetching data from server. The only thing you might be doing incorrectly is not to refresh the tableView when you reinitialize self.data.
When you pull down and release table view needs to display the 6th tableView cell which went out of viewport and your cell needs 6th object from your data but your data is not present anymore.
just insert the following.
-(void)loadProjectsFromService{
[spinner beginRefreshing];
self.data = [[NSMutableArray alloc] init];
[self.tableView reloadData]; //Insert table reload here.
//... rest of your code ...
}
I'm using the following code to create a UIRefreshControl:
- (void) viewDidLoad
{
[super viewDidLoad];
UIRefreshControl *refreshControl = [[UIRefreshControl alloc] init];
[refreshControl addTarget:self action:#selector(doLoad) forControlEvents:UIControlEventValueChanged];
self.refreshControl = refreshControl;
}
- (void) doLoad
{
dispatch_async(dispatch_get_global_queue(0, 0), ^{
// Instead of sleeping, I do a webrequest here.
[NSThread sleepForTimeInterval: 5];
dispatch_async(dispatch_get_main_queue(), ^{
[tableView reloadData];
[self.refreshControl endRefreshing];
});
});
}
It works great. If I navigate to my view, drag the table, the code runs and the data displays.
However, what I would like to do is have the view in the 'loading' state as soon as it appears (that way the user knows something is going on). I have tried adding the following:
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[self.refreshControl beginRefreshing];
}
But it does not seem to work. When I navigate to the view, it looks like a regular view (refresh control is not visible), plus when I try to pull the refresh control, it never finished loading.
Obviously I'm going about this the wrong way. Any suggestions on how I should handle this?
Try this:
- (void) viewWillAppear: (BOOL) animated
{
[super viewWillAppear: animated];
self.tableView.contentOffset = CGPointMake(0, -self.refreshControl.frame.size.height);
[self.refreshControl beginRefreshing];
// kick off your async refresh!
[self doLoad];
}
Remember to call endRefreshing at some point!
EDIT to add full working sample:
This sample view controller, built and run in iOS6.1 as the root viewcontroller starts with the UIRefreshControl already visible and animating when the app launches.
TSTableViewController.h
#interface TSTableViewController : UITableViewController
#end
TSTableViewController.m
#import "TSTableViewController.h"
#implementation TSTableViewController
{
NSMutableArray* _dataItems;
}
- (void) viewDidLoad
{
[super viewDidLoad];
self.refreshControl = [UIRefreshControl new];
[self.refreshControl addTarget: self
action: #selector( onRefresh: )
forControlEvents: UIControlEventValueChanged];
}
- (void) viewWillAppear:(BOOL)animated
{
[super viewWillAppear: animated];
self.tableView.contentOffset = CGPointMake(0, -self.refreshControl.frame.size.height);
[self.refreshControl beginRefreshing];
[self onRefresh: nil];
}
- (void) onRefresh: (id) sender
{
double delayInSeconds = 2.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
_dataItems = [NSMutableArray new];
for ( int i = 0 ; i < arc4random() % 100 ; i++ )
{
CFUUIDRef uuid = CFUUIDCreate( NULL );
[_dataItems addObject: CFBridgingRelease(CFUUIDCreateString( NULL, uuid)) ];
CFRelease( uuid );
}
[self.refreshControl endRefreshing];
[self.tableView reloadData];
});
}
#pragma mark - Table view data source
- (NSInteger) numberOfSectionsInTableView: (UITableView *) tableView
{
return 1;
}
- (NSInteger) tableView:(UITableView *) tableView numberOfRowsInSection: (NSInteger) section
{
return _dataItems.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle: UITableViewCellStyleDefault
reuseIdentifier: nil];
cell.textLabel.text = [_dataItems objectAtIndex: indexPath.row];
return cell;
}
#end
Manually modifying the contentOffset is insecure and wrong and can lead to unexpected behavior in some cases. This solution works without touching the contentOffset at all:
func showRefreshControl(show: Bool) {
if show {
refreshControl?.beginRefreshing()
tableView.scrollRectToVisible(CGRectMake(0, 0, 1, 1), animated: true)
} else {
refreshControl?.endRefreshing()
}
}
Another option is fire a UIControlEventValueChanged in your viewDidAppear: to trigger an initial refresh.
- (void) viewDidAppear: (BOOL) animated
{
[super viewDidAppear: animated];
UIRefreshControl *refreshControl = [[UIRefreshControl alloc] init];
[refreshControl addTarget:self action:#selector(doLoad) forControlEvents:UIControlEventValueChanged];
self.refreshControl = refreshControl;
[self.refreshControl beginRefreshing];
}
You never set self.refreshControl