react-native-custom-splash 2.1.5 ā 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/plugin/src/index.js +2 -1
- package/plugin/src/withCustomSplash.js +216 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-custom-splash",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.2.0",
|
|
4
4
|
"description": "A custom splash screen module for React Native with native iOS and Android support, fully compatible with Expo",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"types": "src/index.d.ts",
|
package/plugin/src/index.js
CHANGED
|
@@ -8,6 +8,7 @@ const {
|
|
|
8
8
|
} = require('@expo/config-plugins');
|
|
9
9
|
const path = require('path');
|
|
10
10
|
const fs = require('fs');
|
|
11
|
+
const withCustomiOSSplash = require('./withCustomSplash');
|
|
11
12
|
|
|
12
13
|
/**
|
|
13
14
|
* Validate and normalize plugin configuration
|
|
@@ -530,6 +531,6 @@ module.exports = (config, props = {}) => {
|
|
|
530
531
|
|
|
531
532
|
return withPlugins(config, [
|
|
532
533
|
[withSplashScreenAndroid, pluginConfig],
|
|
533
|
-
[
|
|
534
|
+
[withCustomiOSSplash, pluginConfig],
|
|
534
535
|
]);
|
|
535
536
|
};
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
const { withDangerousMod } = require('@expo/config-plugins');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* COMPLETE iOS Splash Screen Setup
|
|
7
|
+
* This runs AFTER Expo's default splash and overwrites everything
|
|
8
|
+
*/
|
|
9
|
+
function withCustomiOSSplash(config, pluginConfig) {
|
|
10
|
+
return withDangerousMod(config, [
|
|
11
|
+
'ios',
|
|
12
|
+
async (config) => {
|
|
13
|
+
const projectRoot = config.modRequest.projectRoot;
|
|
14
|
+
const iosPath = config.modRequest.platformProjectRoot;
|
|
15
|
+
const projectName = config.modRequest.projectName;
|
|
16
|
+
|
|
17
|
+
console.log('\nš Setting up custom iOS splash screen...\n');
|
|
18
|
+
|
|
19
|
+
// 1. Update Info.plist
|
|
20
|
+
const plistPath = path.join(iosPath, projectName, 'Info.plist');
|
|
21
|
+
if (fs.existsSync(plistPath)) {
|
|
22
|
+
let plist = fs.readFileSync(plistPath, 'utf8');
|
|
23
|
+
|
|
24
|
+
// Force LaunchScreen
|
|
25
|
+
if (!plist.includes('<key>UILaunchStoryboardName</key>')) {
|
|
26
|
+
plist = plist.replace(
|
|
27
|
+
'</dict>\n</plist>',
|
|
28
|
+
'\t<key>UILaunchStoryboardName</key>\n\t<string>LaunchScreen</string>\n</dict>\n</plist>'
|
|
29
|
+
);
|
|
30
|
+
} else {
|
|
31
|
+
plist = plist.replace(
|
|
32
|
+
/<key>UILaunchStoryboardName<\/key>\s*<string>.*?<\/string>/,
|
|
33
|
+
'<key>UILaunchStoryboardName</key>\n\t<string>LaunchScreen</string>'
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Remove UILaunchScreen dictionary
|
|
38
|
+
plist = plist.replace(/<key>UILaunchScreen<\/key>[\s\S]*?<\/dict>/g, '');
|
|
39
|
+
|
|
40
|
+
fs.writeFileSync(plistPath, plist);
|
|
41
|
+
console.log('ā
Info.plist updated');
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// 2. Delete ALL old storyboards
|
|
45
|
+
const storyboards = [
|
|
46
|
+
path.join(iosPath, projectName, 'SplashScreen.storyboard'),
|
|
47
|
+
path.join(iosPath, projectName, 'LaunchScreen.storyboard'),
|
|
48
|
+
];
|
|
49
|
+
|
|
50
|
+
storyboards.forEach(sb => {
|
|
51
|
+
if (fs.existsSync(sb)) {
|
|
52
|
+
fs.unlinkSync(sb);
|
|
53
|
+
console.log(`šļø Deleted: ${path.basename(sb)}`);
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
// 3. Delete SplashScreenLegacy
|
|
58
|
+
const legacyPath = path.join(iosPath, projectName, 'Images.xcassets', 'SplashScreenLegacy.imageset');
|
|
59
|
+
if (fs.existsSync(legacyPath)) {
|
|
60
|
+
fs.rmSync(legacyPath, { recursive: true });
|
|
61
|
+
console.log('šļø Deleted: SplashScreenLegacy');
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// 4. Delete SplashScreen imageset (from Expo)
|
|
65
|
+
const splashImagePath = path.join(iosPath, projectName, 'Images.xcassets', 'SplashScreen.imageset');
|
|
66
|
+
if (fs.existsSync(splashImagePath)) {
|
|
67
|
+
fs.rmSync(splashImagePath, { recursive: true });
|
|
68
|
+
console.log('šļø Deleted: SplashScreen');
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// 5. Copy user images
|
|
72
|
+
const assetsPath = path.join(iosPath, projectName, 'Images.xcassets');
|
|
73
|
+
let hasImage = false;
|
|
74
|
+
let hasLogo = false;
|
|
75
|
+
|
|
76
|
+
if (pluginConfig.image) {
|
|
77
|
+
const srcImage = path.join(projectRoot, pluginConfig.image);
|
|
78
|
+
if (fs.existsSync(srcImage)) {
|
|
79
|
+
const destDir = path.join(assetsPath, 'splash_image.imageset');
|
|
80
|
+
fs.mkdirSync(destDir, { recursive: true });
|
|
81
|
+
|
|
82
|
+
// Copy for all scales
|
|
83
|
+
['', '@2x', '@3x'].forEach(scale => {
|
|
84
|
+
const destFile = path.join(destDir, `splash_image${scale}.png`);
|
|
85
|
+
fs.copyFileSync(srcImage, destFile);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
// Create Contents.json
|
|
89
|
+
const contents = {
|
|
90
|
+
images: [
|
|
91
|
+
{ idiom: 'universal', filename: 'splash_image.png', scale: '1x' },
|
|
92
|
+
{ idiom: 'universal', filename: 'splash_image@2x.png', scale: '2x' },
|
|
93
|
+
{ idiom: 'universal', filename: 'splash_image@3x.png', scale: '3x' }
|
|
94
|
+
],
|
|
95
|
+
info: { author: 'xcode', version: 1 }
|
|
96
|
+
};
|
|
97
|
+
fs.writeFileSync(path.join(destDir, 'Contents.json'), JSON.stringify(contents, null, 2));
|
|
98
|
+
|
|
99
|
+
hasImage = true;
|
|
100
|
+
console.log('ā
Copied: splash_image');
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (pluginConfig.logo) {
|
|
105
|
+
const srcLogo = path.join(projectRoot, pluginConfig.logo);
|
|
106
|
+
if (fs.existsSync(srcLogo)) {
|
|
107
|
+
const destDir = path.join(assetsPath, 'splash_logo.imageset');
|
|
108
|
+
fs.mkdirSync(destDir, { recursive: true });
|
|
109
|
+
|
|
110
|
+
// Copy for all scales
|
|
111
|
+
['', '@2x', '@3x'].forEach(scale => {
|
|
112
|
+
const destFile = path.join(destDir, `splash_logo${scale}.png`);
|
|
113
|
+
fs.copyFileSync(srcLogo, destFile);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
// Create Contents.json
|
|
117
|
+
const contents = {
|
|
118
|
+
images: [
|
|
119
|
+
{ idiom: 'universal', filename: 'splash_logo.png', scale: '1x' },
|
|
120
|
+
{ idiom: 'universal', filename: 'splash_logo@2x.png', scale: '2x' },
|
|
121
|
+
{ idiom: 'universal', filename: 'splash_logo@3x.png', scale: '3x' }
|
|
122
|
+
],
|
|
123
|
+
info: { author: 'xcode', version: 1 }
|
|
124
|
+
};
|
|
125
|
+
fs.writeFileSync(path.join(destDir, 'Contents.json'), JSON.stringify(contents, null, 2));
|
|
126
|
+
|
|
127
|
+
hasLogo = true;
|
|
128
|
+
console.log('ā
Copied: splash_logo');
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// 6. Create NEW LaunchScreen.storyboard
|
|
133
|
+
const storyboard = createStoryboard(hasImage, hasLogo, pluginConfig.backgroundColor);
|
|
134
|
+
const storyboardPath = path.join(iosPath, projectName, 'LaunchScreen.storyboard');
|
|
135
|
+
fs.writeFileSync(storyboardPath, storyboard);
|
|
136
|
+
|
|
137
|
+
console.log('ā
Created: LaunchScreen.storyboard');
|
|
138
|
+
console.log(` Background: ${hasImage ? 'splash_image ā' : 'color only'}`);
|
|
139
|
+
console.log(` Logo: ${hasLogo ? 'splash_logo ā' : 'none'}`);
|
|
140
|
+
console.log(` Color: ${pluginConfig.backgroundColor}\n`);
|
|
141
|
+
|
|
142
|
+
return config;
|
|
143
|
+
},
|
|
144
|
+
]);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function createStoryboard(hasImage, hasLogo, bgColor) {
|
|
148
|
+
const color = bgColor.replace('#', '');
|
|
149
|
+
const r = parseInt(color.substr(0, 2), 16) / 255;
|
|
150
|
+
const g = parseInt(color.substr(2, 2), 16) / 255;
|
|
151
|
+
const b = parseInt(color.substr(4, 2), 16) / 255;
|
|
152
|
+
|
|
153
|
+
const bgImageView = hasImage ? `
|
|
154
|
+
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="splash_image" translatesAutoresizingMaskIntoConstraints="NO" id="bgImg">
|
|
155
|
+
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
|
|
156
|
+
</imageView>` : '';
|
|
157
|
+
|
|
158
|
+
const logoImageView = hasLogo ? `
|
|
159
|
+
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="splash_logo" translatesAutoresizingMaskIntoConstraints="NO" id="logoImg">
|
|
160
|
+
<rect key="frame" x="132" y="348" width="150" height="150"/>
|
|
161
|
+
<constraints>
|
|
162
|
+
<constraint firstAttribute="width" constant="150" id="logoW"/>
|
|
163
|
+
<constraint firstAttribute="height" constant="150" id="logoH"/>
|
|
164
|
+
</constraints>
|
|
165
|
+
</imageView>` : '';
|
|
166
|
+
|
|
167
|
+
const bgConstraints = hasImage ? `
|
|
168
|
+
<constraint firstItem="bgImg" firstAttribute="top" secondItem="view" secondAttribute="top"/>
|
|
169
|
+
<constraint firstItem="bgImg" firstAttribute="leading" secondItem="view" secondAttribute="leading"/>
|
|
170
|
+
<constraint firstItem="bgImg" firstAttribute="trailing" secondItem="view" secondAttribute="trailing"/>
|
|
171
|
+
<constraint firstItem="bgImg" firstAttribute="bottom" secondItem="view" secondAttribute="bottom"/>` : '';
|
|
172
|
+
|
|
173
|
+
const logoConstraints = hasLogo ? `
|
|
174
|
+
<constraint firstItem="logoImg" firstAttribute="centerX" secondItem="view" secondAttribute="centerX"/>
|
|
175
|
+
<constraint firstItem="logoImg" firstAttribute="centerY" secondItem="view" secondAttribute="centerY"/>` : '';
|
|
176
|
+
|
|
177
|
+
const resources = [];
|
|
178
|
+
if (hasImage) resources.push(' <image name="splash_image" width="1242" height="2688"/>');
|
|
179
|
+
if (hasLogo) resources.push(' <image name="splash_logo" width="512" height="512"/>');
|
|
180
|
+
|
|
181
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
182
|
+
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="21507" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
|
|
183
|
+
<device id="retina6_1" orientation="portrait" appearance="light"/>
|
|
184
|
+
<dependencies>
|
|
185
|
+
<deployment identifier="iOS"/>
|
|
186
|
+
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21505"/>
|
|
187
|
+
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
|
188
|
+
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
|
189
|
+
</dependencies>
|
|
190
|
+
<scenes>
|
|
191
|
+
<scene sceneID="EHf-IW-A2E">
|
|
192
|
+
<objects>
|
|
193
|
+
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
|
|
194
|
+
<view key="view" contentMode="scaleToFill" id="view">
|
|
195
|
+
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
|
|
196
|
+
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
|
197
|
+
<subviews>${bgImageView}${logoImageView}
|
|
198
|
+
</subviews>
|
|
199
|
+
<viewLayoutGuide key="safeArea" id="safeArea"/>
|
|
200
|
+
<color key="backgroundColor" red="${r}" green="${g}" blue="${b}" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
|
201
|
+
<constraints>${bgConstraints}${logoConstraints}
|
|
202
|
+
</constraints>
|
|
203
|
+
</view>
|
|
204
|
+
</viewController>
|
|
205
|
+
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
|
206
|
+
</objects>
|
|
207
|
+
<point key="canvasLocation" x="53" y="375"/>
|
|
208
|
+
</scene>
|
|
209
|
+
</scenes>
|
|
210
|
+
<resources>
|
|
211
|
+
${resources.join('\n')}
|
|
212
|
+
</resources>
|
|
213
|
+
</document>`;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
module.exports = withCustomiOSSplash;
|