cellForRowAtIndexPath is not called sometimes after calling numberOfRowsInSection on calling reloadData - iphone

I have a simple application where I have a table view & depending on the callback with array I reload the table data using reloadData method. Initially for first 5-10 times of loading this table view works without any problem. After playing with the application for sometimes I noticed that the table view is blank because of the reason that cellForRowAtIndexPath is not called by the framework. I verified that datasource & delegate is set properly & numberOfRowsInSection is returning a valid positive number. Once I see the blank table view then I can see this blank view repeatedly until I exit the application. I do not get any error or warnings in the console. I tried to tweak the array but to no avail.
Here is the code:
- (void)notifyArrayLoaded:(NSArray *)arr {
NSUInteger capacity = [arr count];
_tbDataArray = [[NSMutableArray alloc] initWithCapacity:capacity];
// _tbDataArray is filled with the data from arr
// Reload the table data
[_tableView reloadData];
}
Any clue will be highly appreciated.
Thanks in advance.
Sumit
Hi DeanWombourne, I do not have viewDidUnLoad method but I have viewWillDisAppear method which I am posting it here.
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[UIApplication sharedApplication].statusBarOrientation = UIInterfaceOrientationPortrait;
[[UIApplication sharedApplication] setStatusBarHidden:NO animated:NO];
[self.navigationController setNavigationBarHidden:NO animated:NO];
UIWindow *window = [[UIApplication sharedApplication] keyWindow];
UIView *view = [window.subviews objectAtIndex:0];
view.transform = CGAffineTransformIdentity;
view.transform = CGAffineTransformMakeRotation(0);
view.bounds = CGRectMake(0.0, 0.0, 320, 480);
}
This is the logs of tableView as logged.
[numberOfSectionsInTableView:] [Line 223] <UITableView: 0xa5fc00; frame = (0 0; 320 0); clipsToBounds = YES; layer = <CALayer: 0x4830b0>; contentOffset: {0, 0}>
[tableView:numberOfRowsInSection:] [Line 229] <UITableView: 0xa5fc00; frame = (0 0; 320 0); clipsToBounds = YES; layer = <CALayer: 0x4830b0>; contentOffset: {0, 0}>
<UITableView: 0xa5fc00; frame = (0 0; 320 0); clipsToBounds = YES; layer = <CALayer: 0x4830b0>; contentOffset: {0, 0}>
[numberOfSectionsInTableView:] [Line 223] <UITableView: 0xa5fc00; frame = (0 0; 320 0); clipsToBounds = YES; layer = <CALayer: 0x4830b0>; contentOffset: {0, 0}>
[tableView:numberOfRowsInSection:] [Line 229] <UITableView: 0xa5fc00; frame = (0 0; 320 0); clipsToBounds = YES; layer = <CALayer: 0x4830b0>; contentOffset: {0, 0}>

