How to initialize UITableView before viewDidLoad? - ios

I have to initialize my UITableView instance within viewDidLoad, such as:
tableView1 = UITableView(frame: frame)
However, when I tried to access the view controller that includes the tableView1, the UITableViewDataSource's tableView: numberOfRowsInSection: is called before the viewDidLoad method, and within the numberOfRowsInSection:, I want to refer to the tableView1 like if tableView == tableView1.
However, then how can I initialize the tableView1 before calling viewDidLoad? If I tried to initialize it within viewDidLoad but still refer to it from within numberOfRowsInSection:, then it causes the error: fatal error: unexpectedly found nil while unwrapping an Optional value because it is not initialized yet and hence its value is nil.
I think if I use storyboard and #IBOutlet, I don't have the problem. But now I use them all from my code, so I'm not sure how I can cope with the issue.
UPDATE
If this cannot be done as posted by #coolcracker, then how can I port this Objective-C code?
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
id <NSFetchedResultsSectionInfo> sectionInfo;
if (tableView == _tableView1) {
sectionInfo = _frc1.sections[section];
} else if (tableView == _tableView2) {
sectionInfo = _frc2.sections[section];
}
return sectionInfo.numberOfObjects;
}

Don’t use Storyboard at all, just declare and initialise your tableview as follows
#interface ApplicationsPageGroups : UIViewController
{
UITableView* tableView1;
}
- (void)viewDidLoad
{
[super viewDidLoad];
tableView1 = [[UITableView alloc] init];
tableView1.frame = CGRectMake(10, 95, [UIScreen mainScreen].bounds.size.width-20, [UIScreen mainScreen].bounds.size.height-230);
tableView1.delegate = self;
tableView1.dataSource = self;
tableView1.tableFooterView = [[UIView alloc] init] ;
[self.view addSubview:tableView1];
[self getTableData];
}
-(void) getTableData
{
// after you get some array of data to fill the tableview
[tableView1 reloadData];
}
Now override all the UITableView delegate and dataSource methods.

simply add
[tableView1 reloadData];
which call your DataSource methods call again

You could override the view controller initWithFrame or initWithNibOrNil method and initialize the tableViews there.
Also, have you tried initializing the table views BEFORE calling the [super viewDidLoad] method? I have not checked, but it may be possible that the superclass controller is calling the reload method before your initializers, depending on your class hierarchy.

Related

cellForRowAtIndexPath not being called after successful api call

In my app(Using Storyboards FYI) I have a viewController which contains a tableView. My viewController hits an API and returns an array of items that my tableView should populate with. The problem is, after successfully retrieving the array, the reloadData method does not trigger a call to cellForRowAtIndexPath. I've set the delegate and datasource properly, and still have no luck. Here is my code:
in my viewDidLoad:
- (void)viewDidLoad {
[super viewDidLoad];
self.tableView = [[UITableView alloc]init];
self.tableView.dataSource = self;
self.tableView.delegate = self;
[[CCHTTPClient sharedClient] getItemsWithSuccess:^(NSArray *items) {
self.items = items;
[self.tableView reloadData];
} failure:^(NSError *error) {
}];
}
and also in my numberOfRowsInSection:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [self.items count];
}
I've also found that if i did an API call on the previous VC and set the array of the viewController's items before the viewController is pushed, the table populates properly. really unsure about what's going on here. Any help would be gratefully appreciated !
self.tableView should be an outlet if your controller and table view are made in a storyboard. You're creating a different table view in viewDidLoad with alloc init, so you're calling reloadData on that one, not the one you have on screen. Remove that alloc init line, and connect the outlet in IB.
I would suspect this is caused by calling reloadData from a secondary thread. Try this:
[[AGHTTPClient sharedClient] getItemsWithSuccess:^(NSArray *items) {
self.items = items;
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableView reloadData];
});
} failure:nil];

cellForRowAtIndexPath not called but numberOfRowsInSection called

