Quickly adding multiple items to an UICollectionView fails - cocoa-touch

I've got this UICollectionView set up with a fetched results controller, using core data. I use UIImagePickerController to add items to the UICollectionView. Now when I tap one of the photos stored on my device, it will be added to my managed object context and will be inserted into the UICollectionView.
Now when I quickly add multiple items, the app crashes with the following error:
2012-10-07 13:17:46.770 PhotoLibrary[2444:907] *** Assertion failure in -[UICollectionView _endItemAnimations], /SourceCache/UIKit/UIKit-2372/UICollectionView.m:2801
It seems like it can't handle adding an item while the animation of the previous added item hasn't ended yet. Just happens when you add them really quickly, but some of the users will do that.
Is there a good way to just wait and add the item when the other one is finished? Users should be able to add them "real time", so I can't just run all the changes at once.

The error only occurred when adding a few items quickly. But the real problem wasn't really related to the animation. I was creating the managed objects used to store the new items in a block, used in the ALAssetsLibrary's assetForURL:resultBlock: method.
Took a while to figure out that that was the problem, the managed objects were created in a separate thread. Turns out that managed objects doesn't handle that well.
Now moved the creation of the new items outside of the block, now it just works fine.

Without example code, I'm presuming the problem occurs when calling the - (void)reloadData method after inserting data into the UICollectionView. The documentation for this method explicitly calls out the fact you shouldn't call this method in the middle of an animation caused by insertion/deletion since the insertion/deletion will invoke the animation code automatically.
Some other notable methods are - (void)insertItemsAtIndexPath:(NSArray *)indexPaths and - (void)performBatchUpdates:(void (^)(void))updates completion:(void (^)(BOOL finished))completion. I have not used the batch method, but I've used the insert method a number of times and it works well.

Related

Observers remaining in dispatch table after object removed

Kind of a newbie question, so forgive me if I'm missing some basic concepts...
I have a view controller within a window, which contains multiple NSTableViews, each of which having a variety of associated observers added via addObserver. These work fine and as expected, with notifications going to each of the correct table views. However, if I close the window, then open a new window (of the same type), posted notifications are being sent to the table views of the window that was previously closed.
I was under the impression that as of macOS 10.11 observers did not have to be explicitly removed, which I would assume would happen when the original window is closed... UNLESS I'm missing something fundamental about closing windows. At the moment, I don't do anything special when the user closes a window, and the window just vanished from the screen. Do the views created in my viewDidLoad method live on even after the window is closed? Or no I need to explicitly dispose of these views in a method such as viewWIllDisappear?
Thanks!
Even when object you added as observer is disposed the observer is not. According to official documentation you have to pair each addObserver with corresponding removeObserver. This is true and for NotificationCenter and for KVO. By the way adding object as observer does not increase its owners, so under correct memory management left observer result in run-time crash - that is why needed paired removeObserver (if there is no crash in such situation it means there is leak).
The place where to do this depends on usage. If you add observer in viewWillAppear then it is better to remove it in viewWillDisappear, if you add on creation then remove should be done in deinit

UICollectionView does not respond or update - iOS8

I have a UICollectionView that needs to be updated, reloaded as I need. I have this implemented in other projects but specifically in this one it does not allow the UI to update, even though I can get the code to execute upon reloadData, reloadSections:, and invalidateLayout.
I can see the delegate and datasource methods for the collection view get called when I do this, however the UI does not update at all.
Any advice for workarounds or possible solutions?
My code is fairly generic so I can not understand why this is happening. I subclassed the UICollectionView, but did not add any subclassed initializers to the subclass since I have gotten away with this in other projects and I do not think it is the issue.
I have seen some force the operations onto the mainQueue, but I don't think this is the issue anyway since UIKit updates only run on the main queue.
EDIT: extra info:
I perform a reloadData or reloadSections, THEN I called invalidateLayout on collectionView.collectionViewLayout

Using NSManagedObjectContextObjectsDidChangeNotification

In many of my UIViewControllers, I update certain controls based on the state of my data. For example, I might have an edit button on a UITableViewController that should only be enabled when there is one or more items. Or perhaps I want to limit the number of items that can be added, and disable the 'add' button otherwise.
Every time I add or delete an item (or take any other action that can add/remove items), I have to remember to update any controls that might need enabling/disabling. This is trivial for the most part, but doesn't feel comfortable - there is a lot of repetition, and I have to remember to add the calls to updateControlEnabled (or whatever) whenever I add new functionality that might affect the data.
And then I noticed NSManagedObjectContextObjectsDidChangeNotification. Reading the docs, it looks like I can receive a notification whenever something changes in my managed object context. This seems ideal, but I have a few questions:
Is this an appropriate use of
NSManagedObjectContextObjectsDidChangeNotification?
Should I anticipate any performance impact if a controller
subscribes to these and parses each one to see if it needs to update
the UI? I will be checking the userInfo for every change, instead of
only those that I know I will care about.
Where should I subscribe to the notifications? My UIViewController has a
reference to the context, which helps, but I don't know where to
subscribe (loadView? viewDidLoad? init?) such that the view
controller will always have one and only one subscription.
The view controller will continue to receive and process notifications
when it's offscreen - enabling and disabling controls as the
data model is affected from elsewhere. Is this ok?
I guess I'm mostly just wondering if anyone else uses this approach and if so, what their experience is.
Q) Is this an appropriate use of NSManagedObjectContextObjectsDidChangeNotification?
A) Yes - I used it on OSX for a similar purpose.
Q) Should I anticipate any performance impact if a controller subscribes to these and parses each one to see if it needs to update the UI? I will be checking the userInfo for every change, instead of only those that I know I will care about.
A) NO - it will normally be a very small set of objects - ones that were directly changed.
Q) Where should I subscribe to the notifications? My UIViewController has a reference to the context, which helps, but I don't know where to subscribe (loadView? viewDidLoad? init?) such that the view controller will always have one and only one subscription.
A) Well, you cannot affect the UI til the view shows - so probably viewDidLoad or viewWillAppear. The problem with the later is you may get it a few times depending on push/pops, so maybe I'd do it in viewDidLoad.
Q) The view controller will continue to receive and process notifications when it's offscreen - enabling and disabling controls as the data model is affected from elsewhere. Is this ok?
A) Sure - when the view reappears all the elements will be setup correctly.
What you want to do is a classical use of that notification. Just check the thread it comes in on - if its not the mainThread then you want to make all your changes in a block posted to the mainThread.

