UIScrollView notifications - iphone

I'm coding an app that works much like Apple's Weather.app: There's a UIPageControl at the bottom and a UIScrollView in the middle of the screen.
In my code, I implemented the - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView method to figure out when the user did move to a new page. If they move to a new page, I load the adjacent pages' data, as to make further page-switching faster. (In one of Apple's examples, the - (void)scrollViewDidScroll:(UIScrollView *)sender is used, but that causes my app to shortly hang when loading a new page, so it's not suitable.)
That code works very well.
I'm using scrollRectToVisible:: to programmatically scroll inside the scrollview when the user clicks the UIPageControl. The problem is that the scrollRectToVisible: doesn't post a notification to the UIScrollViewDelegate when it's done scrolling - so the code responsible for loading adjacent pages never get's called when using the UIPageControl.
Is there any way to make the UIScrollView notify its delegate when it gets called by the scrollRectToVisible: method? Or will I have to use threads in order to prevent my app from freezing?
Thanks!
-- Ry

How about -scrollViewDidEndScrollingAnimation:?
If it doesn't work, try to listen to the UITextSelectionDidScroll notification. (Of course, it's undocumented.)
Alternatively, an SDK-safe method is measure the time taken for the animation and send a delayed notification at the call site of -scrollRectToVisible:.