Make sure you call reloadData on the main thread. If you are loading your data asynchronously using performSelectorInBackground and calling reloadData in that method, you should, instead of doing something like:
[tableViewController reloadData]
What you should be doing is
[tableViewController performSelectorOnMainThread:#selector(reloadData) withObject:nil waitUntilDone:NO]

If reloadData causes numberOfRowsInSection to be called but not cellForRowAtIndexPath, check that the UITableView instance you called reloadData on is the same UITableView that you have displayed in your view, e.g.
NSLog(#"Calling reloadData on %#", self.tableView);
[self.tableView reloadData];
I think you'll find you have two different UITableViews. The puzzle then is working out why...

go to the header file of the viewcontroller and add (IBOutlet) to the tableView member.
then connect throught interfaceb

This may be too late, but anyways this might help.I have faced the same problem.
In short may be "Florian Segginger's" answer was right.
My situation was
1.calling web service asynchronously to get data (pull to refresh)
2.While this was processing, moved to another view controller (pushed)
3.poped back and my app crashed.
So in between steps 2 and 3, I noticed that number of rows and number of sections for table is actually called but cell for row did not.It resumed when I popped back by the time the count of the tablecell changed due to another logic.Handled this by testing, if view is visible in the connection did finish loading since I was reloading table in view will appear .This might be due to cell for row is actually a rendering(UI) action which should run on main thread.

I had the same symptoms too. In my case, the first time I loaded the data (from core data) in viewDidLoad, NSSortDescriptor was used to sort the data.
On the click of a button, the core data was fetched again (this time with changes) and tableView data reloaded. It initially gave me a blank table view after the button was clicked because I forgot to sort the data the second time I fetched it.
Learning points: Remember to call all methods which modify the cell like NSSortDescriptor if you have used them in the beginning!

Related

Can't get scrollsToTop working on iOS7

I'm targeting iOS7 in my latest app, and tapping on the status bar doesn't seem to scroll a tableView or collectionView to the top.
I've set self.tableView.scrollsToTop = true and still nothing happens.
I know Apple significantly changed the status bar in iOS7, but did those changes break the scrollsToTop functionality?
Update
In response to a comment in one of the answers, I tested to ensure that my collection view was indeed the only scrollView on the screen, and it was:
(lldb) po [self.view recursiveDescription]
<UIView: 0x1092ddf0; frame = (0 0; 320 568); autoresize = W+H; layer = <CALayer: 0x109357e0>>
| <UICollectionView: 0x11351800; frame = (0 0; 320 568); clipsToBounds = YES; opaque = NO; autoresize = W+H; gestureRecognizers = <NSArray: 0x10966080>; layer = <CALayer: 0x109623a0>; contentOffset: {0, -64}> collection view layout: <UICollectionViewFlowLayout: 0x10940a70>
| | <UIImageView: 0x10965fa0; frame = (0 564.5; 320 3.5); alpha = 0; opaque = NO; autoresize = TM; userInteractionEnabled = NO; layer = <CALayer: 0x10965ee0>> - (null)
| | <UIImageView: 0x10948f60; frame = (316.5 561; 3.5 7); alpha = 0; opaque = NO; autoresize = LM; userInteractionEnabled = NO; layer = <CALayer: 0x10966030>> - (null)
Update #2
Not sure if it matters, but I'm using a standard iOS7 NavigationController where the navigationBar is transparent and applies a blur to my collection/tableViews as they scroll underneath.
Update #3
Figured it out. Turns out I did have more than one scrollView on the screen. My app has a left drawer menu underneath the main part of the app, and that menu has a tableView for the options. I simply set self.menuTable.scrollsToTop = false and everything worked as expected throughout the rest of the app. Didn't have to implement the scrollView Delegate methods or anything.
Do you have more than one scroll view/table view/collection view on screen? If so, only one of them can have scrollsToTop set to YES, otherwise iOS7 will not scroll any of them to the top.
You can also implement the UIScrollViewDelegate method scrollViewShouldScrollToTop: and return YES if the passed in scroll view is equal to the one that you want to scroll to the top:
- (BOOL) scrollViewShouldScrollToTop:(UIScrollView*) scrollView {
if (scrollView == self.myTableView) {
return YES;
} else {
return NO;
}
}
The short answer is there's nothing different in iOS7. As long as there isn't more than one UIScrollView loaded, your tableView or collectionView will scroll to the top when the user taps the status bar. The key here is loaded; another scrollView doesn't necessarily have to be on screen to conflict with another scrollView that is.
Sliding drawers in the left/right are very popular these days, and this was the reason for my problem. I have a menu containing my navigation options, and these are all held by a UITableView. I had to make sure that I set menuTable.scrollsToTop = false before I could get things working in the other parts of my app.
My problem was that I had a UITextView with scrollsToTop set to YES, so my UITableView wasn't responding to the gesture. In short, make check all other scrollable views.
for others :
Remember that the scroll view you are searching can also be a UIWebView..not just UITableView.
Another important thing is that it's not only about VISIBLE scrollViews, but LOADED scrollviews.
If you don't find the scrollView, you can always
insert UITableView test table, immediately when app is starting, check if it's scroll to top,
and then load more and more views, until the test table stop scrolling to top.
If your table cells are dynamic, remove the following:
- (BOOL)scrollViewShouldScrollToTop:(UIScrollView *)scrollView
{
return YES;
}
Create a new function as follows:
- (void) disableScrollsToTopPropertyOnAllSubviewsOf:(UIView *)view {
for (UIView *subview in view.subviews) {
if ([subview isKindOfClass:[UIScrollView class]]) {
((UIScrollView *)subview).scrollsToTop = NO;
}
[self disableScrollsToTopPropertyOnAllSubviewsOf:subview];
}
}
Call the function above in - (void)viewDidLoad
[self disableScrollsToTopPropertyOnAllSubviewsOf:self.view];
Now enable ScrollsToTop for your table view as follows:
[myTableView setScrollsToTop:YES];
This always works for me:
[self.tableView scrollRectToVisible:CGRectMake(0, 0, 1, 1) animated:YES];

iOS: addConstraints: crashing app

Problem
I can't seem to adopt Auto Layout into my existing project.
Details
I was having the same issue before as this question presentViewController: crash on iOS <6 (AutoLayout) but none of the provided answers were a solution for me: I'm using all storyboard views with no xibs. My 'Use Auto Layout' setting is already turned off and I am using nothing but iOS 6.
My view controller was initially crashing, so I set the constraints to be added with a delay and now my app crashes during any addConstraints: call. Worst part is that it won't tell me why.
Code
I will link my code, but its pretty straight forward.
-(void)addAllConstraints
{
NSDictionary * views = NSDictionaryOfVariableBindings(_memoryImage, _peopleView, _contentHolder, _commentsTableView);
NSArray * constraints = [NSLayoutConstraint constraintsWithVisualFormat:#"V:[_memoryImage]-50-[_peopleView]-0-[_contentHolder]-0-[_commentsTableView]" options:0 metrics:nil views:views];
NSLog(#"Views %#, Constraints %#", views, constraints);
[_peopleView addConstraints:constraints];
[_memoryImage addConstraints:constraints];
[_contentHolder addConstraints:constraints];
[_commentsTableView addConstraints:constraints];
}
App crashes on _peopleView's call to addConstraints. Both the views and the NSLayoutConstraints are successfully created.
Any ideas? Thank you, Happy Holidays.
EDIT:
Adding Crash logs to show that nothing useful is showing:
2012-12-25 10:40:13.936 -----[4955:907] Views {
"_commentsTableView" = "<UITableView: 0x1eb6be00; frame = (0 372; 320 100); clipsToBounds = YES; gestureRecognizers = <NSArray: 0x1e51ce00>; layer = <CALayer: 0x1e51cee0>; contentOffset: {0, 0}>";
"_contentHolder" = "<UIView: 0x1e5c6590; frame = (0 270; 320 112); layer = <CALayer: 0x1e5c27f0>>";
"_memoryImage" = "<UIButton: 0x1e5c4aa0; frame = (0 0; 320 280); opaque = NO; layer = <CALayer: 0x1e5c4b60>>";
"_peopleView" = "<UIView: 0x1f0ceea0; frame = (0 230; 320 50); layer = <CALayer: 0x1f0cf790>>";
Constraints (
"NSLayoutConstraint:0x1e51a880 V:[UIButton:0x1e5c4aa0]-(50)-[UIView:0x1f0ceea0]",
"NSLayoutConstraint:0x1e5ba4e0 V:[UIView:0x1f0ceea0]-(0)-[UIView:0x1e5c6590]",
"NSLayoutConstraint:0x1e51b860 V:[UIView:0x1e5c6590]-(0)-[UITableView:0x1eb6be00]"
)
}
(lldb)
Constraints are supposed to be added to the view that is the superview of the subviews. So, if these objects are in your main view, then you should have (and none of the other addConstraints: lines):
[self.view addConstraints:constraints];
Also, your dictionary, views, should be nil terminated (I don't know whether this is necessary or not. I've noticed in an Apple example that they didn't do this, but the function definition shows it with the nil).

Objective c - proper way to set subview frame so it will fill the screen

I have a viewController inside of a navigationController, the view controller has a tableview.
In viewDidLoad I set the tableview
- (void)viewDidLoad
{
[super viewDidLoad];
// init tableView
CGRect tableFrame = self.view.bounds;
_tableView = [[UITableView alloc] initWithFrame:tableFrame style:UITableViewStylePlain];
_tableView.delegate = self;
_tableView.dataSource = self;
[self.view addSubview:_tableView];
}
The problem with this code is that the table view frame is not correct - the height is 460 and I need it to be 416.
[The iPhone screen height is 480, minus the status bar (20) minus the navigation bar (44) = 416]
So what is the proper way to set the table view so it will fill the screen?
I can think of two ways:
set its frame to = (0, 0, 320, 416)
use: [_tableView setAutoresizingMask:(UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth)];
Don't use magic numbers. Use the resizing flags correctly.
So yes, your 2. approach is correct.
1) Use the superviews bounds._tableView.frame = self.view.bounds;;
2) Set autoresizing flags [_tableView setAutoresizingMask: UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth];
(you did all of this already :)
Number 1 is absolutely the wrong way to do it... what happens if the screen size changes in a future OS / device?
I'm curious why you're not doing this using a nib file, and saving yourself the trouble, but if you must do it in code, set the auto-resizing mask per your option 2.

UIView changing size

I have a view that I'm creating in the loadview like this:
- (void)loadView {
UIView *mainView = [[UIView alloc] initWithFrame:[UIApplication sharedApplication].keyWindow.bounds];
self.view = mainView;
[mainView release];
}
So, if I print the view I got the result:
>
OK, that's what I wanted, 767x1024
The problem is, if I call this method:
[[UIApplication sharedApplication] setStatusBarHidden:NO withAnimation:UIStatusBarAnimationSlide];
and present a modalViewController and then dismiss it, on my previous view controller, printing the method viewDidAppear and viewWillAppear I got this:
viewWillAppear:
[<UIView: 0x4e946a0; frame = (0 0; 768 1024); layer = <CALayer: 0x4e946d0>>]
viewDidAppear:
[<UIView: 0x4e946a0; frame = (0 0; 768 1004); layer = <CALayer: 0x4e946d0>>]
Why the view size is changing by 20? I know that has something to do with the status bar, but can't figure it out.
Thanks.
You are right. Status bar default size is 20 pixel in height. UIViewController automatically adjust child view size as status bar appears. According to apple documentation "When a view controller is displayed on screen, its root view is typically resized to fit the available space, which can vary depending on the window’s current orientation and the presence of other interface elements such as the status bar." — http://developer.apple.com/library/ios/#documentation/uikit/reference/UIViewController_Class/Reference/Reference.html

Custom cell created with IB is hidden after calling reloadSections:withRowAnimation:

so I followed this guide ("The Technique for Static Row Content") to create my own custom UITableViewCell-s that would contain one image.
The following code is excerpt from my tableView:cellForRowAtIndexPath: method:
UIImageView *imageView = (UIImageView *)[imageViewCell viewWithTag:1];
imageView.image = [UIImage imageNamed:imageName];
cell = imageViewCell;
NSLog(#"%#", cell);
...
return cell;
imageViewCell refers to my custom cell created in interface builder. As you can see I'm trying to change image each time.
Everything works fine, but if I use reloadSections:withRowAnimation: on the UITableView, this cell disappears.
Here's console output:
<UITableViewCell: 0x9c68fe0; frame = (0 0; 302 215); autoresize = RM+BM; layer = <CALayer: 0x4b88110>>
<UITableViewCell: 0x9c68fe0; frame = (0 120; 320 102); autoresize = W; layer = <CALayer: 0x4b88110>>
<UITableViewCell: 0x9c68fe0; frame = (-320 120; 320 102); alpha = 0; autoresize = W; layer = <CALayer: 0x4b88110>>
<UITableViewCell: 0x9c68fe0; frame = (-320 121; 320 105); alpha = 0; autoresize = W; layer = <CALayer: 0x4b88110>>
<UITableViewCell: 0x9c68fe0; frame = (-320 120; 320 111); alpha = 0; autoresize = W; layer = <CALayer: 0x4b88110>>
So as you can see it's frame and alpha is changed to weird values and stays like that.
That makes sense, because I'm not initializing it each time again, it's initialized only once after waking up from nib.
How do I reset its attributes to make it visible again? I found method called prepareForReuse, but that didn't work. I need something that would reset alpha and frame to make it appear again.
Solution with loading nib each time
To be more clear about my first approach: I created the table view cell in the nib of view controller. I set up an outlet, so I could use it in tableView:cellForRowAtIndexPath:.
Since the cell's attributes were messed up after animation I figured that recreating that cell would definitely help. The problem was my nib was loaded only once and I (still) don't know how to do something like reinitialization on a view that was initialized by nib file.
So I decided to create a new nib file and load it each time. It's not exactly what I was looking for, but it works. Here's what the code looks like, it's very simple:
[[NSBundle mainBundle] loadNibNamed:#"TestTableCell" owner:self options:nil];
UIImageView *imageView = (UIImageView *)[imageViewCell viewWithTag:1];
imageView.image = [UIImage imageNamed:imageName];
cell = imageViewCell;
imageViewCell = nil; // imageViewCell is still an outlet and setting
// it to nil makes the nib load it again the next
// time - so I'm sure I'll get a new instance.
cell.selectionStyle = UITableViewCellSelectionStyleNone;
Using reloadSections:withRowAnimation: with UITableViewRowAnimationNone actually solves the problem for me, and it still shows animation on adding/deleting cells from section.
I still believe, that this is a bug in UIKit.
The cell is disappearing because something is changing its frame such that it eventually gets drawn off screen.
If the size of the image changes, the image view itself is set to autoresize and the tableview cell's content view does as well, that might cause the cell's frames to migrate depending on the order of which the images load.