Uitableviewcells Initial Load View/Display Issue

Swift 4: UITableViewCell does will not display data upon initial load, but will load on second load

The problem is very clear:

observe works asynchronously – the result is returned later – so userCurrentGames is empty when displayUsersGames() is called.

A solution is to move displayUsersGames() into the completion block

...   
ref.child("users").child(current).child("user games").observe(.value, with: {(snapshot) in
if let snapshot = snapshot.children.allObjects as? [DataSnapshot] {
self.userCurrentGames = []
for snap in snapshot {
let gameKey = snap.key

print("SNAPSHOT DATAAA: \(gameKey)")
self.userCurrentGames.append(gameKey)
}
self.displayUsersGames()
}
})

Note:

Force downcast the cell

let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath) as! ProfileGameCell

The code must not crash. If it does it reveals a design error.

UITableView cells not showing on first load

You are not using your numberOfRowInSection properly and are confusing sync and async programming.

numberOfRowsInSection is a synchronous method that needs to return "immediately" the number of rows for that section.
This means that you need to know how many cells are in that section in advance.

Inside of it, instead, you are using a firebase method to get that number, and that method is asynchronous. This means that when you return the number of rows it will always be 0 since the block of code you pass to the observe method will execute only after some time (the time of the http request to be made).

Another problem in your code is also the fact that you don't know how many times numberOfRowsInSection is called by the tableView, so you could end up re-making the same http request over and over (maybe not now, but in some future implementation of yours).

You should find a single place, that is maybe called only once and before the table view is shown, to call that method and fill your array before reloading the tableview.

In order to do that, you can add, in the viewDidLoad the code that handles the fetching of the array of items, than (once it's completed) you can reload the table (after you dispatch on main thread).

It would look something like this:


func viewDidLoad() {
super.viewDidLoad()
refHandle = ref.child("Services").child("ServiceA").observe(.value, with : { (snapshot) in

mynamesArray = []

for child in snapshot.children {
mynamesArray.append((child as AnyObject).key)
}
DispatchQueue.main.async {
self.tableView.reloadData()
}

})
}

And in numberOfRowsInSection you just return the count:

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> 
return mynamesArray.count
}

UITableViewCells not displaying first time

You've accidentally hidden your cell.

  1. Open Main.storyboard
  2. Select Cell
  3. Uncheck Hidden

Side note: As for why it's displaying the second time around with the cell hidden? It appears to be a bug. It should still be hidden (print cell.hidden, notice it's always true despite showing the text on the screen).

UITableViewCell does not display content until tapped / Two cells on same row

I'm still not sure if this is an iOS/Xcode bug or if there is a logical explanation for this but the solution is to create "genericCell" only after the customCells are created and returned, like so:

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath = IndexPath(row:0, section:0) {
if let customCell = self.dequeueReusableCell(withIdentifier: "myCustomCell") as? MyCustomCell {
return customCell
}
} else {
let genericCell = tableView.dequeReusableCell(withIdentifier: "genericCell")
if indexPath = IndexPath(row:1, section:0) {
genericCell.titleLabel.text = "cell1"
} else if indexPath = IndexPath(row:2, section: 0) {
genericCell.titleLabel.text = "cell2"
}
return genericCell
}
}

or to create the UITableViewCell via an init method rather than dequeueing it from the storyboard -- as that worked without bugs on secondVC.

If anyone has an explanation for why this is needed please post as an answer and I'm happy to make your answer as the accepted one!

How to detect the end of loading of UITableView

Improve to @RichX answer:
lastRow can be both [tableView numberOfRowsInSection: 0] - 1 or ((NSIndexPath*)[[tableView indexPathsForVisibleRows] lastObject]).row.
So the code will be:

-(void) tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
if([indexPath row] == ((NSIndexPath*)[[tableView indexPathsForVisibleRows] lastObject]).row){
//end of loading
//for example [activityIndicator stopAnimating];
}
}

UPDATE:
Well, @htafoya's comment is right. If you want this code to detect end of loading all data from source, it wouldn't, but that's not the original question. This code is for detecting when all cells that are meant to be visible are displayed. willDisplayCell: used here for smoother UI (single cell usually displays fast after willDisplay: call). You could also try it with tableView:didEndDisplayingCell:.

UIWebView in UITableViewCell only completes load in first cell

Thanks to some tips from @Ricowere I got this working. My solution is based on the ideas from @Ricowere answer but using a UITableView delegate instead of a UITableViewCell subclass, and doing it all in Objective C instead of Swift.

Here's what ended up working for me...

In my WKWebView subclass:

- (id)initWithFrame:(CGRect)frame {
if ( self = [super initWithFrame:frame] ) {
...
self.navigationDelegate = self;
}

return self;
}

- (void)setStyle:(RenderStyle *)style {
_style = style;

NSURL *url = [NSBundle URLForResource:@"style" withExtension:@"html" subdirectory:nil inBundleWithURL:[[NSBundle mainBundle] bundleURL]];

[self loadFileURL:url allowingReadAccessToURL:[url URLByDeletingLastPathComponent]];
}

- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
[self drawFeatures]; // This line runs the secondary instance-dependant javascript that relies on the generic javascript already having been completed
}

Then in my UITableViewDelegate:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"styleCell"];

if ( cell == nil ) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@"styleCell"];
...
WKWebViewSubclass *styleView = [[WKWebViewSubclass alloc] initWithFrame:frame];

cell.accessoryView = styleView;
}

...
((WKWebViewSubclass *)cell.accessoryView).style = [[RenderStyle alloc] initWithString:styleString];

return cell;
}

It seems to run a little slower than the UIWebView, which is odd, but at least it works without errors, and without any dodgy work arounds.



Related Topics



Leave a reply



Submit