Search Unity

UIScrollView bugs out when drawn over Unity.

Discussion in 'iOS and tvOS' started by techmage, Aug 10, 2011.

  1. techmage

    techmage

    Joined:
    Oct 31, 2009
    Posts:
    2,133
    So I found a solution to this, and just thought I would post it for any future person that encounters the same problem.

    I modified Prime31's etcetera plugin to be able to bring up a window that relies on UIScrollView. I found that doing this, it produced a UIScrollview in which it's scrolling ability would randomly break, for absolutely no reason. It drove me nuts, for like 8 hours I was completely perplexed and in a mild panic as I had to get this working.

    I found that the reason UIScrollview would bug out when drawn over Unity is because Unity by default sets the ios app to run on a timer that is on a seperate thread, that is synced to the refresh rate of the display (I believe thats what its doing) It's done this way because it's the best way to tune an app for opengl rendering. BUT, this makes it so the incoming input stream may have a few missing ticks here and there because the event loop is tuned for drawing, not for capturing every input. Internally Unity is set up to deal with this sort of input stream. But all the native ios ui's are not, and so when using the UIScrollView, which relies on an interrupted stream of input to do it's gestures and swiping, when the input stream would miss a tick, it would break.

    The solution is actually simple, as described here:
    http://unity3d.com/support/documentation/Manual/iphone-Optimizing-MainLoop.html

    in appcontroller.mm disable CADDisplayLink by setting:
    #define USE_DISPLAY_LINK_IF_AVAILABLE 0
    and also switch it to use NSTimer by setting:
    #define MAIN_LOOP_TYPE NSTIMER_BASED_LOOP
    //#define MAIN_LOOP_TYPE THREAD_BASED_LOOP
    //#define MAIN_LOOP_TYPE EVENT_PUMP_BASED_LOOP

    So, hope that helps someone and you don't have to rack your brain like I did digging through xcode to figure this out.
    If your getting unpredictable behavior from overlaying native ios UI's on top of unity, PROBABLY has something to do with this timer thing....


    What I am curious to know, could it be possible to switch your app to NSTimer when using native ui's, then switch it back to thread based when you re-enter unity?
     
    Last edited: Nov 16, 2012
  2. Dreamora

    Dreamora

    Joined:
    Apr 5, 2008
    Posts:
    26,601
    The problem you see here comes from your code just being wrong or you approaching it in a very bad way.

    Correct fullscreen View overlay, as you see in other Prime31 plugins and basically any opensource solution floating around ALWAYS includes a call to UnityPause(...) when the view goes up and when its dismissed again to pause unity completely from running so the UI layer gets the cpu, gpu and audio.
    there is no reason to let unity continue in the background as you neither see it nor can you interact with it, you can only break it or break your front view especially in U3.4+

    Hacking and killing cadlink is definitely no option for the majority of games to plain simply a horrible idea
     
    Last edited: Aug 10, 2011
  3. techmage

    techmage

    Joined:
    Oct 31, 2009
    Posts:
    2,133
    Dreamora, UnityPause(true) was being called before the pulling up of the UIScrollView, and it was still having this issue.

    Which leads me to believe that when you set those CADisplayLink or NSTimer defines, that essentially sets up the main loop of the app for handling everything, unity's eaglview and native UI's input.

    Admittedly I am not well-versed in xcode, nor digging deep into the xcode unity generates so I do not know for sure. If you have any ideas on how you could fix the UIScrollView while keeping CADDisplayLink on, I would appreciate any tips you could offer. But UnityPause(true) was being called before the launching of my UIScrollView and it would still break without using NSTimer.

    I assume though you could potentially use the event pump to fix this as well, but I've not tested it.
     
  4. JustinKent

    JustinKent

    Joined:
    Aug 22, 2011
    Posts:
    1
    I'm having this same exact problem. A UIScrollView layered on top of a Unity EAGLView will work fine at first, then will break so that scrolling becomes very burdensome. When you scroll all the way to either extreme and wait briefly, it starts working again for a bit. But then will break again.

    Eem, your solution of
    seems to have fixed it.

    I had also previously tried using UnityPause(true) and found that it didn't work.

    So now I'm wondering what I'm giving up by disabling Display Link and switching to an NSTimer based loop.

    Anecdotally, performance doesn't seem to suffer, but I noticed a couple audio stutters. Could be unrelated.

    Any suggestions for a better fix?
     
  5. redd

    redd

    Joined:
    Jul 23, 2009
    Posts:
    122
    Just a note on this. I noticed that this seems to be a far bigger problem on iOS 6 than not. DisplayLink adds a significant performance boost which I didn't want to lose by disabling it, so I found a potentially better way that I thought I'd share:

    Display Link definitely causes issues with UIScrollView; causes it to stutter, stop in mid-scroll, etc. Changing to a thread-based system works a lot better in fixing those UIScrollView issues, however I've seen that you'll lose a lot of performance in your game. Therefore, the answer is to pause DisplayLink while you're doing your UIScrollView (assuming you're not needing to run something from Unity behind your scrollview.)

    How:

    In your AppController.h file, add this:

    Code (csharp):
    1. - (void) pauseDisplayLink:(bool)pause;
    In AppController.mm, add this within the @implementation of AppController:

    Code (csharp):
    1. - (void) pauseDisplayLink:(bool)pause {
    2.     [_displayLink setPaused:pause];
    3. }
    Now when you bring up your UIScrollView, you can use this to pause your DisplayLink. To access from another class, the easy way is:

    Code (csharp):
    1.     AppController *appDelegate = (AppController *)[[UIApplication sharedApplication] delegate];
    2.     [appDelegate pauseDisplayLink:YES];
    And when you're "going back" to Unity, simply resume Display Link
    Code (csharp):
    1.     [appDelegate pauseDisplayLink:NO];
    Hope this helps. I know I tried all sorts of ways to get around this issue, and this seems the easiest. It's not a 100% solution (I've been able to still get a very occasional stutter/stop in UIScrollView) however I would say it's a greater than 90% solution. Keep in mind that this effectively pauses Unity completely, so if you need Unity to be doing something with a greater than 0 fps behind the scenes this may not work for you. Instead, you may wish to just change the refresh interval to something like 2 or 3 fps. That works too, just not as reliably as completely pausing.

    Thanks,
    - Corey
     
    Last edited: Sep 25, 2012
  6. haoxiaolei

    haoxiaolei

    Joined:
    Nov 13, 2012
    Posts:
    2

    Thanks for your advice, I followed your method, and my project now running well.
    I have a UIScrollView element in the Native UIKit ViewController, so it's always freezing while scroll.
    My Device have iOS 6.0.1 on iPod Touch 4G.

    BTW: I noticed that in the "AppController.mm", there's a note write:
    This proves that you are right.
    Thanks again. It helpful.
     
  7. haoxiaolei

    haoxiaolei

    Joined:
    Nov 13, 2012
    Posts:
    2
    I hope your solution will be helpful.
    I will try this, thanks.

    ===========================

    I tried your codes, and also it works well.
     
    Last edited: Nov 13, 2012
  8. pcboylee

    pcboylee

    Joined:
    Nov 15, 2012
    Posts:
    1
    I am working in the same problem, I'm going to try it tomorrow the above two methods, thanks for sharing
     
  9. akasurreal

    akasurreal

    Joined:
    Jul 17, 2009
    Posts:
    442
    FYI, this has been addressed and fixed by Unity in 4.0 apparently, but was not backported to 3.5x.

    For anyone interested in manually applying that fix to AppController, I was given this and it works:

    Code (csharp):
    1. instead of
    2. - (void) RepaintDisplayLink
    3. {
    4. #if USE_DISPLAY_LINK_IF_AVAILABLE
    5.     [_displayLink setPaused: YES];
    6.  
    7.     while (CFRunLoopRunInMode(kCFRunLoopDefaultMode, kInputProcessingTime, TRUE) == kCFRunLoopRunHandledSource)
    8.         ;
    9.  
    10.     [_displayLink setPaused: NO];
    11.     [self Repaint];
    12. #endif
    13. }
    14. do
    15. - (void) RepaintDisplayLink
    16. {
    17.     [_displayLink setPaused: YES];
    18.  
    19.     static const CFStringRef kTrackingRunLoopMode = CFStringRef(UITrackingRunLoopMode);
    20.     while (CFRunLoopRunInMode(kTrackingRunLoopMode, kInputProcessingTime, TRUE) == kCFRunLoopRunHandledSource)
    21.         ;
    22.  
    23.     [_displayLink setPaused: NO];
    24.     [self Repaint];
    25. }
    26. and inside of - (void) prepareRunLoop
    27. instead of
    28. [_displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];do
    29. [_displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
    You can also modify the AppController in the Unity bundle to make this change permanent.
     
  10. techmage

    techmage

    Joined:
    Oct 31, 2009
    Posts:
    2,133
    Are you sure about this? Can you or anyone confirm this as true?

    I've been holding off on 4.0 update as I haven't needed it, but if this is true I'll get it ASAP. Would just like to be absolutely sure of it before putting down the money.
     
  11. akasurreal

    akasurreal

    Joined:
    Jul 17, 2009
    Posts:
    442
    Alexey @ Unity was who gave me this information. I don't know if its in the release 4.0 or in upcoming betas though and have no way to verify myself. Anyone with 4.0 can look at their AppController to verify though?
     
  12. Alexey

    Alexey

    Unity Technologies

    Joined:
    May 10, 2010
    Posts:
    1,624
    it should be in released 4.0, as i fixed it long ago ;-) [but yes i am too lazy to recheck]
     
  13. Randy-Edmonds

    Randy-Edmonds

    Joined:
    Oct 10, 2005
    Posts:
    1,122

    Thank you, thank you, thank you!!!
    That just saved my ass.