I have a simple problem but I can't understand what is.
I have a UIViewControler (called RootController) that load a UIView (called SecondView) that contain a tableView. The problem is that the UIView call the numberOfSectionsInTableView and the numberOfRowsInSection but don't call cellForRowAtIndexPath and the table view is not displayed.
The code of the RootViewController is:
SecondView *secondView = [[seconddView alloc] initWithFrame:CGRectMake(0, 60, self.view.bounds.size.width, self.view.bounds.size.height)];
[self.view addSubview:secondView];
And the code of the SecondView is:
#interface SecondView () <UITableViewDelegate, UITableViewDataSource>
#property (nonatomic,retain) UITableView *table;
#end
#implementation SecondView
#synthesize table;
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
self.table = [[UITableView alloc] init];
self.table.delegate = self;
self.table.dataSource = self;
[self addSubview:self.table];
}
return self;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
cell.textLabel.text = #"Prova";
return cell;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return 5;
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
Can you help me to find the problem? Thank you.
You need to set the Frame of UITableView
My problem was that I had a simple class doing the implementing of my delegate and data source, but the lifetime of the simple class was too short.
I was doing
MyDataSourceClass* myClass = [[MyDataSourceClass alloc] initWithNSArray:someArray];
tableView.dataSource = self.tableViewDataSource;
tableView.delegate = self.tableViewDataSource;
[tableView reloadData];
// end of function, myClass goes out of scope, and apparently tableView has a weak reference to it
Needed to be doing
self.tableDataSource = [[MyDataSourceClass alloc] initWithNSArray:someArray];
tableView.dataSource = self.tableDataSource;
tableView.delegate = self.tableDataSource;
[tableView reloadData];
// now at the end of the function, tableDataSource is still alive, and the tableView will be able to query it.
Note that the code above is pseudocode from memory. Take from it the concept of "make sure your data source/delegate lives long", but don't copy paste it, because there's other stuff you need to do (like set your frame etc etc).
This could also happen if reloadData is called on a different thread. Make sure it is run on the main thread since all UI stuff has to happen on the main thread.
dispatch_async(dispatch_get_main_queue(),{
self.myTableView.reloadData()
});
In XCode6 same weird problem was occurring to me when "Add Missing Constraints" is applied on tableView on Storyboard to adjust views.
To resolve this issue on Storyboard first clear constraints:
then apply constraints in following fashion:
You can only call the viewcontroller's view AFTER viewDidLoad is called.
You can't interact with self.view in your init method
- (void)viewDidLoad {
[super viewDidLoad];
self.table = [[UITableView alloc] initWithFrame:self.view.bounds];
self.table.delegate = self;
self.table.dataSource = self;
[self addSubview:self.table];
}
In your case, you need to init your tableview with a frame (like a suggested in the code above). Just make sure you add the code in viewDidLoad in your viewController
SecondView *secondView = [[seconddView alloc] initWithFrame:CGRectMake(0, 60, self.view.bounds.size.width, self.view.bounds.size.height)];
[self.view addSubview:secondView];
I have same issue like you:
I have only one method from delegate protocol that called and this is numberOfRowsInSection. Meanwhile I have second method cellForRowAtIndexPath that was not called.
The reason of this behaviour was that my numberOfRowsInSection returned 0 rows and cellForRowAtIndexPath didn't call because it needed to draw 0 cells.
I would comment on Hussain Shabbir's answer, to clarify it, but as I am not yet able to comment, I will post an answer instead.
I had exactly the same issue. numberOfRowsInSection would fire, but cellForRowAt would not. I tore my code apart looking for the reason, but the reason is not in the code.
The solution (for me) was in the storyboard. I had not set constraints for the Table View. Select the table view, click the "Add New Constraints" icon (looks like a square TIE fighter) and set constraints for top, bottm, leading and trailing. Then cellForRowAt will be called and your table will populate.
I hope this helps someone.
This solution was specific for my case but maybe helps someone.
I had the same problem. I was using a computed property instead of stored property; so every time I call the tableView, I was getting a new one.
I had this code:
var tableView: UITableView{
let tableView = UITableView()
tableView.dataSource = self
tableView.delegate = self
tableView.register(SomeCell.self, forCellReuseIdentifier: cellID)
return tableView
}
And this is the fixed code:
lazy var tableView: UITableView = {
let tableView = UITableView()
tableView.dataSource = self
tableView.delegate = self
tableView.register(SomeCell.self, forCellReuseIdentifier: cellID)
return tableView
}()
Delete UITableView and add again in your ViewController

