I am developing an pdf reader in which i have to load each page of the pdf book on finger swipe. For each swipe i am incrementing one page it is working fine if i am swiping slowly.where it is not at all working if the swipe is so fast it gets crashed.
hi folks i resolved the above issue with the NSobject class reference it has only two lines..
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:#selector(loadPagesAccordingToCurrentOrientation) object:nil];
[self performSelector:#selector(loadPagesAccordingToCurrentOrientation) withObject:nil afterDelay:0.5];
I cant be sure but It feels like you are not accounting for the use case where one page may not have finished rendering while another comes into the pipe.
The line in
loadSinglePageWithWidth:(float)width andHeight:(float)height
myPageRef = CGPDFDocumentGetPage(...
feels especially unstable even if wrapped in the #synchronised pragma.
I think you need to account for that situation and cancel any existing renderings/animations before allowing the next one to begin.
SO isnt a debugger.
some of the code is used for my problem
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:#selector(loadPagesAccordingToCurrentOrientation) object:nil];
[self performSelector:#selector(loadPagesAccordingToCurrentOrientation) withObject:nil afterDelay:0.5];
Related
I am not sure why, but for some reason my UIActivityIndicator spinner stays visible for awhile after I ask it to stop spinning. In my app, I check for updates with a block method callback when the check is complete after which point the activity indicator is to be hidden. The NSLog is processed immediately when the block is called, but it takes maybe 5-10 seconds for the indicator to disappear. Nothing is going on in the main as far as I can tell, the app is just sitting there. I am very confused...
[self showActivityIndicator];
[[self schedulePack] checkForUpdates:^(void)
{
NSLog(#"Done updating.");
[self hideActivityIndicator];
}];
The problem is probably that you access a UI element from a separate thread. Try replacing
[self hideActivityIndicator];
with
[self performSelectorOnMainThread:#selector(hideActivityIndicator) withObject:nil waitUntilDone:NO];
I have an iPad app, and I want to do this
-(IBAction) clicked {
image=download(#"http://....."); // this is on the main thread
}
The download function is going to call a whole bunch of non blocking functions to download a file from the internet, but download itself shouldn't return until the image is downloaded.
While the program is waiting for the download at the image=download(...) line above, I want the UI to be able to still function, for example be able to scroll a UITableView, click another button etc.
So what I did was this inside the download function I used a RunLoop
-(void) download:(NSString *)url
{
BOOL stillDownloading=TRUE;
while(stillDownloading) {
stillDownloading=downloadAFwBytes(...);
CFRunLoopRunInMode(kCFRunLoopCommonModes, 0, YES);
}
}
I thought the CFRunLoopRunInMode function will keep pumping UI messages, touches, scrolls through the main UI thread so that the UI will keep working and not freeze until the download finished, but for some reason, it only works for a short time, and eventually the UI freezes.
Do you know why, or how to fix?
The download function is called everywhere in the program, that expects it to wait for the download, so I can't change it to non blocking at the moment.
The direct answer to your question is, no, this is not what CFRunLoopRunInMode does. What you are effectively trying to do is have the current run loop "yield" so execution can continue while the loading operation continues. This is not how iOS and run loops work. Your download function blocks the thread it is on until downloading is complete so the only solution to your issue is to change the implementation so that downloading occurs on a background thread and the objects that care are notified when it is complete. Here's a relatively small change that can get you on the right track. This overall topic (concurrency, managing background tasks) is a bigger discussion and there are different considerations/tradeoffs. I'll cut to the chase and hopefully get you on the right track.
Define a couple NSNotification's that your download method can post for interested objects to observe:
// in the .h file of the class performing the download
extern NSString * const MyClassLoadingDidStartNotification;
extern NSString * const MyClassLoadingDidFinishNotification;
// in the .m file of the class performing the download
NSString * const MyClassLoadingDidStartNotification = #"MyClassLoadingDidStart";
NSString * const MyClassLoadingDidFinishNotification = #"MyClassLoadingDidFinish";
In your download routine, do the download in the background and post the appropriate notifications:
-(void) download:(NSString *)url
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:MyClassLoadingDidStartNotification object:self];
BOOL stillDownloading=TRUE;
while(stillDownloading) {
stillDownloading=downloadAFwBytes(...);
}
[[NSNotificationCenter defaultCenter] postNotificationName:MyClassLoadingDidFinishNotification object:self];
});
}
In any object that initiates a download, observe and handle the notifications
// in any class that initiates a download
- (void)init...
{
self = [super init...];
if (self) {
// other initialization
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(didStartLoading:) name:MyClassLoadingDidStartNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(didFinishLoading:) name:MyClassLoadingDidFinishNotification object:nil];
}
return self;
}
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self name:MyClassLoadingDidStartNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:MyClassLoadingDidFinishNotification object:nil];
}
- (void)didStartLoading:(NSNotification *)notification
{
// update UI to show loading status (make sure you do UI changes on main thread)
// optionally check notification.object to ensure it's the loader class instance you care about
}
- (void)didFinishLoading:(NSNotification *)notification
{
// update UI to show loading status (make sure you do UI changes on main thread)
// optionally check notification.object to ensure it's the loader class instance you care about
}
Keep in mind that this is a very basic starting point. As you learn more and decide what you need you will definitely customize it. For example, you may want to add error handling, limit concurrent loading operations, provide other loading status, etc.
There are some architecture concerns with your question, but to properly download an image in the background and load it into a UIImageView or use it in any other way, I'd suggest taking a look at AFNetworking and read through their sample code for downloading resources.
Can you request to download the image on the background and then, return a static image asap in your method?
This way, the main thread returns fast and depending on your architecture, your background thread can update the image once it got it?
I think we need more details on your code (your download method is void so how is the downloaded data used?) to be able to really help.
Along with the 3 other guys telling you the same thing, I'll tell you to forget about your own run loop model, and just use the async download capabilities (assuming you're downloading over the net). You don't even have to build a thread, just start the async downloads and they will tell you when they're done. If you don't have the time to code it right, when will you have the time to fix your code?
Not sure if this is solved yet but can't you use?
[self.operationQueue addOperationWithBlock:^{
[self MethodName];
}];
and when you want something to happen in the UI like table updates then put this in the method code:
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
////perform something here ////
}];
hope this helps.
I am working on a pdf Reader application.I am making use of CALayer to render the pdf contents.i employed swipe gesture to navigate across the pages.The issue is, if the user attempts to go to next or previous page once the rendering of layer has started,the 'going to next page' action is being performed after completion of rendering of the current pdf page on to the layer.I want the rendering of the current page to be stopped immediately as soon as the swipe occurred and next page should start rendering on the layer.any idea please help.
UPDate:
here is my code
-(void)loadSinglePageWithWidth:(float)width andHeight:(float)height{
draw=0;
NSLog(#"before draw");
[markupView.layer insertSublayer:renderingLayer1 above:tiledLayer1];
loadingThread=[[NSThread alloc] initWithTarget:self selector:#selector(loadSinglePage) object:nil];
[loadingThread start];
}
-(void)loadSinglePage{
[renderingLayer1 setNeedsDisplay];
}
as soon as i swipe, in my action method, the code is written like
[loadingThread cancel];
[loadingThread release];
loadingThread=nil;
even i cancel the "loadingThread" the execution of the drawLayer: method seems to be running.am i correct with this thread approach?will drawLayer: code be executed by the thread which i am using to call setNeedsDisplay method?
I think that the best solution is to do the rendering in a separate thread. Once it's done, you simply display the rendered image on the screen. If not, you can always cancel the operation.
I have a UIView that I'm sliding in using a UIScrollView. Code looks like this:
[view setObject:object]; // updates a bunch of labels, images, etc
[scrollView addSubview:view];
[scrollView setContentOffset:CGPointMake(320,0) animated:YES];
Problem is, the scrollView doesn't seem to wait for the view to be completely loaded before animating its contentOffset, and so the animation is kinda jerky (almost non-existent on the first slide-in and on older devices). Oddly enough, switching the 3rd line to this fixes it:
[UIView animateWithDuration:0.3 animations:^{
[scrollView setContentOffset:CGPointMake(320,0) animated:NO];
}];
No lag whatsoever, perfectly smooth slide-in. However, this doesn't trigger any of the UIScrollViewDelegate calls, on which I also depend (but which aren't responsible for the lag).
Any idea of how I could tell the UIScrollView to wait for the view to be completely loaded before animating its contentOffset? Or maybe there's something else that I'm missing?
EDIT: before anyone else suggests it: yes, I did try:
[self performSelector:#selector(slideIn) withObject:nil afterDelay:1];
just to see if it would fix it. And yes it does fix the lag, but this is not an actual solution. performSelector:afterDelay: is never a solution, it's only a superficial fix. Plus, you're making the user wait extra seconds every time (since the actual delay may be much shorter than 1 second depending on the device model).
Performing selector after delay 0.01 works because the invocation is scheduled on the runloop vs. calling the method immediately. For that matter, afterDelay:0.0 may also work. Did not try in this particular case but works in may similar situations.
have you tried starting the scrolling in the "viewDidAppear" function of the viewcontroller... maybe this is called when the loading is finished.
otherwise try afterdelay with a very short time like 0.01 .. so that the call is scheduled next after the current work is done.
Try this, set contentSize after some delay (here delay is 2 second).
[view setObject:object];
[scrollView addSubview:view];
[self performSelector:#selector(contentSize) withObject:nil afterDelay:2];
}
-(void) contentSize {
[scrollView setContentOffset:CGPointMake(320,0) animated:YES];
}
I am working on pdf reader app.i am using gestures for rendering next page from the current page,for this i am using swipe.
1.My work is working well.
2. For each time i am calling drawLayer using setNeedsDisplay.
3.My app is working fine if the swipe is slow means while page rendering is in process if i swipe then it is getting crashing.
can any one help me to solve this issue with some library files.
Thank you all,
this is the answer for the above question [NSObject cancelPreviousPerformRequestsWithTarget:self selector:#selector(loadPagesAccordingToCurrentOrientation) object:nil]; [self performSelector:#selector(loadPagesAccordingToCurrentOrientation) withObject:nil afterDelay:0.5];