Allow use of launch storyboard instead of launch images on iOS

This commit is contained in:
Justin Espedal
2019-11-02 17:30:06 +09:00
committed by Joshua Granick
parent 97a6580752
commit d5e80fa5c1
8 changed files with 373 additions and 38 deletions

View File

@@ -40,6 +40,7 @@ class HXProject extends Script
public var javaPaths:Array<String>;
public var keystore:Keystore;
public var languages:Array<String>;
public var launchStoryboard:LaunchStoryboard;
public var libraries:Array<Library>;
public var libraryHandlers:Map<String, String>;
public var meta:MetaData;
@@ -412,6 +413,11 @@ class HXProject extends Script
}
project.languages = languages.copy();
if (launchStoryboard != null)
{
project.launchStoryboard = launchStoryboard.clone();
}
for (library in libraries)
{
@@ -895,6 +901,15 @@ class HXProject extends Script
keystore.merge(project.keystore);
}
if (launchStoryboard == null)
{
launchStoryboard = project.launchStoryboard;
}
else
{
launchStoryboard.merge(project.launchStoryboard);
}
languages = ArrayTools.concatUnique(languages, project.languages, true);
libraries = ArrayTools.concatUnique(libraries, project.libraries, true);

View File

@@ -8,6 +8,7 @@ import lime.utils.UInt8Array;
#end
import lime.tools.Platform;
import sys.io.File;
import sys.io.FileSeek;
import sys.FileSystem;
class ImageHelper
@@ -132,6 +133,30 @@ class ImageHelper
return null;
}
public static function readPNGImageSize(path:String)
{
var toReturn = {width: 0, height: 0};
var fileInput = File.read(path);
var header = (fileInput.readByte() << 8) | fileInput.readByte();
if (header == 0x8950)
{
fileInput.seek(8 + 4 + 4, FileSeek.SeekBegin);
var width = (fileInput.readByte() << 24) | (fileInput.readByte() << 16) | (fileInput.readByte() << 8) | fileInput.readByte();
var height = (fileInput.readByte() << 24) | (fileInput.readByte() << 16) | (fileInput.readByte() << 8) | fileInput.readByte();
toReturn = {
width: width,
height: height
};
}
fileInput.close();
return toReturn;
}
public static function resizeImage(image:#if (lime && lime_cffi && !macro) Image #else Dynamic #end, width:Int,
height:Int):#if (lime && lime_cffi && !macro) Image #else Dynamic #end
{

View File

@@ -0,0 +1,66 @@
package lime.tools;
import hxp.ObjectTools;
class LaunchStoryboard
{
public var assetsPath:String;
public var assets:Array<LaunchStoryboardAsset>;
public var path:String;
public var template:String;
public var templateContext:Dynamic;
public function new ()
{
assets = [];
templateContext = {};
}
public function clone():LaunchStoryboard
{
var launchStoryboard = new LaunchStoryboard();
launchStoryboard.assetsPath = assetsPath;
launchStoryboard.assets = assets.copy();
launchStoryboard.path = path;
launchStoryboard.template = template;
launchStoryboard.templateContext = ObjectTools.copyFields(templateContext, {});
return launchStoryboard;
}
public function merge(launchStoryboard:LaunchStoryboard):Void
{
if (launchStoryboard != null)
{
if (launchStoryboard.assetsPath != null) assetsPath = launchStoryboard.assetsPath;
if (launchStoryboard.assets != null) assets = launchStoryboard.assets;
if (launchStoryboard.path != null) path = launchStoryboard.path;
if (launchStoryboard.template != null) template = launchStoryboard.template;
if (launchStoryboard.templateContext != null) templateContext = launchStoryboard.templateContext;
}
}
}
class LaunchStoryboardAsset
{
public var type:String;
public function new(type:String)
{
this.type = type;
}
}
class ImageSet extends LaunchStoryboardAsset
{
public var name:String;
public var width = 0;
public var height = 0;
public function new(name:String)
{
super("imageset");
this.name = name;
}
}

View File

@@ -1365,6 +1365,86 @@ class ProjectXMLParser extends HXProject
}
splashScreens.push(splashScreen);
case "launchStoryboard":
if (launchStoryboard == null)
{
launchStoryboard = new LaunchStoryboard();
}
if (element.has.path)
{
launchStoryboard.path = Path.combine(extensionPath, substitute(element.att.path));
}
else if (element.has.name)
{
launchStoryboard.path = Path.combine(extensionPath, substitute(element.att.name));
}
else if (element.has.template)
{
launchStoryboard.template = substitute(element.att.template);
launchStoryboard.templateContext = {};
for (attr in element.x.attributes())
{
if (attr == "assetsPath") continue;
var valueType = "String";
var valueName = attr;
if (valueName.indexOf(":") != -1)
{
valueType = valueName.substring(valueName.lastIndexOf(":") + 1);
valueName = valueName.substring(0, valueName.lastIndexOf(":"));
}
var stringValue = element.x.get(attr);
var value:Dynamic;
switch(valueType)
{
case "Int":
value = Std.parseInt(stringValue);
case "RGB":
var rgb:lime.math.ARGB = Std.parseInt(stringValue);
value = {r: rgb.r/255, g: rgb.g/255, b: rgb.b/255};
case "String":
value = stringValue;
default:
Log.warn("Ignoring unknown value type \"" + valueType + "\" in storyboard configuration.");
value = "";
}
Reflect.setField(launchStoryboard.templateContext, valueName, value);
}
}
if (element.has.assetsPath)
{
launchStoryboard.assetsPath = Path.combine(extensionPath, substitute(element.att.assetsPath));
}
for (childElement in element.elements)
{
var isValid = isValidElement(childElement, "");
if (isValid)
{
switch(childElement.name)
{
case "imageset":
var name = substitute(childElement.att.name);
var imageset = new LaunchStoryboard.ImageSet(name);
if (childElement.has.width)
imageset.width = Std.parseInt(substitute(childElement.att.width));
if (childElement.has.height)
imageset.height = Std.parseInt(substitute(childElement.att.height));
launchStoryboard.assets.push(imageset);
}
}
}
case "icon":
var path = "";