How to clear MKMapView cache?

I am trying load map region and MKMapView delegate methods are not being called on second or subsequent load. None of the delegate methods viz
- (void)mapViewWillStartLoadingMap:(MKMapView *)mapView;
- (void)mapViewDidFinishLoadingMap:(MKMapView *)mapView;
- (void)mapViewDidFailLoadingMap:(MKMapView *)mapView withError:(NSError *)error;
are ever called. The Only methods called are
- (void)mapView:(MKMapView *)mapView regionWillChangeAnimated:(BOOL)animated;
- (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated;
It seems that ios4 is caching mapview tiles images.
I found these lines in MKMapViewDelegate Protocol Reference documentation. Highlighted line is my problem.
This method is called when the map tiles associated with the current request have been loaded. Map tiles are requested when a new visible area is scrolled into view and tiles are not already available. Map tiles may also be requested for portions of the map that are not currently visible. For example, the map view may load tiles immediately surrounding the currently visible area as needed to handle small pans by the user.
I need to perform certain operations after the map is loaded but since none of the above mentioned delegate methods are getting called I am not able to perform desired functionality. Can anyone suggest a fix to either clear the cache or provide an alternative solution for this ? I have already tried using the methods described here and this but I am still not been able to get the code working.
I think you can couple willStartLoadingMap, didFinishLoadingMap and regionDidChange, like this:
in willStartLoadingMap set a loading flag to true;
in didFinishLoadingMap set the loading flag to false and also check if you have a queued call for the method that captures the screen. If so, call it;
in regionDidChange check the loading flag and if it's set to false, call the method that captures the screen. Otherwise, queue it so it's executed when the map finishes loading.
This way you're sure that you capture the screen after the tiles have been loaded.
However, regionDidChange may be called many times, so make sure you grab the screen only when the map view changes significantly (you can compare previous map region/center and current map region/center for this).
I created a new project and connected the delegate to my view controller. The first two methods of the three in question gets called. Since it loads all tiles the error delegate method wasn't called.
I just recently walked into similar problems. I subclassed MKMapView and forget to set the delegate in my custom init methods and in awakeFromNib:. Perhaps that's what causing your problems, too.

UITableView delegate method called twice

Today my question is about UITableViewController-s
In particular I have noticed that the datasource delegate method
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView;
is called twice (even if for instance I just create a navigation based application and without adding a line of code.. well adding an NSLog to track it).
Now, since in my application I need to determine the number of sections basing the choice on the documents in the file system, I need to call some methods to do so. I have put these methods in the above mentioned method, so they will be called twice, which is something I don't need.
The questions are why is it called twice, can I have it called once?
I hope that in the official documentation this is not clearly stated (which would mean that I didn't read it at all :) ). By the way I could see others posting similar questions, but I couldn't find a fully satisfying answer.
Thank you.
I was experiencing the same problem, only with the call to numberOfRowsInSection:
The answered laid in the stack trace for each call I received.
The first call was due to a change in the table header view I was making in the viewDidLoad: of my viewcontroller.
thumbView.tableHeaderView = nil;
thumbView.tableFooterView = nil;
This resulted in internal call to _updateContentSize: which called heightForTable: which eventually called numberOfRowsInSection:. This was something I triggered, and could be easily avoided by not doing the above code :)
The second call was the legitimate one in order to reloadData. This is triggered by a layout event somewhere and most likely you can't skip it.
I'm sure you can observe something similar for the numberOfSections: method
So, my conclusion is that due to the the implementation of UITableView there are many situations where certain delegate methods will get called twice or more because the table view has to refresh something. I try to design my code around this bug/feature/etc.
Hope it helps
If your tableview is contained by a child view controller,
Try this at your parent ViewController
[parentViewController addChildViewController:childViewController];
before [parentViewController.view addSubview:childViewController.view]
Please check your code, after adding TableView you may again called realodData method of table in mey be ViewWillAppear method
This can happen if you'r table view's frame gets changed by mistake in the story board.Say you clicked on the storyboard where you have added the table view as a subview and now your table may not be having the proper frame which you have set in the beginning.