OrientationChanged called multiple times at startup?

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?

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.

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.

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!

@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), "\
", orientations[ori+1], ": ", WIDTH, "x", HEIGHT}
    if previousWidth ~= WIDTH then
        str[#str+1]="\
orientation has really changed"
    end
    print(table.concat(str))
    previousWidth = WIDTH
end

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

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.

@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

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

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

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

@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.

@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.

@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

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!

@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.