I have a large modal view that pops up and it requires lots of other little views to be build, rendered and then added. I have been doing this asyncronisely by building the view parts aync and only performmingselectoronMainthread when necessary.
This is all good unless the user wants to quit out of the view (using a close button) before its finished rendering. How would I safely stop the view from rendering?
You generally do not want to kill threads. Probably the cleanest way to handle this would be to turn your view into a "zombie" that accepts the input from the secondary threads, but does nothing with it. If at all possible, you also want to have your secondary threads accept a signal telling them to quit as soon as possible (or at least as soon as reasonable, anyway).
Related
I am attempting to run a database fetch process in the background without locking the user interface.
Currently I have a button that does this, but I would like it to be automatic so that it can get more results as user is browsing current results.
Here is the code that the button does, I would like to make this automatic and not lock the UI. Also if there is a way to pause the process, but continue where it left off if user goes to another screen that would also be very useful.
Thanks in advance!
-(IBAction)continueUpdatingResultsButtonPressed:(UIButton*)sender{
[findMoreButton removeFromSuperview];
[self continueFindingMoreRecipes]; //(do this in background without locking screen)
[self loadRefreshButton];//At completion load this button (a blinking button) to refresh the cells with new results
}
A typical pattern you can use is something like this:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// perform data processing here (done in the background)
dispatch_async(dispatch_get_main_queue(), ^{
// update user interface here (done on the main thread)
});
});
You could do batch requests where you cache the next X amount of answers every time your UI got with in Y of the current end. Depending on what you are using a lot of databases have protocols that can help you batch cache easily. Good luck!
Use grand central dispatch. Create a new queue, dispatch it with a block and when you need to update, call dispatch get main queue. There is no way to pause this once the queue has been dispatched though. Maybe load recipes into intermediary then update as needed.
Look for some gcd tutorials there are a few decent ones there.
Would give you more code but I'm typing on iPhone.
It strikes me (especially since you are, by your own admission, "very new to programming") that it might not be prudent to pursue GCD to prefetch data in a background queue, make sure you coordinate the background queue's database operations with the foreground's database operations (possibly via something like FMDB's FMDatabaseQueue or something equivalent), gracefully handle pausing this operation as you go to other screens (as you indicated in your question) and making sure you don't cause retain cycles in the process.
To make matters worse, I gather that this is all in pursuit of a possibly questionable goal, if I understand you correctly, to retrieve 10,000 recipes in the background. Don't get me wrong. You certainly can design all of the background operations like we've outlined, but I think you should stop and ask yourself whether that's the right design for your business problem.
I might suggest an infinitely easier solution. Just load your tableview with the first x recipes, and as the user scrolls down, when you start to approach the end of the tableview, detect that fact and retrieve the next x records and add them to the tableview. But any user interface that is expecting the user to flip through 10,000 entries doesn't pass the smell test. When I think of a database with 10,000 entries, I think of something more like a imdb/wikipedia/google/facebook-like user interface rather than a contacts-style user interface.
Regardless, you almost certainly don't want your app just endlessly retrieving recipes in the background. You can solve your UI performance issue, but maybe replace it with memory management issues. And you're going to design a complicated system architecture when it's not entirely clear whether your recipe app requires that.
I have implemented a client for a paginated REST service where I populate the whole table with empty entries and then load them in the background using pagination when the user starts scrolling the table.
This seems to work very well except for a little detail: I am calling reloadRowsAtIndexPaths: in the main UI thread (because UIKit requires so), and I feel the animation sometimes makes me lose input touches.
This is, the user starts scrolling through the list and items start to load. Sometimes a reload animation happens at the same time the user is just touching the screen again, and this touch is not recognized. The scrolling then doesn't continue and stops. It doesn't happen all the time, and I guess it is an infrequent user case (the user is expected to read each row of results before scrolling down) but it annoys me to no end.
I'm doing most of my animations with blocks and the flag UIViewAnimationOptionAllowUserInteraction is really helpful, so is there a block version of reloadRowsAtIndexPaths where the reload animation would not block the user interaction with the scrolling?
I think the solution you seek is to background-load your data in a thread (as you describe) and only call reloadRowsAtIndexPaths: once the data is received and ready to display. You should also probably redisplay the data NOT animated.
I don't think there's anything you can do to get around the missed touches with your current scheme. My suggestion is to "do what iOS wants" rather than "try to cajole iOS to do what you want." Yes, probably not the answer you wanted to hear, but I think that's just the way it works.
(Btw, you can always use bugreporter.apple.com to make an enhancement request! :)
I have NSOperationQueue with some NSOperations in it (NSInvocationOperations, in particular). This operations do some calculations and change states of UI elements accordingly (of course, via performSelectorOnMainThread:...), often with use of animations.
My UI has UINavigationViewController and some buttons for navigation to another views. So user can leave current view, while calculations / animations are still in progress. And what I need is to stop this somehow until user comes back to current view.
The only solution I found is to create some thread-safe boolean flag - and to check it in all threads (something like: while !flag sleep_for_some_time;). Is there something better?
The question is a bit vague, so it's hard to say without knowing all of the code in play. With that said, I may approach the problem by:
Option 1. In your subclass of NSOperation, add your own atomic KVO property "isPaused". Within the operation itself, observe that property and handle accordingly if it ever changes.
Option 2. Are you ever suspending the Operation Queue itself? If so, consider observing that property from within your operations, and each one independently can take action if that value changes.
Option 3. Cancel all operations in the queue, and if the view appears again, just restart with new operations.
Overall, though, there is no magic bullet for pausing operations already in progress. You'll have to bake your own solution. The damage shouldn't be too bad though.
I have disabled interactions in my primary view (which contains some subviews I use as buttons).
I keep this disabled while I have a secondary view up indicating network activity (loading data). When it is finished, I re-enable interactions in the primary view.
This is so the user isn't tapping those buttons while the network operation takes place.
Anyway, all seems well, but the if the user starts tapping buttons in the primary view, once the re-enable occurs those touch events (from the past few seconds) actually trigger. Not the touches-began, which highlights the buttons, but the functions called on the touches-ended. Its like its queued the whole time it was disabled and "races to catch up".
It is very bizarre, why would touch events be queued while the view has its user interaction disabled?
It's hard to be confident of an answer here without seeing the code, but here is one idea:
If your network activity is synchronous, aka blocking, then it could be the case that the user's touches are queued before they get a chance to hit any of your code. If that's the case, then those touches won't be sent into your responder chain until the network activity finishes, and they'd never get a chance to "know" that you'd disabled interaction with those controls.
Here's one way you could help debug your situation: Add some debug NSLog statements in the top layer (what you call the secondary view) that's indicating network activity. Make sure the secondary view's frame is big enough to include those touches, and see if it logs the touches as soon as they happen. If not, my guess might be correct. If yes, well, you still get useful information - and you might be able to simply capture the touches at this level, instead of allowing them to queue up.
If this guess is correct, the least hacky fix I can think of would be make your network operations asynchronous.
If you don't want to do that, you could also try using an NSTimer to leave the network activity indicator up for a split second after the synchronous call completes. This way, your responder chain could clear out the queue of incoming touches, and ignore them if that's the desired behavior.
I'm going to do some sophisticated things in the delegate's methods during scrolling. I'll also implement the dynamic preloading of contents, so that it's theoretically possible to scroll through a few hundret thousand images. But I fear, that every time I do the preloading action for the next big chunk in scrolling direction, the delegate will wait for the data source to deliver the data, and the whole scroll view will be freezed for that moment.
The problem is, that I need always access to the subviews of the scroll view. I'm new to the platform and I don't know if I would still have access when I open up another thread for that preloading actions? Or would the scrollview not wait for the delegate to get things done?
I'm in the planing phase, so haven't implementet much jet.
You can only use UI classes from the main thread. So what you should do is to compute as much as possible in a background thread (I believe you can load your images in a background thread too), and then use performSelectorOnMainThread:withObject:waitUntilDone: to manipulate UI classes on the main thread.
See How do I update the UI in the middle of this thread? for the another instance of your question
I don't have a specific answer, to your specific question. I just want to make sure you are aware of the ~25MB limit of RAM for your app. I can't give links on this, because even though Apple knows the limit, they aren't telling.