UICollectionView datasource methods not getting called, but are being set in the init

Here is my source code
- (id)initWithCollectionView:(UICollectionView *)collectionView
{
self = [super init];
if (self)
{
self.collectionView = collectionView;
self.collectionView.dataSource = self;
self.collectionView.delegate = self;
[self.collectionView registerClass:[TWNTweetCell class] forCellWithReuseIdentifier:kCell];
self.collectionViewLayout = self.collectionView.collectionViewLayout;
self.tweetArray = #[];
self.tweetTextArray = #[];
self.twitter = [STTwitterAPI twitterAPIOSWithFirstAccount];
}
return self;
}
#pragma mark - CollectionView
#pragma mark DataSource
-(NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
{
return 1;
}
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection: (NSInteger)section
{
return [self.tweetArray count];
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
TWNTweetCell *cell = (TWNTweetCell *)[collectionView dequeueReusableCellWithReuseIdentifier:kCell forIndexPath:indexPath];
NSDictionary *status = [self.tweetArray objectAtIndex:indexPath.row];
NSString *text = [status valueForKey:#"text"];
cell.usernameLabel.text = screenName;
// cell.createdAtLabel.text = dateString;
cell.autoresizingMask = UIViewAutoresizingFlexibleWidth;
UITextView *textView = [self.tweetTextArray objectAtIndex:indexPath.row];
[cell setTweet:text withTweetTextView:textView];
return cell;
}
All the methods don't get interupted at all by breakpoints. The tweets are getting loaded in the log so I know everything else is ok, its just not recognizing the collection view. And yes i've set the
Anyone have any idea whats going on?
It is not your case, it might be helpful for others who will came here having problem with data source methods not being called. It could be assigning data source like:
collectionView.dataSource = MyDataSource()
which is wrong as dataSource is a weak reference so it needs to be stored by some strong reference to be alive after creating it. Added a private property in a ViewController to keep the strong reference, initialising and then assigning it fixes the issue.
A few suggestions:
Do all your UICollectionView setup and configuration in viewDidLoad.
Ensure you calling the create init method from your other class
Your tweetArray is also empty, so if the number of items method is called, it will return nothing and the other methods will not be called
A couple things:
1) in (and only in) your "init" method, use the underlying instance variable for your #property. That is,
_collectionView = collectionView;
_collectionView.dataSource = self;
_collectionView.delegate = self;
This is called "direct access", and more information can be seen in this related question.
2) in your .h file, make certain to declare that your object conforms to the data source & delegate protocols. E.G.
#interface JustinViewController : UIViewController <UICollectionViewDelegate, UICollectionViewDataSource>
for swift do this , set a property
//MARK: props
let dataSource = MyDataSource()
and in
viewDidLoad(){
// your other code
..
..
collectionView.dataSource = dataSource // it is a strong reference
}
apart form these other general pitfall are
not returning the count or the data source
not populating the data source
Add the collectionView to a view hierarchy.
In the init method you set the property (self.collectionView) but you do not add the collectionView to a view hierarchy. So the collectionView won't call any dataSource or delegate method.
I created collection view in storyboard and linked datasource and delegate but they were not being called in Xcode 8.0 with Swift 3.0. Tried multiple things but the solution was to declare the delegate and datasource in class declaration line:
class ViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource {
...
}
Previously when we linked delegate and datasource through storyboard it was not required, may be a bug :)
Call [collectionView reloadData] at the end of your init method. The collection view needs to be told to populate itself. I assume UICollectionViewController does this internally, but you don't seem to be using UICollectionViewController (or at least not in the usual way).

ReloadData for UITableView not working; tableView returns NULL when logged

