In iOS, is there a relationship between run loop and display refresh? If so how?
Apple has a very good WWDC 2012 video describing in detail what happens at the end of each run loop when the current CATransaction commits and drawing happens and animations begin. You might also enjoy reading the less technical explanation of the "redraw moment" in my book:
http://www.apeth.com/iOSBook/ch17.html#_drawing_animation_and_threading
Apple's View Programming Guide for iOS / View and Window Architecture / View Architecture Fundamentals / The View Drawing Cycle says this:
When the contents of your view change, you do not redraw those changes directly. Instead, you invalidate the view using either the setNeedsDisplay or setNeedsDisplayInRect: method. These methods tell the system that the contents of the view changed and need to be redrawn at the next opportunity. The system waits until the end of the current run loop before initiating any drawing operations. This delay gives you a chance to invalidate multiple views, add or remove views from your hierarchy, hide views, resize views, and reposition views all at once. All of the changes you make are then reflected at the same time.
(Emphasis added.)
Related
I have been only using Xcode 5 for a little while now and I need help when it comes to auto scrolling a UIScrollView. I am using a single view application. I need the screen to scroll down at a pace that speeds up incrementally. Also I need the screen to keep progressing even when the screen is touched. If someone can explain which code goes where it would be great! Your help will be greatly appreciated. :)
Check out this library: https://github.com/danielamitay/DAAutoScroll
It stops to scroll when the user touches the screen and that's the only solution I see possible. I don't even see why you wouldn't want the user to be able to stop the scrolling..
OK, just adding this from your duplicate question.
I suspect the Piano Tiles game is actually using something like Sprite Kit.
This allows a lot more control over thing like "scrolling" speed.
Instead of using a UIScrollView you would use an SKNode as a layer with the buttons added to that parent layer.
Then using the update game loop you can incrementally increase the speed of the movement based on the time since the game started.
In essence... don't use UIScrollView, don't use UIKit, use SpriteKit.
I can see a few options:
1) suggested by Fogmeister, use Sprite Kit instead.
2) see setContentOffset
3) just use a normal view as parent, then have another child view on top with the full content (would be longer than the parent view), create a NSTimer to periodically call a method which scrolls the child view in whatever direction and speed as required.
Note that might need something on top to mask around the child view from showing the suppose-to-be-hidden sections of the child view.
Hope this helps
I would like to have a UIView subclass that implements a method similar to setNeedsDisplay, except that redrawing (i.e., that would usually be called via drawRect:) will occur in a background thread sometime soonish, rather than at the end of the current update cycle.
It might be called setNeedsAsynchronousDisplay. Or the existing setNeedsDisplay could get hijacked and not cause redraw at the end of the cycle, or whatever, as long as it lets the redraw not happen on the main thread blocking screen updating an interaction until its completed.
Until the redraw occurs, the view can continue to use its current drawn representation.
Is something along these lines reasonably doable?
Thanks!
Yes it is possible. You will probably need to generate the content view as an image in the background and push the images into a nsdictionary or array.
So while your background is generating the images you can just show the image in drawrect function by rendering the image, providing the image has been generated.
A WWDC video that shows how to do it: WWDC 2012 session 211 - Building Concurrent User Interfaces on IOS. Here is the video description:
For a great user experience, it's essential to keep your application responsive while it renders complex UI elements and processes data. Learn how to use concurrency at the UIKit layer to perform drawing and other common operations without blocking user interaction.
No. View Drawing must occur in the foreground. Apple makes that very clear in their docs.
EDIT: You're right that you CAN do Core Graphics drawing in the background as long as it's not in a UIView object's drawing methods. You'd have to do the drawing in the background, then send a message to the main thread to update your view object once drawing is complete.
I would suggest not trying to override setNeedsDisplay. Instead, add your new setNeedsAsynchronousDisplay method. In that method, queue rendering code to an async queue using GCD calls. Once the rendering is complete, have the rendering code send a setNeedsDisplay message to self on the main thread.
Then in your subclass's drawRect method, check for pre rendered images and draw those into the view's context instead of the normal code.
A downside of this is that merely by implementing drawRect, you may slow down rendering because the system calls that instead of doing other, more efficient things to render your view's content.
I've seen a lot of helpful tutorials that show one how to:
make an image move according to a predefined path, or
move the image, a few pixels at a time, in response to a UIButton.
What I want to do is have the image "drift" arbitrarily according to an Vxy velocity I define, then have the button(s) change the velocity. (Yes, I'd have it slow down with time if no action made).
In other languages there might have been a way to do Change Pxy position by Vxy (to ad infinitum) unless button pushed. I believe GET was the command. I can think of a way to do that in iOS I suppose but that would involve setting up a series of 1 sec CGMutablePathRef anims. Alternatively, I have seen some talk of NSTimer: would it be a good practice to introduce some sort of delay: draw, delay, draw, delay.
Request: specific classes or terms I can search in the manuals for myself.
Iirc using uiview's animateWithDuration:completion is cheaper than using core animation. frame is an animatable property. So, yeah I think I would use an NSTimer to call your method for default calculation of the end frame of your view and then call animateWithDuration:completion there.
[deleted bad idea]
I ran across a wonderful tutorial for anyone considering such a project;
http://www.youtube.com/watch?v=nH_Rj152DRM
I believe the key "noob" problem I was having was in not realizing I should declare the instance variable for my sprite/ image in the
-(void) viewDidLoad{
then work on other properties of the animation in touches/ other user events. Once I figured that out, I am now capable of doing the heavy lifting for the rest of the project myself.
I have a view with the following structure:
GrandView
-Parent View1
-Parent View2
--Child View1
--Child View2
The child views take up almost all of the bounds of Parent View2, but there is still some space around the edges. I can select Parent View2 in the simulator with the accessibility inspector if I click on the edges. I can also tap Parent View2 in UIAutomation if I use:
tapWithOptions({tapOffset:{x:0.15, y:0.95}});
However, my calls to isVisible() always return 0. I expect that if I can tap the element, or select it with the accessibility inspector, it should return 1.
How does UIAutomation determine whether a UIAElement is visible?
Every operation you perform against an element has a timeout. This isn't obvious from the Apple documentation, but if you look at: setTimeout , it tells us:
The timeout value establishes a grace period for object resolution. If an object representing a UI element becomes available within the grace period, an attempt is made to instantiate that object from information retained by the instrument.
setTimeout itself just changes the default value (as do push and pop). What you really want to do is perform your action on your view, and fail on the timeout if it never becomes available (the default timeout is 5 seconds). The WWDC 2010 session "Automating User Interface Testing with Instruments" does go into this a little, it's available on the ADC WWDC 2010 page, with both video and slides. In your case, you'd want to execute the tap() on your view. If, for some reason, that view isn't available to UIAutomation within 5 seconds, you should see an exception. Experiment with changing the timeout by doing:
var oldTimeout = target.timeout();
target.pushTimeout(10);
before your code, and
target.popTimeout(oldTimeout);
after.
If it's a UIView, it should be driven the the hidden property. If it's not a view, and it's a container, it should be driven by accessibilityElementsHidden .
In general though, you don't want to use this for UIAutomation. Instead, whatever you were going to do on the view - in this case, a tap() - go ahead and do it, and let the system throw an error if it times out. In general this is the model you want to follow in your scripts rather than testing whether something is available first. Sine UIAutomation is DOM scripting the UIAccessibility information, when things like animated view transitions happen things get out of sync. Sometimes the script executes faster than the UI animates, and sometimes the opposite! waitForInvalid may be a shortcut to a solution for you.
Alex Vollmer's tuneup.js library for UIAutomation makes writing tests much easier, and is easy to extend.
https://github.com/alexvollmer/tuneup_js
I have always been a bit unclear on the type of tasks that should be assigned to viewDidLoad vs. viewWillAppear: in a UIViewController subclass.
e.g. I am doing an app where I have a UIViewController subclass hitting a server, getting data, feeding it to a view and then displaying that view. What are the pros and cons of doing this in viewDidLoad vs. viewWillAppear?
viewDidLoad is things you have to do once. viewWillAppear gets called every time the view appears. You should do things that you only have to do once in viewDidLoad - like setting your UILabel texts. However, you may want to modify a specific part of the view every time the user gets to view it, e.g. the iPod application scrolls the lyrics back to the top every time you go to the "Now Playing" view.
However, when you are loading things from a server, you also have to think about latency. If you pack all of your network communication into viewDidLoad or viewWillAppear, they will be executed before the user gets to see the view - possibly resulting a short freeze of your app. It may be good idea to first show the user an unpopulated view with an activity indicator of some sort. When you are done with your networking, which may take a second or two (or may even fail - who knows?), you can populate the view with your data. Good examples on how this could be done can be seen in various twitter clients. For example, when you view the author detail page in Twitterrific, the view only says "Loading..." until the network queries have completed.
It's important to note that using viewDidLoad for positioning is a bit risky and should be avoided since the bounds are not set. this may cause unexpected results (I had a variety of issues...)
This post describes quite well the different methods and what happens in each of them.
currently for one-time init and positioning I'm thinking of using viewDidAppear with a flag, if anyone has any other recommendation please let me know.
Initially used only ViewDidLoad with tableView. On testing with loss of Wifi, by setting device to airplane mode, realized that the table did not refresh with return of Wifi. In fact, there appears to be no way to refresh tableView on the device even by hitting the home button with background mode set to YES in -Info.plist.
My solution:
-(void) viewWillAppear: (BOOL) animated { [self.tableView reloadData];}
Depends, Do you need the data to be loaded each time you open the view? or only once?
Red : They don't require to change every time. Once they are loaded they stay as how they were.
Purple: They need to change over time or after you load each time. You don't want to see the same 3 suggested users to follow, it needs to be reloaded every time you come back to the screen. Their photos may get updated... you don't want to see a photo from 5 years ago...
viewDidLoad: Whatever processing you have that needs to be done once.
viewWilLAppear: Whatever processing that needs to change every time the page is loaded.
Labels, icons, button titles or most dataInputedByDeveloper usually don't change.
Names, photos, links, button status, lists (input Arrays for your tableViews or collectionView) or most dataInputedByUser usually do change.