MKMapView blocks main thread while used in UITableView - objective-c

I have a basic UITableView that contains 20 cells, one of the cells is a MKMapView just dropped in as is without any custom code (not even setRegion:Animated), if I open the view at the first time and scroll down the table view (towards the map's cell), there is a noticeable hang happens for the app, and if I use setRegion:Animated (without dropping any pin) the hang gets longer. However, this block of the main thread disappears on later attempts to scroll towards the map's cell.
I can't use the MKSnapShotter because I want the user to interact with the map so an image won't satisfy the case.
the table view does not make any block on the main thread if the map does not exist.
how to avoid blocking the main thread while showing the map's cell for the first time ?

First make a tableView IB outlet if you haven't already done so. Create the cell in view did load using tableView.dequeueReusableCellWithIdentifier("mapCellIdentifier") and store that in an ivar. Then in the data source return the cell when the index path is the one you want so inside of cellForRowAtIndexPath do
if (indexPath.row == wantedRow) {
return mapCell
}
The mapCellIdentifier needs to be set in the cell you created in interface builder for the map. This is assuming you have dynamic cells.

Related

View-based NSTableView is only redrawn when focus is changed

I'm writing a simple Cocoa app with GUI that only consists of a simple table whose data may update sometimes. But when data actually updates, table's rows in most cases disappear completely. But as soon as I change focus (click on desktop if my app's a primary window or click on app's window if it's not), everything shows up properly
Data is handled in a separate singleton class that runs a thread that actually looks after data source and publishes NSNotification when data changes. Window controller receives that notification, extracts new data and triggers [tableView reloadData] - that's where the problems begin
Window controller is a data source and delegate for the table and implements numberOfRowsInTableView method and viewForTableColumn method. When the problem occurs, numberOfRowsInTableView is called and returns non-zero value, but viewForTableColumn isn't called at all
I expect the table to be properly redrawn whenever [tableView reloadData] is called and wherever my focus is, but on practice everything just disappears and properly redrawn only after I change focus
Fixed it. The problem was updating GUI from background thread

Is it possible to force collectionView not to reuse visible cells?

dequeueReusableCellWithReuseIdentifier doc says: "This method dequeues an existing cell if one is available .." When is a cell available? How can I force collectionView not to reuse cells that are visible?
Could it help, if for indexPath I pass a nil argument?
NOT the visible cells get reused, but the ones which where visible, but at the moment are off-screen.
This is how the Guidelines tells us to do!!!
Reusable Views Improve Performance.
"Collection views employ a view recycling program to improve efficiency. As views move offscreen, they are removed from view and placed in a reuse queue instead of being deleted. As new content is scrolled onscreen, views are removed from the queue and repurposed with new content."
Check out this link to understand better:
https://developer.apple.com/Library/ios/documentation/WindowsViews/Conceptual/CollectionViewPGforIOS/CollectionViewBasics/CollectionViewBasics.html

JSON, cellForRowAtIndexPath bug

Struggling to find where fault is with my code. On first view load, everything works and loads fine as it should, but when i revisit that view, it seems that the first two cells are empty. I logged the dictionary (dict) in viewWillAppear: and it logs the data fine, so error has to be in cellForRow method. Take a look at my method, and see where i'm going wrong, the third cell populate third piece of data, so i'm totally stumped, but the first two cells are completely blank, no data.
http://pastebin.com/Va84MG5g
First of all, why are you doing all of that insane UITableViewCell customization inside of your tableView:cellForRowAtIndexPath: method? Create a custom UITableViewCell subclass and do the set up there.
In the class's initWithStyle: method, add all of your subviews with a frame of CGRectZero, because at initialization, the table view cell doesn't know how big it is. You can set text alignments, colors, etc. here as well. Then, in layoutSubviews, go ahead and set all the frames. Override prepareForReuse and set things like your UIImageViews to nil. This will help with performance for reused cells.
As for why you're not seeing your data in your first two cells, my initial thought is that it has something to do with the way you're setting up your cells for reuse. You're asking your tableView to dequeue a regular UITableViewCell and only creating all of these subviews if the returned cell is nil. So what happens when it returns a UITableViewCell? You skip the part where you alloc/init all these subviews, and so you're basically adding nothing to the cell. I feel if you create a custom subclass and ask your UITableView to dequeue that instead, you'll get the result you're looking for.
NOTE: If you're targeting at least iOS 5, you can create your UITableViewCell's layout in a nib and register the nib with the table view. Doing so will guarantee that you always get a dequeued cell, and you never have to do your if (cell == nil) check. If you're targeting iOS 6, you can register a UITableViewCell subclass.

Value is shown in UITableView after it is scrolled

In my iPad application, I am inserting some value on a label. That value should also appear on a UITableView in another class. When I insert the value in the label and navigate to another view, there is no value appearing on the table view but when I scroll the table view up and as it comes to its original position, the value appears. How can I fix it?
Thanks and regards
PC
When you scroll the table you are eventually reloading the data as things become visible. If you call [tableView reloadData] on viewillAppear this should get the label refreshed and displaying correctly for you.
Good Luck!
Check out the reloadData method in the documentation. You will need to call this method after changing the value so the table knows to reload the cells currently being displayed. As you scroll up the cells are being redrawn, which is why you see the new value after a scroll.
Just to clarify, the reloadData method will need to be called on the UITableView object.
[myTableView reloadData];
From the documentation:
Reloads the rows and sections of the receiver.
- (void)reloadData
Discussion
Call this method to reload all the data that is used to construct the table, including cells, section headers and footers, index arrays, and so on. For efficiency, the table view redisplays only those rows that are visible. It adjusts offsets if the table shrinks as a result of the reload. The table view's delegate or data source calls this method when it wants the table view to completely reload its data. It should not be called in the methods that insert or delete rows, especially within an animation block implemented with calls to beginUpdates and endUpdates

How to setup a NSTableView with a custom cell using a subview

I am trying to setup a NSTableView with a custom cell using an ArrayController and Bindings. To accomplish this I added a subview to the custom cell. The data connection seems to work somewhat. Though, there seems to be a redraw problem which I cannot fix. When I load the application only some of the cells are rendered. When I scroll through the rows or select one the rendering changes.
I created an example project on github to illustrate what the problem is.
The actual source code for the cell rendering can be found here:
// CustomCell.m
- (void)drawInteriorWithFrame:(NSRect)cellFrame inView:(NSView*)controlView {
if (![m_view superview]) {
[controlView addSubview:m_view];
}
// The array controller only gets wrapped data items pack by the NSObjectTransformer.
// Therefore, objectValue returns a NSObjectWrapper.
// Unpack the wrapper to retreive the data item.
DataItem* dataItem = [(NSObjectWrapper*)[self objectValue] original];
[[m_view name] setStringValue:dataItem.name];
[[m_view occupation] setStringValue:dataItem.occupation];
[m_view setFrame:cellFrame];
}
It seems as if the parent controlView does not redraw. Can I force it somehow?
This is almost certainly not a best practice way of doing this, and I'll explain why afterwards: however, it does seem to work. Replace your cell class's drawInteriorWithFrame:inView: method with the following:
- (void)drawInteriorWithFrame:(NSRect)cellFrame inView:(NSView*)controlView {
DataItem* dataItem = [(NSObjectWrapper*)[self objectValue] original];
[[m_view name] setStringValue:dataItem.name];
[[m_view occupation] setStringValue:dataItem.occupation];
[m_view setFrame:cellFrame];
NSData *d = [m_view dataWithPDFInsideRect:[m_view bounds]];
NSImage *i = [[NSImage alloc] initWithData:d];
[i setFlipped:YES];
[i drawInRect:cellFrame fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0];
}
The problem is that only one NSCell is created for the entire table. That's how cells are meant to work: the table view creates a cell, and calls setObject… followed by drawInterior… over and over again to get the cell to draw the whole table. That's great from an efficiency perspective (the NSCell class was designed back when 25mhz was a fast computer, so it aimed to minimise the number of object allocations), but causes problems here.
In your code, you populate a view with values, and set its frame, adding it as a subview of the table view if needed. However, since you've only got one instance of NSCell, there can only be one view: you took the single view that you had and merely moved it down the rows of the table.
To do this properly, you'd need some data structure to track all the views you added as subviews of your NSTableView, and when the cell is updating one in the drawInterior… method you'd need to look up which the correct one was and update that. You'd also need to allocate all these views in code (or at least move the view to a separate nib which you could load multiple copies of), because as it is you've only got one in your nib and copying a view is a pain.
The code I wrote is a kludge, since it's really inefficient. What I did was each time the view needs to draw, I drew the view into an off screen image buffer, and then drew the buffer into the correct place in the table view. In doing so, I avoided the problem of only having one view, since the code just takes and draws a new copy of its contents whenever it is needed.
EDIT: See my other answer for explanation
Have you implemented copyWithZone:? You'll need to ensure you either copy or recreate your view in that method, otherwise different cells will end up sharing a view (because NSTableView copies its cells).