View File

@@ -0,0 +1,48 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14460.31" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" ::if (deploymentVersion.major >= 9)::useSafeAreas="YES"::end:: colorMatched="YES" initialViewController="01J-lp-oVM">
<device id="ipad11_0rounded" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment version="::deploymentVersion.code::" identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14460.20"/>
::if (deploymentVersion.major >= 9)::<capability name="Safe area layout guides" minToolsVersion="9.0"/>::end::
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<rect key="frame" x="0.0" y="0.0" width="834" height="1194"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView userInteractionEnabled="NO" contentMode="::contentMode::" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Oxd-N2-wEZ">
<rect key="frame" x="0.0" y="0.0" width="834" height="1194"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
::if portrait::<variation key="heightClass=regular-widthClass=compact" image="::portrait::"/>::end::
::if landscape::<variation key="heightClass=compact-widthClass=regular" image="::landscape::"/>
<variation key="heightClass=compact-widthClass=compact" image="::landscape::"/>::end::
::if ipad::<variation key="heightClass=regular-widthClass=regular" image="::ipad::"/>::end::
</imageView>
</subviews>
::if bg::
<color key="backgroundColor" red="::bg.r::" green="::bg.g::" blue="::bg.b::" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
::else::
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
::end::
::if (deploymentVersion.major >= 9)::<viewLayoutGuide key="safeArea" id="Bcu-3y-fUS"/>::end::
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="52.5" y="375"/>
</scene>
</scenes>
<resources>
::foreach imagesets::
<image name="::name::" width="::width::" height="::height::"/>
::end::
</resources>
</document>

View File

