I have a MvxRecyclerView that gets filtered by a SearchView and ordered by options in a PopupMenu. If I background the app and resume it, changing options in the PopupMenu does not re-order the items in the MvxRecyclerView as it does before backgrounding but the SearchView filters fine without ordering.
Trying to solve a different issue this one got fixed.
I noticed FragmentInventoryTabView's OnResume was being called multiple times every time it came back from the background, which meant the ViewModel and the View were being instanced on every resume. They were being built with Mvx.IoCProvider.IoCConstruct() in the tab layout root ViewModel's ctor, so I changed those calls to Mvx.IoCProvider.GetSingleton() and registered the ViewModel types as singletons in the MvxAppStart.
Now I can order the items in the MvxRecyclerView after resuming back from the background. But OnResume is still being called multiple times.
Related
Similar to Foreach delegate in QML view I'm having trouble finding a list of current delegates in a QML TreeView.
However, the actual problem I'm trying to solve is this: I have a C++ class that inherits from QAbstractItemModel that provides several different roles. One of them is Qt::CheckStateRole which functions as an indicator for whether a particular item in the TreeView is selected for display elsewhere in the GUI.
Using Edit QStandardItemModel via TableView with Custom Delegate as a general guide (but adapted to TreeView, hint: use mapToItem() instead of mapFromGlobal()) I'm able to service user clicks to the checkbox that appears in that particular column (role). However, I need to programmatically alter the state of other checkboxes as well (different than the one that got clicked).
Strangely, dataChanged() signals (got them working for all of the other roles) do not affect the state of the checkbox (this is Qt 5.12.2, Ubuntu 20.04). I know what state I want to impose on the checkbox, and of course I should only need to do that for the currently existing delegates. However, it seems like it ought to be extremely convoluted -- the way there's a separate delegate for each role -- on top of the hierarchal nature of the data.
So, is there a way to access the set of currently existing delegates for a particular role that is operative in a QML TreeView?
Instead of trying to iterate over delegates I found that instead I could just create a Connections component within the delegate that connects a global youNeedToUpdateYourValue() signal to the delegate, which reassesses the current state of the checkbox. As the connection will disappear when the delegate is pruned, this seems to be a very elegant way to impose an action upon all existing delegates.
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.
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.
I have an app that imports a potentially large amount of data from the web after the user explicitly presses a Sync button, and stores that data using Core Data. Since I want to show feedback and I don't want the user interacting with the rest of the app while this happens, pressing the Sync button brings up a Modal dialog. Since I want the operation to happen immediately, the operation executes in the viewDidAppear method. I'm sure this is frowned upon.
There are a bunch of problems with the approach right now:
Everything happens in the main thread. The user kind of gets feedback because there is an activity indicator that continues to animate, but there's no way to indicate progress or show intermediate messages. This is not the right way to do things.
But, I am told that when using Core Data, everything has to use the main thread, so breaking off the work into another thread does not seem like it will be straightforward.
If the app enters the background state (user hits Home button or iPad falls sleep), it's game over - the operation dies. It's clear to me from the documentation why this is the case.
I know there are "I'm about to enter the background" events that you can handle, but it's not as though I can move execution of code from one place to another in the middle of a file download. Whatever solution I use has to be a continuous action that executes in the same way both before and after the transitions to/from the background.
I want the operation to execute in the foreground as far as the user is concerned. It does not make sense for them to interact with other parts of the app while this operation is taking place.
I am reading the Apple documentation on this, but I'm asking this in hopes of finding more concise guidance on this particular combination of needs. Thanks.
You really should not freeze the main thread. You can still "prohibit" certain UI actions.
Create a separate context, as a child, and do all your work in there. When done (or at certain intervals), save the context to the main context, and notify the main thread to do some UI update interaction... maybe a progress bar or something...
NSManagedContext *backgroundContext = [NSManagedContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
backgroudContext.parentContext = [self mainManagedObjectContext];
[backgroundContext performBlock:^{
// This block is running in a background thread.
// Go get your data from the web
// Call this to push data to the main MOC (either at end, or at intervals)
[backgroundContext save:&error];
// When you want to do something on the main thread...
dispatch_async(dispatch_get_main_queue(), ^{
// This block is running on the main queue... I can do anything with the UI...
}];
}];
Couple of things to note... your mainMOC needs to be private or main queue concurrency type. If you are using the Core Data template, where it is in the app delegate, just change the alloc/init to initWithConcurrencyType:NSMainQueueConcurrencyType.
I would, however, suggest using the canonical main/parent relationship. Create a private MOC, assign it to the persistent store, then create a main MOC, set its parent to be that private MOC. Now you are ready to handle any I/O with background operations, without blocking your UI.
Still, when loading from the web, use the pattern above: create a child MOC, then load objects into the main MOC.
Note, that the data is not saved to disk until the "root" MOC calls save.
There is a NSCollectionView in my Mac application, and it contains some icons. Meanwhile, I am using NSOperationQueue as a task scheduler to queue some operations. In one NSOperation, I modify the Content of NSCollectionView, the view should update at once when Content changes.
If I update it in the main thread, then it works perfectly well. If I put the same source code at the end of the NSOperation::main(), then the NSCollectionView would be completely blank.
Is this some kind of bug or I do it in the wrong way?
Thanks!
All interaction with UI elements must be performed on the main thread.
You can use the performSelectorOnMainThread:withObject:waitUntilDone: method to help you update UI elements from background threads.