iOS app: storyboard plist entries produced by Projucer

Hi all,

Another quick question on iOS apps. The Projucer adds a LaunchScreen.storyboard to the XCode project, and the following key in the plist file:


However, it seems that the story board data is not taken into account when running in any of the simulators. For example the app appears as fullscreen instead of appearing within the safe area defined in the storyboard. However, if I change the above two lines in the plist file to the following:


It actually works as expected, in the sense that the safe area defined in the story board is actually taken into account.

Is this expected behavior? Or is it a bug somewhere?

Are you using a custom storyboard? The autogenerated one is empty and just ensures that the app is the correct resolution on all screens but if you want custom behaviour like safe areas then you’ll need to set the “Custom Launch Storyboard” option in the Projucer iOS exporter.

Thanks ed95 for your reply! And perhaps I should have a bit more clear in my first message.

I have a plugin project in the Projucer, and I’m simply adding an iOS exporter with stand alone and auV3 wrappers. If I do so, a storyboard is automatically created and included in Xcode, which has the safe area defined as well (Juce v5.4.3; Xcode 10.2.1).

Now any changes I make to this auto-generated storyboard aren’t applied when running the app in the simulator, nor are the safe areas applied. The App appears full screen and on iPhone X is partially under the ‘notch’.

All I had to change is the plist described above to make the storyboard do the right thing. I’m not using any custom story boards nor did I put in a custom story board entry in the Projucer project.

Perhaps I’m doing something wrong though?

No, what you’re seeing is the project without a storyboard. When you modify the plist to use NSMainStoryboardFile instead of UILaunchStoryboardName then the setting is ignored because, as the name suggests, NSMainStoryboardFile only applies to macOS apps. As there is no storyboard being used the app resolution is much smaller and is no longer fullscreen so it might look like it’s using the safe area that you defined, but it isn’t. If you want to use a custom storyboard with a different safe area other than fullscreen then you will need to create your own .storyboard file, and add the path to it to the “Custom Launch Storyboard” setting in the iOS exporter (see this post for more info).

Ahhhh! Thanks ed95, that clears up a lot for me! Ok, custom story board is the way to go… Thanks again!

Hmm I’m still going in circles even with a custom storyboard.

I can easily include a custom storyboard, and set that in the projucer, which populates the various plist entries etc in Xcode.

However, whatever I put in my custom storyboard, I cannot restrict the standalone JUCE app to the safe area, whatever I put in my custom storyboard. FYI I’m trying to do this with an auV3 plugin (sizing works fine for the auV3 wrapper) but the stand-alone one wrapper is giving me head aches wrt the notch on iPhone X.

Perhaps I’m doing something that is impossible, or needs changes in the code instead of the storyboard? So let me perhaps ask a couple of concrete questions:

  1. Is it possible to include a (custom) storyboard for iOS for which the JUCE stand-alone plugin wrapper app will get restricted to the safe area, such that the notch on iPhone X++ does not cover any of the JUCE app?

  2. If so, is there a simple tutorial on how to do this? If not, any pointers to what lines of code need to be changed inside JUCE to rescale the editor such that it follows the safe area instead of being full screen?

Really sorry to ask these noobish questions. I’ve been looking at this for 2-3 days and I must be overlooking something as I can’t get it to work… Also the other discussions on this forum haven’t really helped me…

Thanks much for any hints on how to do this!

Sorry, no solution here. But I am struggling with a similar thing (iOS Multitasking). I tried to implement multitasking for iOS but did not manage to get it to work yet. JUCE always fills the whole screen (though a part may be invisible). It ignores the bounds given by iOS or the storyboard.

Thanks. I also have the observation that whatever we put in a custom launchscreen, the JUCE app will always be fullscreen.

If someone from the JUCE team has a working launchscreen that puts some bounds onto the JUCE app, would you be willing to share that file? @ed95, would this be something that can be shared if you have an example that works with the JUCE plugin client / stand alone wrapper?

So digging a bit further, and trying it with a JUCE standalone app, I see the same issues. The Storyboard has no effect on the app appearance.

In the auto-generated Main.cpp, the app is set to fullscreen for iOS (line 72). This, in turn sets the window to: Desktop::getInstance().getDisplays().getMainDisplay().userArea which is supposedly the area that is not covered by objects like the task bar etc, in contrast to the totalArea.

Then in, I read this (line 746):
d.userArea = d.totalArea = UIViewComponentPeer::realScreenPosToRotated (convertToRectInt ([s bounds])) / masterScale;

In other words, the userArea and totalArea are always the same and there is no dependency on anything defined in the storyboard?

I’m having the same issue with my JUCE UI appearing under the notch and not obeying the Safe Area on iPhone X / 11.

Does anyone have a solution for this?

As far as I am aware there is no easy solution to the moment to bound your app to the safe area.

What we’ve done (and others probably did something similar) is to define a bounding box in the app’s editor yourself, and make it’s size dependent on what platform / OS the app is running (the bounding box is smaller when running on iOS so that the iPhone notch doesn’t block the GUI). Not great because you loose some precious screen real estate that way but it works.

1 Like

You could do this kind of minimally invasive surgery in your JUCE fork:

Then something like this in resize() will get you the safe area converted to your editor or whatever component local coordinates:

if (auto peer = getPeer()) 
    auto& pc       = peer->getComponent();
    auto  safeArea = getLocalArea(&pc, peer->getSafeAreaInsets().subtractedFrom(pc.getLocalBounds()));

Or further, something like this to get the part of local bounds that’s safe to use for your layout:

safeArea = getLocalBounds().getIntersection(safeArea);  

If you would rather use some #if JUCE_IOS macros rather than forking JUCE, I guess you use this in a .mm file where you keep your iOS-specific stuff:


and convert that to juce::BorderSize or whatever

That being said, can we please get it into JUCE?

1 Like

@amethystdeceiver Thank you! I was having the same problem.

Without an official update from the JUCE team, this is probably the most elegant solution for now.

1 Like