@@ -24,6 +24,7 @@
4257533F1A5EFD8C004AA45B /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4257533E1A5EFD8C004AA45B /* Images.xcassets */; };
792E75C91C6C876900D01DE0 /* GameController.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 792E75C81C6C876900D01DE0 /* GameController.framework */; };
792E75C91C6C876900D01DE1 /* CoreText.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 792E75C81C6C876900D01DE1 /* CoreText.framework */; };
::if (IOS_LAUNCH_STORYBOARD != null)::D099CA9021A64C87003837AD /* ::IOS_LAUNCH_STORYBOARD::.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D099CA8F21A64C86003837AD /* ::IOS_LAUNCH_STORYBOARD::.storyboard */; };::end::
::ADDL_PBX_BUILD_FILE::
/* End PBXBuildFile section */
@@ -58,6 +59,7 @@
6662F3920A0E282007F4E3E /* ::APP_FILE::.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; name = "::APP_FILE::.entitlements"; path = "::APP_FILE::/::APP_FILE::.entitlements"; sourceTree = "<group>"; };
792E75C81C6C876900D01DE0 /* GameController.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = GameController.framework; path = System/Library/Frameworks/GameController.framework; sourceTree = SDKROOT; };
792E75C81C6C876900D01DE1 /* CoreText.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreText.framework; path = System/Library/Frameworks/CoreText.framework; sourceTree = SDKROOT; };
::if (IOS_LAUNCH_STORYBOARD != null)::D099CA8F21A64C86003837AD /* ::IOS_LAUNCH_STORYBOARD::.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = "::APP_FILE::/::IOS_LAUNCH_STORYBOARD::.storyboard"; sourceTree = "<group>"; };::end::
::ADDL_PBX_FILE_REFERENCE::
8D1107310486CEB800E47090 /* ::APP_FILE::-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "::APP_FILE::/::APP_FILE::-Info.plist"; plistStructureDefinitionIdentifier = "com.apple.xcode.plist.structure-definition.iphone.info-plist"; sourceTree = "<group>"; };
@@ -153,6 +155,7 @@
isa = PBXGroup;
children = (
4257533E1A5EFD8C004AA45B /* Images.xcassets */,
::if (IOS_LAUNCH_STORYBOARD != null)::D099CA8F21A64C86003837AD /* ::IOS_LAUNCH_STORYBOARD::.storyboard */,::end::
8D1107310486CEB800E47090 /* ::APP_FILE::/::APP_FILE::-Info.plist */,
1E2E17A5131E8B5D0048F3C7 /* ::APP_FILE::/assets */,
);
@@ -229,6 +232,7 @@
buildActionMask = 2147483647;
files = (
1E2E17AD131E8B5D0048F3C7 /* ::APP_FILE::/assets in Resources */,
::if (IOS_LAUNCH_STORYBOARD != null)::D099CA9021A64C87003837AD /* ::IOS_LAUNCH_STORYBOARD::.storyboard in Resources */,::end::
4257533F1A5EFD8C004AA45B /* Images.xcassets in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -345,7 +349,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
::if (IOS_LAUNCH_STORYBOARD == null)::ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;::end::
CODE_SIGN_ENTITLEMENTS = "::APP_FILE::/::APP_FILE::.entitlements";
::if DEVELOPMENT_TEAM_ID::DEVELOPMENT_TEAM = ::DEVELOPMENT_TEAM_ID::;::end::
ENABLE_BITCODE = ::if (ENABLE_BITCODE)::YES::else::NO::end::;
@@ -401,7 +405,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
::if (IOS_LAUNCH_STORYBOARD == null)::ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;::end::
CODE_SIGN_ENTITLEMENTS = "::APP_FILE::/::APP_FILE::.entitlements";
::if DEVELOPMENT_TEAM_ID::DEVELOPMENT_TEAM = ::DEVELOPMENT_TEAM_ID::;::end::
ENABLE_BITCODE = ::if (ENABLE_BITCODE)::YES::else::NO::end::;

View File

@@ -28,6 +28,8 @@
<string>::APP_BUILD_NUMBER::</string>
<key>LSRequiresIPhoneOS</key>
<true/>
::if (IOS_LAUNCH_STORYBOARD != null)::<key>UILaunchStoryboardName</key>
<string>::IOS_LAUNCH_STORYBOARD::</string>::end::
<key>UIRequiredDeviceCapabilities</key>
<dict>
::foreach REQUIRED_CAPABILITY::<key>::name::</key>

View File

@@ -21,8 +21,10 @@ import lime.tools.DeploymentHelper;
import lime.tools.HXProject;
import lime.tools.Icon;
import lime.tools.IconHelper;
import lime.tools.ImageHelper;
import lime.tools.IOSHelper;
import lime.tools.Keystore;
import lime.tools.LaunchStoryboard;
import lime.tools.Platform;
import lime.tools.PlatformTarget;
import lime.tools.ProjectHelper;
@@ -501,54 +503,147 @@ class IOSPlatform extends PlatformTarget
}
}
var splashSizes:Array<SplashSize> = [
{name: "Default.png", w: 320, h: 480}, // iPhone, portrait {name: "Default@2x.png", w: 640, h: 960}, // iPhone Retina, portrait
{name: "Default-568h@2x.png", w: 640, h: 1136}, // iPhone 5, portrait {name: "Default-667h@2x.png", w: 750, h: 1334}, // iPhone 6, portrait
{name: "Default-736h@3x.png", w: 1242, h: 2208}, // iPhone 6 Plus, portrait {name: "Default-Landscape.png", w: 1024, h: 768}, // iPad, landscape
{name: "Default-Landscape@2x.png", w: 2048, h: 1536}, // iPad Retina, landscape {name: "Default-736h-Landscape@3x.png", w: 2208, h: 1242},
// iPhone 6 Plus, landscape
{name: "Default-Portrait.png", w: 768, h: 1024}, // iPad, portrait {name: "Default-Portrait@2x.png", w: 1536, h: 2048},
// iPad Retina, portrait
{name: "Default-812h@3x.png", w: 1125, h: 2436}, // iPhone X, portrait
{name: "Default-Landscape-812h@3x.png", w: 2436, h: 1125} // iPhone X, landscape
];
var splashScreenPath = Path.combine(projectDirectory, "Images.xcassets/LaunchImage.launchimage");
System.mkdir(splashScreenPath);
for (size in splashSizes)
if (project.launchStoryboard != null)
{
var match = false;
for (splashScreen in project.splashScreens)
var sb = project.launchStoryboard;
var assetsPath = sb.assetsPath;
var imagesets = [];
for (asset in sb.assets)
{
if (splashScreen.width == size.w && splashScreen.height == size.h && Path.extension(splashScreen.path) == "png")
switch (asset.type)
{
System.copyFile(splashScreen.path, Path.combine(splashScreenPath, size.name));
match = true;
case "imageset":
var imageset = cast(asset, ImageSet);
imagesets.push(imageset);
var imagesetPath = Path.combine(projectDirectory, "Images.xcassets/" + imageset.name + ".imageset");
System.mkdir(imagesetPath);
var baseImageName = Path.withoutExtension(imageset.name);
var imageScales = ["1x", "2x", "3x"];
var images = [];
for (scale in imageScales)
{
var filename = baseImageName + (scale == "1x" ? "" : "@"+scale) + ".png";
if (FileSystem.exists(Path.combine(assetsPath, filename)))
{
images.push({idiom: "universal", filename: filename, scale: scale});
System.copyFile(Path.combine(assetsPath, filename), Path.combine(imagesetPath, filename));
if (imageset.width == 0 || imageset.height == 0)
{
var dim = ImageHelper.readPNGImageSize(Path.combine(assetsPath, filename));
var scaleValue = Std.parseInt(scale.charAt(0));
imageset.width = Std.int(dim.width / scaleValue);
imageset.height = Std.int(dim.height / scaleValue);
}
}
}
var contents = {
images: images,
info: {
version: "1",
author: "xcode"
}
};
File.saveContent(Path.combine(imagesetPath, "Contents.json"), Json.stringify(contents));
default:
}
}
if (!match)
if (sb.template != null)
{
var imagePath = Path.combine(splashScreenPath, size.name);
if (!FileSystem.exists(imagePath))
sb.templateContext.imagesets = [];
for (imageset in imagesets)
{
#if (lime && lime_cffi && !macro)
Log.info("", " - \x1b[1mGenerating image:\x1b[0m " + imagePath);
var image = new Image(null, 0, 0, size.w, size.h, (0xFF << 24) | (project.window.background & 0xFFFFFF));
var bytes = image.encode(PNG);
File.saveBytes(imagePath, bytes);
#end
sb.templateContext.imagesets.push({
name: imageset.name,
width: imageset.width,
height: imageset.height,
});
}
var deployment:String = context.DEPLOYMENT;
var parts = deployment.split(".");
var major = Std.parseInt(parts[0]);
var minor = parts.length >= 2 ? Std.parseInt(parts[1]) : 0;
var patch = parts.length >= 3 ? Std.parseInt(parts[2]) : 0;
Reflect.setField(sb.templateContext, "deploymentVersion", {
major: major,
minor: minor,
patch: patch,
code: Std.parseInt("0x" + major + minor + patch)
});
System.copyFileTemplate(project.templatePaths, "ios/storyboards/" + sb.template, projectDirectory + sb.template, sb.templateContext, true, true);
context.IOS_LAUNCH_STORYBOARD = Path.withoutExtension(sb.template);
}
else
{
System.copyFile(sb.path, projectDirectory + Path.withoutDirectory(sb.path));
context.IOS_LAUNCH_STORYBOARD = Path.withoutDirectory(Path.withoutExtension(sb.path));
}
}
else
{
var splashSizes:Array<SplashSize> = [
{name: "Default.png", w: 320, h: 480}, // iPhone, portrait {name: "Default@2x.png", w: 640, h: 960}, // iPhone Retina, portrait
{name: "Default-568h@2x.png", w: 640, h: 1136}, // iPhone 5, portrait {name: "Default-667h@2x.png", w: 750, h: 1334}, // iPhone 6, portrait
{name: "Default-736h@3x.png", w: 1242, h: 2208}, // iPhone 6 Plus, portrait {name: "Default-Landscape.png", w: 1024, h: 768}, // iPad, landscape
{name: "Default-Landscape@2x.png", w: 2048, h: 1536}, // iPad Retina, landscape {name: "Default-736h-Landscape@3x.png", w: 2208, h: 1242},
// iPhone 6 Plus, landscape
{name: "Default-Portrait.png", w: 768, h: 1024}, // iPad, portrait {name: "Default-Portrait@2x.png", w: 1536, h: 2048},
// iPad Retina, portrait
{name: "Default-812h@3x.png", w: 1125, h: 2436}, // iPhone X, portrait
{name: "Default-Landscape-812h@3x.png", w: 2436, h: 1125} // iPhone X, landscape
];
context.HAS_LAUNCH_IMAGE = true;
var splashScreenPath = Path.combine(projectDirectory, "Images.xcassets/LaunchImage.launchimage");
System.mkdir(splashScreenPath);
for (size in splashSizes)
{
var match = false;
for (splashScreen in project.splashScreens)
{
if (splashScreen.width == size.w && splashScreen.height == size.h && Path.extension(splashScreen.path) == "png")
{
System.copyFile(splashScreen.path, Path.combine(splashScreenPath, size.name));
match = true;
}
}
if (!match)
{
var imagePath = Path.combine(splashScreenPath, size.name);
if (!FileSystem.exists(imagePath))
{
#if (lime && lime_cffi && !macro)
Log.info("", " - \x1b[1mGenerating image:\x1b[0m " + imagePath);
var image = new Image(null, 0, 0, size.w, size.h, (0xFF << 24) | (project.window.background & 0xFFFFFF));
var bytes = image.encode(PNG);
File.saveBytes(imagePath, bytes);
#end
}
}
}
context.HAS_LAUNCH_IMAGE = true;
}
System.mkdir(projectDirectory + "/resources");
System.mkdir(projectDirectory + "/haxe/build");