I am calling a method in my TableViewController class from another class.
To call the method of displaying the tableview, I do this:
TableViewController *tableVC = [[TableViewController alloc]init];
[tableVC setTableViewContent];
then in TableViewController.h
#interface TableViewController : UITableViewController <UITableViewDelegate, UITableViewDataSource>
{
NSMutableArray *nameArray;
}
-(void)setTableViewContent;
#property (nonatomic, strong) IBOutlet UITableView *tableView;
#end
TableViewController.m
#implementation TableViewController
#synthesize tableView;
- (void)viewDidLoad
{
nameArray = [[NSMutableArray alloc]init];
[super viewDidLoad];
}
-(void)setTableViewContent{
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
for(int i=0;i< [appDelegate.businessArray count];i++)
{
NSDictionary *businessDict = [[appDelegate.businessArray objectAtIndex:i] valueForKey:#"location"];
nameArray = [appDelegate.businessArray valueForKey:#"name"];
}
NSLog(#"%#", nameArray);
NSLog(#"tableview: %#", tableView);
// here tableview returns null
[tableView reloadData];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return [nameArray count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"updating tableview...");
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell =[tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
// Configure the cell...
cell.textLabel.text = [nameArray objectAtIndex:indexPath.row];
return cell;
}
For some reason when I try to log the tableview, it returns null, so the ReloadData doesn't work. The delegate and datasource is connected properly in IB, and there is a referencing outlet for tableView.
Any idea what is going on here? Thanks in advance
If you added the table view controller to a container view, then you can get a reference to that controller in prepareForSegue. For a controller in a container view, prepareForSegue will be called right before the parent controller's viewDidLoad, so you don't need to do anything to invoke it. In my example below, I've called the segue "TableEmbed" -- you need to give the segue that identifier in IB.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if([segue.identifier isEqualToString:#"TableEmbed"]) {
TableViewController *tableVC = (TableViewController *)segue.destinationViewController;
[tableVC setTableViewContent];
}
}
Be aware that prepareForSegue:sender: is called before either controller's viewDidLoad is called, so you should move the initialization of your array to setTableViewContent, and your reloadTable should go into viewDidLoad.
BTW, it's not clear to me why you want to call setTableContent from your other class anyway. Why not move all the code in that method to the viewDidLoad method of the table view controller?
This is happening because you are calling a method on tableView before it actually exists. Simply initializing that class doesn't draw the table itself, so using reloadData before the table has actually been created doesn't really make any sense.
What you want to do in this situation is create your nameArray in whatever class is calling setTableViewContent, and then pass it in either via a custom init method, or by setting tableVC.nameArray before loading that table view controller.
What I would do is make custom init method like - (id)initWithArray:(NSMutableArray *)nameArr
Which should look something like this:
if (self = [super init]) {
nameArray = [nameArr copy];
}
return self;
Then where you have TableViewController *tableVC = [[TableViewController alloc]init]; put TableViewController *tableVC = [[TableViewController alloc]initWithArray:theNameArray]; where theNameArray is the content in setTableViewContent (which you are now generating in the same class that calls the table view instead of in the table view itself).
Make sense?
I solved a similar situation by creating a "safe" reload method on the UITableViewController:
- (void)reloadTableViewData
{
if ([self isViewLoaded])
[self.tableView reloadData];
}
According to the docs for isViewLoaded:
Calling this method reports whether the view is loaded. Unlike the view property, it does not attempt to load the view if it is not already in memory.
Therefore it is safe to call reloadTableViewData on the table view controller at any time.

UITableView Delegate and DataSource methods not getting called in a custom view

I created a UIView subclass, which has a tableView as a property.
I create the tableView object in the initWithFrame of the view and set its delegate and dataSource to the custom view.
I also set the scrollEnabled to NO.
and then add it as a subview.
Result: The tableView appears, it doesn't scroll as expected.
BUT the datasource methods are not at all getting called.
the tableView displays nothing.
I added a breakpoint inside cellForRowAtIndexPath but the control never reaches there.
I have declared the UITableViewDataSource and UITableViewDelegate protocols in the interface of the custom view.
Here's the code in initWithFrame:
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Custom initialization
self.frame = frame;
self.indexListView = [[UITableView alloc] initWithFrame:self.bounds];
self.indexListView.delegate = self;
self.indexListView.dataSource = self;
self.indexListView.scrollEnabled = NO;
[self addSubview:self.indexListView];
[self.indexListView reloadData];
if([self respondsToSelector:#selector(tableView:cellForRowAtIndexPath:)])
NSLog(#"YES.. selector is there");
}
return self;
}
What could be the problem ?

Resources