Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

OrientationChanged called multiple times at startup?

edited December 2015 in Questions Posts: 370

I notice that the orientationChanged function is called three times when the code is first executed from the editor. This seems excessive- is it intentional? once would be enough.

When the code is restarted from the fullscreen button it is not called.

When the orientation is really changed then it is called twice?

Comments

  • Posts: 2,020

    Good catch. Same here on my iPad Air. Gets called once before setup, then twice after. This has changed a little in Codea 2.3.2. Before, I don't know how many times it was called, but it definitely wasn't called before setup.

  • SimeonSimeon Admin Mod
    Posts: 4,847

    I think it's just calling it whenever iOS triggers the underlying call on the view.

    I want to deprecate this function in a future release in favour of a boundsChanged or similar API. Same goes for supportedOrientations — that shouldn't be something you specify in your code, but something which you can apply once you export your project and are in Xcode.

  • Posts: 2,020

    Here's a quick hack to see whether the orientation really has changed. Useful if you have to resize loads of ui elements. I'll add this to Soda, as I thought the orientation change animation seemed sluggish. Might be worth creating an issue for this in the tracker though. Thanks again @piinthesky for discovering this.

    -- Orientation Changed
    
    function setup()
        print("Hello World!")
    end
    
    function draw()
    
    end
    
    function orientationChanged(ori)
        print(ori)
        if previousOrientation ~= ori then
            print"orientation has really changed"
        end
        previousOrientation = ori
    end
    
    
  • @yojimbo2000 @simeon it seems that the width and height variables are only updated after the second call to the orientationChanged call, so catching just the first change is no good!

  • Posts: 2,020

    @piinthesky Good catch again!

    If you add a displaymode call, then it adds a 4th orientation changed call to setup (I guess that one makes sense).

    So here's a modified version of the above code that compares the widths:

    -- Orientation Changed
    
    local orientations = {"PORTRAIT", "PORTRAIT_UPSIDE_DOWN", "LANDSCAPE_LEFT", "LANDSCAPE_RIGHT"}
    
    displayMode(OVERLAY) 
    
    function setup()
        print("This is setup")
        firstDraw = true
    end
    
    function draw()
        if firstDraw then
            print("first draw loop")
            firstDraw = false
        end
    end
    
    function orientationChanged(ori)
        local str = {string.rep("-", 20), "\n", orientations[ori+1], ": ", WIDTH, "x", HEIGHT}
        if previousWidth ~= WIDTH then
            str[#str+1]="\norientation has really changed"
        end
        print(table.concat(str))
        previousWidth = WIDTH
    end
    
  • Posts: 494

    Sorry for bumping an old thread!

    In my current project I had to know when an orientation really changed (or just flipped by 180°) so that I could decide when and how to reposition and rerender some objects.

    I noticed that orientationChanged is fired multiple times and that was not wat I expected from this function. It was also difficult to track and compare current and old WIDTH,HEIGHT values of the screen. So I rewrote Codea's implementation of the orientationChanged() callback. For anyone who encounter the same issues here is my code:

    do
        local _orientationChanged = orientationChanged or function() end
        local portrait = table.concat({PORTRAIT, PORTRAIT_UPSIDE_DOWN, PORTRAIT_ANY}, ",")
        local landscape = table.concat({LANDSCAPE_LEFT, LANDSCAPE_RIGHT, LANDSCAPE_ANY}, ",")
        local prevOrientation = CurrentOrientation
        local prevWidth = WIDTH
        local prevHeight = HEIGHT
    
        local function name(orientation)
            if portrait:find(orientation) then
                return "PORTRAIT"
            else
                return "LANDSCAPE"
            end
        end
    
        local function screen()
            return {
                prevOrientation = prevOrientation,
                currOrientation = CurrentOrientation,
                prevOrientationName = name(prevOrientation),
                currOrientationName = name(CurrentOrientation),
                prevWidth = prevWidth,
                currWidth = WIDTH,
                prevHeight = prevHeight,
                currHeight = HEIGHT
            }
        end
    
        function orientationChanged()
            if prevWidth ~= WIDTH or prevHeight ~= HEIGHT then -- device rotated 90°
                _orientationChanged(screen())
                prevOrientation = CurrentOrientation
                prevWidth = WIDTH
                prevHeight = HEIGHT
            elseif prevOrientation ~= CurrentOrientation then
                if (landscape:find(CurrentOrientation) and landscape:find(prevOrientation)) -- device rotated 180°
                or (portrait:find(CurrentOrientation) and portrait:find(prevOrientation))
                then
                    _orientationChanged(screen())
                    prevOrientation = CurrentOrientation
                end
            end
        end
    end
    

    Put this code into a separate tab and move that tab to the right of the "Main" tab, so that it overrides Codea's original call to orientationChanged. Then use orientationChanged(screen) as before, but now screen variable is a table of values that you can use for better control.

    Use like this:

    function orientationChanged(screen)
        print(string.format(
            "Orientation changed from %d (%s) to %d (%s)",
            screen.prevOrientation,
            screen.prevOrientationName,
            screen.currOrientation,
            screen.currOrientationName
        ))
    
        if screen.prevOrientationName == screen.currOrientationName then
            print("Device was rotated 180°")
            print("Screen dimensions didn't change")
        else
            print("Device was rotated 90°")
            print(string.format(
                "Screen dimensions changed from (%d,%d) to (%d,%d)",
                screen.prevWidth,
                screen.prevHeight,
                screen.currWidth,
                screen.currHeight
            ))
        end
    end
    
  • Posts: 494

    Oh, I did forget to say the best benefit of this code.. the callback will not fire multiple times (randomly) anymore. Just when real changes happen. So you are always up-to-date.

  • dave1707dave1707 Mod
    Posts: 7,470

    @se24vad I'm not sure what you're trying to do in the above code, but here's an example to do screen orientation changes. It shows the WIDTH and HEIGHT values and the screen position. The code in the function orientationChanged is executed only once each orientation change.

    displayMode(FULLSCREEN)
    wHold=WIDTH
    
    function setup()
        cnt=0
        w=WIDTH
        h=HEIGHT
        tab={"PORTRAIT","PORTRAIT_UPSIDE_DOWN","LANDSCAPELEFT","LANDSCAPERIGHT"}   
    end
    
    function draw()
        background(0)
        fill(255)
        text("Width "..w.."     Height "..h.."    Screen changes "..cnt,WIDTH/2,HEIGHT-40)
        text(tab[CurrentOrientation+1],WIDTH/2,HEIGHT-80)
    end
    
    function orientationChanged()
        if wHold~=WIDTH then    -- execute screen change only once
            cnt=cnt+1   -- count the number of screen changes
            wHold=WIDTH -- update value
            w=WIDTH     -- get current screen values
            h=HEIGHT  
        end  
    end
    
  • Posts: 808

    Add a global variable, whitch when called the first time, will be set to false and won't call the function anymore

  • dave1707dave1707 Mod
    Posts: 7,470

    Here's an updated version of my code above. It shows the current screen sizes, the current screen orientation, and the previous screen orientation.

    displayMode(FULLSCREEN)
    currOrien=CurrentOrientation
    prevOrien=CurrentOrientation
    
    function setup()
        tab={"PORTRAIT","PORTRAIT_UPSIDE_DOWN","LANDSCAPELEFT","LANDSCAPERIGHT"}   
    end
    
    function draw()
        background(0)
        fill(255)
        text("Curr Width "..WIDTH.."     Height "..HEIGHT,WIDTH/2,HEIGHT-40)
        text("Current orientation  "..tab[currOrien+1],WIDTH/2,HEIGHT-80)
        text("Previous orientation  "..tab[prevOrien+1],WIDTH/2,HEIGHT-120)
    end
    
    function orientationChanged()
        if currOrien~=CurrentOrientation then    -- execute screen change only once
            prevOrien=currOrien
            currOrien=CurrentOrientation
        end
    end
    
  • dave1707dave1707 Mod
    edited October 2016 Posts: 7,470

    This might explain why orientationChanged gets called multiple times. The first time it's called, I update w1 and h1 with the current WIDTH and HEIGHT. I also update curr with the CurrentOrientation. The second time it get's called, curr is equal to CurrentOrientation so w2 and h2 are updated with the current WIDTH and HEIGHT. I display the values and apparently the WIDTH and HEIGHT variables aren't updated with the correct values the first time orientationChanged is called. Ignore the initial display because orientationChanged gets call about 5 times at startup, but 2 times after that. Rotate the screen and notice that the First time values aren't correct, but the Second ones are.

    displayMode(FULLSCREEN)
    
    function draw()
        background(0)
        fill(255)
        text(" First time   Width "..w1.."     Height "..h1,WIDTH/2,HEIGHT-160)
        text("Second time   Width "..w2.."     Height "..h2,WIDTH/2,HEIGHT-200)
    end
    
    function orientationChanged()
        if curr~=CurrentOrientation then  -- curr not equal to changed orientation
            w1=WIDTH    -- 1st time called, get current width and height
            h1=HEIGHT
            curr=CurrentOrientation
        else    
            w2=WIDTH    -- 2nd time called, get current width and height    
            h2=HEIGHT
        end
    end
    
  • Posts: 494

    @dave1707 that behavior is exactly why I rewrote that callback function of codea.
    Not only does it not fire multiple times anymore, but it also only fires when the correct values for WIDTH and HEIGHT are available.

    I don't want to have stupid "workaround" code in front of my face so I override the default orientationCahnged and store that code in a separate tab. Now I never again have to worry about these things. It just works.

  • Posts: 494

    @dave1707
    Codea calls orientationChanged multiple times before setup(). There is no reason to pass these calls onto the Codea user if nothing has really changed. With my library from above, only 1x call will be passed and this is the reason for it:

    (1) By default Codea doesn't know its screen size upfront, because it doesn't know if the sidebar will be displayed or not. The view is assumed to be FULLSCREEN, but when the app really starts the setup() kicks in and Codea will set displayMode(STANDARD) behind the scenes. The view recalculates itself and orientationChanged gets called, because now the WIDTH and HEIGHT have been really changed by the sidebar.

    (2) If you set displayMode(STANDARD) yourself before setup() then Codea will know its screen size upfront and no call to orientationChanged will occur at all.

    If you set displayMode() somewhere in your code after setup() was called, then the effect will be same as in (1).

    Everytime you do a new call to displayMode(...) or rotate your device a call is made to orientationChanged, because either the orientation or the screen size is changing.

  • dave1707dave1707 Mod
    edited October 2016 Posts: 7,470

    @se24vad You can let orientationChanged run multiple times and still get correct information. In the function orientationChanged below, I get the correct previous orientation the first time it's executed, and WIDTH and HEIGHT will have the correct values the second time it's executed. If you don't need to know the previous orientation, then nothing really needs to be done in orientationChanged. The WIDTH and HEIGHT values will be updated with the correct values the second time it's run. I don't see any problems with it running multiple times.

    displayMode(FULLSCREEN)
    
    function setup()
        tab={"PORTRAIT","PORTRAIT_UPSIDE_DOWN","LANDSCAPELEFT","LANDSCAPERIGHT"}   
        curr=CurrentOrientation
        prev=CurrentOrientation
    end
    
    function draw()
        background(0)
        fill(255)
        text("Current orientation    "..tab[curr+1],WIDTH/2,HEIGHT/2)
        text("Previous orientation    "..tab[prev+1],WIDTH/2,HEIGHT/2-40)
        text("Width  "..WIDTH.."     Height  "..HEIGHT,WIDTH/2,HEIGHT/2-80)
    end
    
    function orientationChanged()
        if curr~=CurrentOrientation then
            prev=curr
            curr=CurrentOrientation
        end   
    end
    
  • Posts: 494

    It's hard for me to the describe my issue without showing you a big chunk of code. But I will not brother you with that since I solved it by now. At some point I might show my project to the public, then I can point you that specific issue, that I wasn't able to resolve with any differen approach.

    I didn't want to battle anyone or say that Codea does it wrong .. I just wanted to share my code for people that might find use for it in a similar case to mine.

    @dave1707 Thank you for taking the time to experiment with this and your motivation to help others!

  • dave1707dave1707 Mod
    Posts: 7,470

    @se24vad Without showing any code, can you explain the issue you were having with currentOrientation being called multiple times. If not, then that's OK. The only issue I know of with currentOrientation is that the WIDTH and HEIGHT values aren't correct the first time it's called.

Sign In or Register to comment.