You could add this delegate method instead:
- (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView
The scroll view calls this method at the end of its implementations of the UIScrollView and setContentOffset:animated: and scrollRectToVisible:animated: methods, but only if animations are requested.

Related

Preloading pages of UIPageViewController

I can't find a good solution to having the UIPageViewController preload the surrounding ViewControllers.
Right now what happens is when the user starts to turn the page, nothing happens, the code calls
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController
viewControllerAfterViewController:(UIViewController *)viewController
and once its loaded thennnn it shows the animation. This takes too long and is not smooth.
Anyone have a solution?
Create a mutable container and when you show the first view, create the two viewController that would be needed if the user pages, once you have the viewController object ask it for its view (to get it to load the nib and call the controller's "viewDidLoad:" method. You need to figure out a system on identifying the viewControlers so you can retrieve the viewController you need). You might be able to do the heavy lifting here in a block on a dispatch_queue.
From then on, when the user pages, you look first in the container for the viewController, and if not found, you have to do it in real time.
You probably want to use a dispatch_group so that you can wait on it for pending blocks to finish before paging.
Everytime the user pages, you will look and see if the pages surrounding that page are in the container or not. You could also pre-fetch more viewControllers - like two forward two reverse at each page shown.

How to refresh a view when resuming from Multitasking?

I have an app which is targeted to iOS 4 and above.I have a custom view, which is having
1.Scroll view
2.Custom drawing inside scroll view.
Now when my app is resuming from background,I want to refresh/reload this scroll view.
setNeedsDisplay is not working here as it usually meant for loading custom drawings and here I want to reload my scroll view contents.
Thanks in advance.
If I understand your question correctly, setNeedsDisplay does not work for you in this case because you need to "reload" the data that the UIScrollView handles before doing the redraw.
If this is right, I would define a method in your controller that does the reloading part; then, I would call this method from applicationDidEnterForeground or applicationWillEnterForeground; the reload method will need to call setNeedsDisplay after reloading the data.
I am sorry if this answer seems very generic to you. If you explain more or post some code, I can try and be more specific.
Have you tried redraw inside viewDidAppear?
- (void) viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[yourScrollView setNeedsDisplay];
}
setNeedDisplay calls the drawRect which is repainting operation. If you want to refresh the content only simply create one method in viewController and notify this method using performSelectorOnMainThread if you created new thread whenever thread complete (or you receive Data).

UI Automation: which delegate methods are called when scrolling in a scrollview

I'm new to UIAutomation introduced by iOS4. I'm scripting a test which requires to scroll in a scrollview.
So UIAScrollView has the following methods:
scrollUp
scrollDown
scrollLeft
scrollRight
scrollToElementWithName
scrollToElementWithPredicate
scrollToElementWithValueForKey
I want to know which UIScrollView delegate methods are invoked in the app when using these functions in my script.
UI Automation has nearly nothing to do with your Application in fact you can even run automation for apps that aren't yours. Indeed you have to know the accessibility label for each element. Therefore it should call the normal UIScrollViewDelegate which contains:
Responding to Scrolling and Dragging
– scrollViewDidScroll:
– scrollViewWillBeginDragging:
– scrollViewDidEndDragging:willDecelerate:
– scrollViewShouldScrollToTop:
– scrollViewDidScrollToTop:
– scrollViewWillBeginDecelerating:
– scrollViewDidEndDecelerating:
Managing Zooming
– viewForZoomingInScrollView:
– scrollViewWillBeginZooming:withView:
– scrollViewDidEndZooming:withView:atScale:
– scrollViewDidZoom:
Responding to Scrolling Animations
– scrollViewDidEndScrollingAnimation:
simply implement all into your application and NSLog() which one gets called.
Not sure if that's what you wanted to know.

viewDidAppear, but where is viewDidShowToUser?

I have an iphone3g with this function running in my ViewController
- (void)viewDidAppear:(BOOL)animated {
[super viewDidLoad];
}
I use a TabBar iphone app. But when I click from tab 1 to tab 2 and debug the secondView Controller it is stopped before the view is actually in the users view.
So there for when you click tab 2 until every function inside - (void)viewDidAppear:(BOOL)animated is complete the user gets to see the view.
Where is the function ViewDidShowToUser? Now I have a few functions running so it's sometimes slow and you're thinking the button is not working really..
First of all, you're calling [super viewDidLoad] instead of [super viewDidAppear:animated] inside your implementation of -viewDidAppear:
Secondly, using the debugger and breakpoints gives an artificial view of how your app behaves. In real world usage, users aren't going to notice that the -viewDidAppear: method returns before actually showing the view.
The real problem is your work that takes too long to complete and makes the app appear sluggish. You should consider performing the work asynchronously, and you have a couple of options to do that.
In your viewDidAppear: implementation you could use performSelector:withObject:afterDelay: to queue up the work. This method will return immediately and schedule your selector to be called in whatever time period you specify. If you pass 0 as the delay, it'll be queued up to run on the next iteration of the run loop, effectively giving you a way to return from the method and keep the user interface responsive.
You could use blocks, if you're not targeting anything below iOS4, and harness the power of Grand Central Dispatch to thread out your work nice and safely.
you are calling super on viewDidLoad: inside of viewDidAppear: ....change the line [super viewDidLoad]; to [super viewDidAppear:animated];
There's no easy way to tell.
UIKit makes UIViews. UIViews draw to CALayers. CALayers are handled by CoreAnimation.
CoreAnimation decides when to ask UIView to draw the layers. It draws all the layers on screen and then composites them on the GPU. Only after they're composited does the screen display the updated UI. This decoupling happens in order to allow CoreAnimation to do the majority of the work independently of the UI thread, but it means that you can't easily tell what's "actually on screen".
There is no easy way to tell when the screen has actually displayed something (apart from the now-private UIGetScreenImage()). viewDidAppear: gets called after UIKit finishes constructing (and animating) the views/layers. At that point, they will be "seen" by CoreAnimation after the next run loop, and displayed shortly thereafter. If you do lots of processing in viewDidAppear:, then CoreAnimation will never see the updated "model tree".

Deferring viewWillAppear until webViewDidFinishLoad

I have an application that uses UIWebViews in several view controllers. The UIWebViews are used to render locally generated html, no slow network access required.
To save memory I only load these on demand as prompted by the viewcontroller viewWillAppear callback. (And unload offscreen instances in response to didReceiveMemoryWarning messages.)
The problem is that the user gets to see the html being rendered, sometimes accompanied by flashes of styling and other assorted unpleasant artifacts. I would much rather the rendering be done offscreen, and reveal the fully rendered view when its ready.
It would be very tidy to be able to have the viewWillAppear not return until the UIWebView is fully rendered. But how?
I tell the UIWebView what to render by sending it a loadHTMLString:baseURL: message. This is asynchronous, and some time (soon) later the webview's delegate gets called back webViewDidFinishLoad.
I experimented with running a runloop inside viewWillAppear, running either the NSDefaultRunLoopMode or UITrackingRunLoopMode. This works in the simulator (it complains to the log
[CATransaction synchronize] called within transaction
but does work) but on a device it deadlocks, with webViewDidFinishLoad never being called.
(Also, it seems like the UIWebView loading property doesn't work. At least, after I call loadHTMLString:baseURL: and before getting the callback it's not true.)
Lots of solutions here I think. A quick one is to load your UIWebView with it's hidden property set to YES. Then set your UIViewController as the UIWebViews delegate and implement:
- (void)webViewDidFinishLoad:(UIWebView *)webView
where you set the property back to NO.
A thing to note is that webViewDidFinishLoad will fire more than once if you have framed/embedded content. So you have to keep track of this. Shouldn't really be a problem if you are loading local content.
I like monowerker's solution best, but another solution would be to hold onto the already-rendered UIWebView all the time (in some more permanent object than the view controller). I'd only do that if the look of monowerker's solution is too disruptive.