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
Related
I'm trying to do something but neither found some example on the web or know if it is possible.
I need to add a table into the first cell of a tbaleview programmatically.
The code gives me problem when I try to set delegate and datasource of the second table (the one in the first table)
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
[[NSBundle mainBundle] loadNibNamed:#"CellaMenu" owner:self options:NULL];
cell = cellaMenu;
cell.selectionStyle = UITableViewCellSelectionStyleNone;
cell.userInteractionEnabled = NO;
tabellaMenu = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStylePlain];
tabellaMenu.dataSource = self;
tabellaMenu.delegate = self;
[cell.contentView addSubview:tabellaMenu];
}
In this way the code loops. If I'don't set the delegate and the dataSource the table appears but I need them to create custom handler.
Any hints?
The cleanest way to do is to have another UITableViewDataSource and UITableViewDelegate compliant object in your view controller, then, set the second table view's delegate and dataSource to this object.
The code is going into loop because tabellaMenu table in the cell has the delegate and datasource set to self. So, it keeps on calling table datasource methods on self and create new cells and enters into loop.
You can either create a separate object(subclass of NSObject) and define the table delegate and datasource methods in that and set it to tabellaMenu's delegate and datasource.
Or you can create a subclass of UITableViewCell and create a table view in that programatically. Define the table's datasource and delegate methods in that. So every table view in the cell will refer to its own cell for datasource and delegate. In addition, you get -(void)prepareForReuse(if the cell is reusable) to reload the table in the cell everytime the main table reloads.
Inside -tableView:cellForRowAtIndexPath:, you could write following code after creating cell.
//if first row & table is not present already
if (indexPath.row == 0 && ![cell.contentView viewWithTag:5]) {
UITableView *tabellaMenu =
[[UITableView alloc] initWithFrame:self.contentView.bounds
style:UITableViewStylePlain];
tabellaMenu.tag = 5;
tabellaMenu.dataSource =tabellaMenuDataSource;
tabellaMenu.delegate = tabellaMenuDelegate;
[cell.contentView addSubview:tabellaMenu];
}
You can get the project from here which i created while learning CoreData.Just click on Add button in navigation bar and Add information and save that. Then you will the data in the Table on Main Screen. Just click on the cell and there is another tableView.
Here is the way:
If you need a tableView only in first cell then you can take it globally instead of initialising in cellForRowAtIndexPath: method as it gets recall every time during scrolling.
UITableView *mainTableView;
UITableView *childTableView;
Now you can easily distinguish both tables in their delegates and datasources methods by comparing their instances.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
if (tableView == mainTableView) {
// Do code for your main tableView
if (indexPath.row == 0) {
[[NSBundle mainBundle] loadNibNamed:#"CellaMenu" owner:self options:NULL];
cell = cellaMenu;
cell.selectionStyle = UITableViewCellSelectionStyleNone;
cell.userInteractionEnabled = NO;
if (!childTableView) {
// Initialise only if it is nil (not initialised yet)
childTableView = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStylePlain];
childTableView.dataSource = self;
childTableView.delegate = self;
}
[cell.contentView addSubview:childTableView];
}
}
else {
// Do code for your child tableView
}
}
The structure of my code is this:
UITableViewController (with one or more)-> Custom UITableviewCell (add the view of)-> UIViewController
Now, to notify an action on the UIViewController to the UITableViewController I have a protocol that follow the inverse flow explained before, but, when I do some action on UIViewController, app crashes because I'm trying to access to a deallocated instance...
I avoid the crash on IBAction on UIViewController in a dirty way: setting a property in the UIViewController as self
How can I solved this leak? This is my code:
UITableViewController:
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
GameTableViewCell *cell;
cell = [tableView dequeueReusableCellWithIdentifier: CellId];
[cell configureWithGame: currentGame];
cell.delegate = self;
return cell;
}
Custom TableViewCell:
-(void)configureWithGame:(Game *)game
{
outcomeController = [[OutcomeViewController alloc] initWithGame:game];
outcomeController.delegate = self;
activeGame = game;
//Adapting outcomeView
CGRect frame = outcomeController.view.frame;
frame.size = self.outcomeView.frame.size;
outcomeController.view.frame = frame;
[[self.outcomeView subviews] makeObjectsPerformSelector:#selector(removeFromSuperview)];
[self.outcomeView addSubview:outcomeController.view];
}
The OutcomeViewController has a property #property (nonatomic, strong) id forceRetain; and it sets in -(void)viewDidLoad in this way:
self.forceRetain = self;
This causes some leaks and I want to solve this issue.
try setting outcomeController property to strongin your cell code.
Moreover, with the code you posted, the OutcomeViewController will be allocated every time you scroll the UITableView. Is this the behavior you want?
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.
I'm struggling with a problem I encountered while trying to create a custom UITableViewCell.
I subclassed UITableViewCell in SGTableViewCell and added it in the prototype cell in the storyboard.
As you can see the label is connected
and the cell identifier is set correctly
Then I linked the label to the SGTableViewCell.h like this
#property (strong, nonatomic) IBOutlet UILabel *nameLabel;
and in the .m file I have this code
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
self.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
[self addGestureRecognizer:recognizer];
self.nameLabel = [[UILabel alloc] init];
_checkView = [[UIView alloc] initWithFrame:CGRectNull];
_checkView.backgroundColor = kGreen;
_checkView.alpha = 0.0;
[self addSubview:_checkView];
self.nameLabel.text = #"Hello";
}
return self;
}
But when I use this cell in my tableview using this code
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
Episode *episode = [self.selectedSeason.episodeList objectAtIndex:indexPath.row];
SGTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Episode"];
UIView *selectionColor = [[UIView alloc] init];
selectionColor.backgroundColor = kSelectionGrey;
cell.selectedBackgroundView = selectionColor;
cell.backgroundColor = kBackgroundGrey;
cell.nameLabel.text = episode.name;
NSLog(#"%#", cell.nameLabel.text);
cell.nameLabel.textColor = [UIColor redColor];
return cell;
}
I get no text at all.
I tried logging the text from each label in each cell and it gives me the right text.
I tried setting programmatically a different disclosure indicator for the custom cell and it did change so everything is allocated and working but label is not displaying.
I honestly have no idea of what's the problem. Did I miss something?
Thank you
PARTIALLY SOLVED:
OK i tried doing the same thing on an empty project and everything worked flawlessly so I checked again my project and found this line
[self.tableView registerClass:[SGTableViewCell class] forCellReuseIdentifier:#"Episode"];
Seeing it was not necessary for the empty project i commented this line and everything started working.
The only problem i have now is that if i don't use this line i can't use the custom cell as was intended. In fact my custom cell is swipable using a pan gesture recognizer but without registering my custom class to the tableview seems like the swipe doesn't work.
Sorry for the trouble, seems like i messed up again :/
You shouldn't alloc init a label that you created in the storyboard, it is already allocated automatically. When you do self.nameLabel = [[UILabel alloc] init];, you reset the self.nameLabel property to point to a new empty memory location and not to the label created in the storyboard, hence you can change its text property and see the result in NSLog but not in the storyboard because it doesn't refer to that label in the storyboard.
Try removing all initialisation from the initWithStyle method (to make sure nothing is covering it such as that subview you create), and everything related to the label in the cellForRowAtIndexPath method (same reason), and try a simple assignment like self.nameLabel.text = #"Test text" in the cellForRowAtIndexPath method, it should work. Then add all your other initialisation.
And yeah, don't forget to input your cell reuse identifier "Episode" in the storyboard.
Make sure you:
Have linked the delegate and the datasource to the view the tablview is housed in.
Have that view implement UITableViewController and UITableViewDelegate (I'm pretty sure it is both of those).
Implement the necessary methods, which you seem to have done. You need the row size, section size, and the add cell methods
After updating the array linked to your tableview, call [tableView reloadData]
Have a look at this link:Tutorial to create a simple tableview app
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).