I've been banging my head against the wall since I thought I tried everything possible on stackoverflow.
So currently I am creating a table like this
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath: (NSIndexPath *)indexPath {
NSLog(#"CHECK IF THIS IS CALLED");
static NSString *CellIdentifer = #"CellIdentifier";
CBPeripheral *placeHolder;
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:CellIdentifer];
// Using a cell identifier will allow your app to reuse cells as they come and go from the screen.
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifer];
}
cell.selectionStyle = UITableViewCellSelectionStyleNone;
placeHolder = _connectedPeripherals[indexPath.row];
cell.textLabel.text = placeHolder.name;
}
return cell;
}
and I am calling [self.tableView reloadData] from another function that I call. I know it's getting called in that function, but cellForRowAtIndexPath does not get called again.
I created a UITableView property on my .h file
#property (strong, nonatomic) UITableView *tableView;
And created the table like this
self.tableView = [[UITableView alloc] init];
self.tableView.rowHeight = 60.0f;
self.tableView.delegate = self;
self.tableView.dataSource = self;
self.tableView.tableFooterView = [[UIView alloc] initWithFrame:CGRectZero];
[self.view addSubview:self.tableView];
[self.view addConstraints:#[ [HTConstraints leftAlignView:self.tableView
toView:self.view
withSpacing:5],
[HTConstraints topAlignView:self.tableView
toView:self.view
withSpacing:5],
[HTConstraints rightAlignView:self.tableView
toView:self.view
withSpacing:5],
[HTConstraints bottomAlignView:self.tableView
toView:self.view
withSpacing:5],
]];
So far what I've tried was making sure that [self.tableView reloadData] was being called in the Main thread. Making sure my sections did not return 0.
Edit
This is my reloadData function and is called in another class
by [[RootViewController sharedInstance] reloadData]; and the log prints when I call it.
- (void)reloadData {
NSLog(#"Reloading");
[self.tableView reloadData];
}
set a break point inside -(void)reloadData or try to append the count of your UITableView datasource in your NSLog.
Usually cellForRowAtIndexPath is not getting call because it does not have anything to display.
I saw, in your code you forgot to add your tableView as a subView to the controller.
Add below code after your tableView's initialization
[self.view addSubview:tableView];
Related
I have two ViewControllers:
An UIViewController with a "push" button, Another UIViewController with a tableView.
PushAction
- (IBAction)pushViewController:(id)sender {
NSArray *dataArr = #[#1,#2,#3,#4,#5];
NextViewController *nextViewController = [[NextViewController alloc] init];
[nextViewController setDataAndReload:dataArr];
[self.navigationController pushViewController:nextViewController animated:YES];
}
NextViewController.h
#interface NextViewController : UIViewController
- (void)setDataAndReload:(NSArray *)dataArr;
#end
NextViewController.m
#import "NextViewController.h"
#interface NextViewController () <UITableViewDelegate, UITableViewDataSource>
#property (nonatomic, strong) UITableView *tableView;
#end
#implementation NextViewController {
NSMutableArray *_arr;
}
- (void)viewDidLoad {
[super viewDidLoad];
[self.view addSubview:self.tableView];
[self.tableView reloadData];
}
- (void)setDataAndReload:(NSArray *)dataArr {
//[self loadViewIfNeeded];
_arr = [dataArr mutableCopy];
[self.tableView reloadData];
}
#pragma mark - data source
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return _arr.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellIdentifier = #"CellIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
}
cell.textLabel.text = [NSString stringWithFormat:#"Cell %#", _arr[indexPath.row]];
return cell;
}
#pragma mark - delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[_arr removeObjectAtIndex:indexPath.row];
[self.tableView reloadData];
}
- (UITableView *)tableView {
if (!_tableView) {
_tableView = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStyleGrouped];
_tableView.delegate = self;
_tableView.dataSource = self;
_tableView.backgroundColor = [UIColor whiteColor];
}
return _tableView;
}
You have noticed that I called [self.tableView reloadData] before tableView was added to any view
Then, when I select any cell on the tableView, which will call [self.tableView reloadData] after selected, the tableView doesn't reload as expected.
After debugging with adding some breakpoints to tableView's dataSource methods, I found that numberOfRowsInSection: has been called correctly, but cellForRowAtIndexPath has not been called.
Then I think what cause the issue maybe is that setDataAndReload: call [self.tableView reloadData] before I added the tableView to viewController's view. So I added [self loadViewIfNeeded] and cellForRowAtIndexPath: was called correctly.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[_arr removeObjectAtIndex:indexPath.row];
[self.tableView reloadData];
}
Thus, I wonder why cellForRowAtIndexPath can not be called after [self.tableView reloadData] in the tableView's delegate method didSelectRowAtIndexPath:.
I have found what cause cellForRowAtIndexPath can not be called after [self.tableView reloadData]
When I call [self.tableView reloadData] in setDataAndReload: It will call the lazy method of tableView.
As I used self.view.bounds to initialize the table view, an at the first time setDataAndReload: was called, the view has not been loaded. So it will load as below (while the tableView has not been created and returned)
:
loadView -> viewDidLoad -> [self.view addSubview:self.tableView] -> lazy method agian
-> return second tableView
-> self.tableView is the second tableView
-> second tableView is added to superView
-> return first tableView
-> self.tableView is the first tableView
Thus, the tableView that self.tableView finally pointed to, was never added to a superView!
When you are setting your in - (void)setDataAndReload:(NSArray *)dataArr, like you sad, the tableView is not even initialised and not part of the view hierarchy. so i would recommend to rename the method to - (void)setData:(NSArray *)dataArr and reload the tableView in viewDidLoad only.
So here is your the updated code:
- (void)viewDidLoad {
[super viewDidLoad];
[self.view addSubview:self.tableView];
[self.tableView reloadData];
}
- (void)setDataAndReload:(NSArray *)dataArr {
_arr = [dataArr mutableCopy];
}
In didSelectRowAtIndexPath, if the tableView doesn't reloadData.You should check the caller self.tableView, it must have some problem. And you will find the self.tableView has change its pointer to a new tableView. Then you should check your getter method. In getter method, if we want to use some properties, we must be careful.Because sometimes we may be call the getter more than once,Why?. In your example, you try to get the bounds from self.view, and self.view will send message to -(void)loadView; ,which is the lifecycle method of VC, and -(void)loadView will send message to -(void)viewDidLoad; Now, -(void)viewDidLoad call the self.tableView again. Fortuantely, it doesn't cause a loop.
I'm creating UITableViewController without a help from the Storyboard.
I've got the following code:
- (void)viewDidLoad {
[super viewDidLoad];
self.cellReuseIdentifier = #"myViewControllerCellReuseIdentifier";
[self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:self.cellReuseIdentifier];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:self.cellReuseIdentifier forIndexPath:indexPath];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:self.cellReuseIdentifier];
}
}
In this case the cell style is not there. How do I fix that?
I think if you just add the UITableView, set your viewcontroller as the delegate and datasource and that's enough. If you set a breakpoint in cellForRowAtIndexPath, does it trigger?
Not sure what you're doing with,
[self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:self.cellReuseIdentifier];
Rather do this,
self.tableView = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStylePlain];
self.tableView.delegate = self;
self.tableView.dataSource = self;
[self.view addSubview:self.tableView];
Let me know if that's not what you're looking for, good luck.
I am making a UICollectionView control which would look like (fig-1) :
I have added the ability to delete cell by swiping the cells to right.
My problem case - If I delete the last cell by swiping (fig-2) , which will call the following code.
- (void)removeTheCell:(AnyObject *)obj {
// remove the object
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:[self.allObjects indexOfObject:obj] inSection:0];
[self.allObjects removeObjectAtIndex:indexPath.row];
[self.collectionView deleteItemsAtIndexPaths:#[indexPath]];
}
And then add a new cell with different color using following method (fig-4):
- (void)addNewObject:(NSNotification *)notification {
NSDictionary *dict = notification.userInfo;
NSArray *newObjects_a = [dict objectForKey:ALL_OBJECTS];
NSMutableArray *indexArrays = [[NSMutableArray alloc] init];
for (AnyObject *obj in newObjects_a) {
[self.allObjects addObject:obj];
[indexArrays addObject:[NSIndexPath indexPathForItem:[self.allObjects indexOfObject:obj] inSection:0]];
}
[self.collectionView performBatchUpdates:^{
[self.collectionView insertItemsAtIndexPaths:indexArrays];
} completion:nil];
}
The cell that is displayed still looks like the old deleted cell with its last state (fig-4). But i checked the data source it doesn't contain the deleted object. It contains the latest data.
(fig-5)If i change to list layout by selecting the segment control which call the following method:
- (IBAction)SwitchCellFrames:(id)sender {
int selection = ((UISegmentedControl *)sender).selectedSegmentIndex;
isGridView = selection == 0 ? YES : NO;
if (isGridView) {
[self.collectionView setCollectionViewLayout:gridFlowLayout animated:YES];
}else {
[self.collectionView setCollectionViewLayout:listFlowLayout animated:YES];
}
}
layout variables are defined as :
gridFlowLayout = [[UICollectionViewFlowLayout alloc] init];
[gridFlowLayout setItemSize:CGSizeMake(160, 155)];
[gridFlowLayout setMinimumInteritemSpacing:0.0f];
[gridFlowLayout setMinimumLineSpacing:0.0f];
[gridFlowLayout setScrollDirection:UICollectionViewScrollDirectionVertical];
listFlowLayout = [[UICollectionViewFlowLayout alloc] init];
[listFlowLayout setItemSize:CGSizeMake(320, 80)];
[listFlowLayout setMinimumInteritemSpacing:0.0f];
[listFlowLayout setMinimumLineSpacing:0.0f];
[listFlowLayout setScrollDirection:UICollectionViewScrollDirectionVertical];
The collectionView now updates the new cell with the right color (fig-5/fig-6).
I tried [self.collectionView setNeedsDisplay] / [self.collectionView setNeedsLayout] / [self.collectionView reloadData]. These are not causing the UI to redraw itself.
I don't know what is causing the UICollectionView to retain the deleted view. Please Help.
Found a work arround that is working for me in this situation :
I was creating and updating the ui under - (void)awakeFromNib() of my customCell class. So, when the the new cell is added at the location from where a cell was earlier deleted, - (void)awakeFromNib() was not getting called again and a previous copy of cell was being returned.
Therefore, i made the ui update method public and removed it from - (void)awakeFromNib(). Calling UI update method explicitly from cellForItemAtIndexPath solved the problem for me.
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
CustomViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"CollectionCell" forIndexPath:indexPath];
cell.obj = [self.allObjects objectAtIndex:indexPath.row];
[cell handleChangesForLayoutAndPosition];
[cell updateUI];
[cell resetScrollView];
cell.delegate = self;
return cell;
}
When I tap on a cell, didSelectRowAtIndexPath isn't getting called. When I press and hold on a cell, it'll highlight the cell, but as soon as I take my finger off, the highlight will disappear. And when I tap with two fingers - one finger on one cell, one finger on another cell - THEN didSelectRowAtIndexPath finally gets called and the one cell remains highlighted.
I stripped out all my custom table view cell code and turned it into a completely generic table with a stock UITableViewCell. Still the same problem.
Yes, the data source and delegate are set. No, there aren't any weird delegate methods that are calling deselectRowAtIndexPath.
Here's the code for the table view.
self.tableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 75.0f, self.view.frame.size.width, self.view.frame.size.height - 75.0f) style:UITableViewStylePlain];
[self.view addSubview:self.tableView];
self.tableView.dataSource = self;
self.tableView.delegate = self;
self.tableView.indicatorStyle = UIScrollViewIndicatorStyleWhite;
self.tableView.allowsSelection = YES;
self.tableView.separatorColor = [UIColor clearColor];
self.tableView.backgroundColor = _backgroundColor;
And here's the code I stripped the cell down to.
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
cell.textLabel.text = #"Hello!";
return cell;
I've been through all the didSelectRowAtIndexPath discussions on here and tried all the recommended solutions. And I can't find anyone mentioning the problem I'm seeing here, where didSelectRowAtIndexPath DOES get called, it's just very intermittent or you have to use two fingers at once.
Make sure the method is
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
}
In the view controller in .h
#interface YourViewController : UIViewController<UITableViewDelegate>
I'm not sure how to approach this. I'm loading a separate table view controller nib file to my viewcontroller. How do I position it the way I want? Also, Is the below query all I need, or am I missing something? It keeps crashing on me.
- (void)viewDidLoad
{
[super viewDidLoad];
HSTableViewController *tableViews = [[HSTableViewController alloc]initWithNibName:#"HSTableViewController" bundle:nil];
[self addChildViewController:tableViews];
tableViews.view.frame = CGRectMake(0, 0, 100, 100);
//tableViews.view.frame = self.view.bounds;
[self.view addSubview:tableViews.view];
[tableViews didMoveToParentViewController:self];
To add a tableview in code to a view controller wherein that VC will act as both the data source and delegate you would do the following.
- (void)viewDidLoad
{
[super viewDidLoad];
UITableView *tableView = [[UITableView alloc] initWithFrame:self.view.bounds];
tableView.delegate = self;
tableView.dataSource = self;
[self.view addSubview:tableView];
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 1;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellID = #"CellIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellID];
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellID];
}
cell.textLabel.text = #"Hello World";
return cell;
}
And then in your .H file you need to inform the program that you're going to act as both the Delegate and DataSource
#interface NSSViewController : UIViewController <UITableViewDataSource, UITableViewDelegate>
From what I gathered out of your question, that should get you where you need to go. I will edit accordingly if you require further assistance.
Set your UITableView's frame in -viewWillAppear: like this:
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
tableViews.view.frame = self.view.bounds;
}
If anyone else needs this. See this tutorial I found on youtube that helped a lot. Thanks for all the help.
http://www.youtube.com/watch?v=DzedmcFlm1c