react-native-custom-splash 2.1.4 ā 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 +47 -8
- 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
|
|
@@ -437,38 +438,76 @@ const withSplashScreenIOS = (config, pluginConfig) => {
|
|
|
437
438
|
const iosProjectPath = config.modRequest.platformProjectRoot;
|
|
438
439
|
const projectName = config.modRequest.projectName;
|
|
439
440
|
|
|
440
|
-
// Step 1: Update Info.plist
|
|
441
|
+
// Step 1: Update Info.plist to use LaunchScreen
|
|
441
442
|
const infoPlistPath = path.join(iosProjectPath, projectName, 'Info.plist');
|
|
442
443
|
if (fs.existsSync(infoPlistPath)) {
|
|
443
444
|
let infoPlist = fs.readFileSync(infoPlistPath, 'utf8');
|
|
445
|
+
|
|
446
|
+
// Update to use LaunchScreen
|
|
444
447
|
if (!infoPlist.includes('<key>UILaunchStoryboardName</key>')) {
|
|
445
448
|
infoPlist = infoPlist.replace('</dict>\n</plist>', '\t<key>UILaunchStoryboardName</key>\n\t<string>LaunchScreen</string>\n</dict>\n</plist>');
|
|
449
|
+
} else {
|
|
450
|
+
// Update existing value to LaunchScreen
|
|
451
|
+
infoPlist = infoPlist.replace(
|
|
452
|
+
/<key>UILaunchStoryboardName<\/key>\s*<string>.*?<\/string>/,
|
|
453
|
+
'<key>UILaunchStoryboardName</key>\n\t<string>LaunchScreen</string>'
|
|
454
|
+
);
|
|
446
455
|
}
|
|
456
|
+
|
|
457
|
+
// Remove UILaunchScreen dictionary if present
|
|
447
458
|
infoPlist = infoPlist.replace(/<key>UILaunchScreen<\/key>[\s\S]*?<\/dict>/g, '');
|
|
459
|
+
|
|
448
460
|
fs.writeFileSync(infoPlistPath, infoPlist);
|
|
449
461
|
}
|
|
450
462
|
|
|
463
|
+
// Step 2: Delete old/existing storyboards
|
|
464
|
+
const oldStoryboards = [
|
|
465
|
+
path.join(iosProjectPath, projectName, 'SplashScreen.storyboard'),
|
|
466
|
+
path.join(iosProjectPath, projectName, 'LaunchScreen.storyboard'),
|
|
467
|
+
];
|
|
468
|
+
|
|
469
|
+
oldStoryboards.forEach(storyPath => {
|
|
470
|
+
if (fs.existsSync(storyPath)) {
|
|
471
|
+
fs.unlinkSync(storyPath);
|
|
472
|
+
console.log(`šļø Removed old storyboard: ${path.basename(storyPath)}`);
|
|
473
|
+
}
|
|
474
|
+
});
|
|
475
|
+
|
|
476
|
+
// Step 3: Remove SplashScreenLegacy from Images.xcassets
|
|
477
|
+
const legacyImagePath = path.join(iosProjectPath, projectName, 'Images.xcassets', 'SplashScreenLegacy.imageset');
|
|
478
|
+
if (fs.existsSync(legacyImagePath)) {
|
|
479
|
+
fs.rmSync(legacyImagePath, { recursive: true, force: true });
|
|
480
|
+
console.log('šļø Removed SplashScreenLegacy imageset');
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
// Step 4: Copy user images to iOS assets
|
|
451
484
|
let hasImage = false;
|
|
452
485
|
let hasLogo = false;
|
|
453
486
|
|
|
454
487
|
if (pluginConfig.image) {
|
|
455
488
|
hasImage = await copyImageToIOS(projectRoot, pluginConfig.image, 'splash_image', iosProjectPath, projectName);
|
|
489
|
+
if (hasImage) {
|
|
490
|
+
console.log('ā
Background image copied: splash_image');
|
|
491
|
+
}
|
|
456
492
|
}
|
|
493
|
+
|
|
457
494
|
if (pluginConfig.logo) {
|
|
458
495
|
hasLogo = await copyImageToIOS(projectRoot, pluginConfig.logo, 'splash_logo', iosProjectPath, projectName);
|
|
496
|
+
if (hasLogo) {
|
|
497
|
+
console.log('ā
Logo image copied: splash_logo');
|
|
498
|
+
}
|
|
459
499
|
}
|
|
460
500
|
|
|
501
|
+
// Step 5: Create fresh LaunchScreen.storyboard with user images
|
|
461
502
|
const launchScreenPath = path.join(iosProjectPath, projectName, 'LaunchScreen.storyboard');
|
|
462
503
|
const storyboardContent = createLaunchScreenStoryboard(hasImage, hasLogo, pluginConfig.backgroundColor);
|
|
463
504
|
fs.writeFileSync(launchScreenPath, storyboardContent);
|
|
464
505
|
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
}
|
|
506
|
+
console.log('ā
LaunchScreen.storyboard created with YOUR images!');
|
|
507
|
+
console.log(` - Background: ${hasImage ? 'splash_image' : 'color only'}`);
|
|
508
|
+
console.log(` - Logo: ${hasLogo ? 'splash_logo' : 'none'}`);
|
|
509
|
+
console.log(` - Color: ${pluginConfig.backgroundColor}`);
|
|
470
510
|
|
|
471
|
-
console.log('ā
iOS LaunchScreen configured!');
|
|
472
511
|
return config;
|
|
473
512
|
},
|
|
474
513
|
]);
|
|
@@ -492,6 +531,6 @@ module.exports = (config, props = {}) => {
|
|
|
492
531
|
|
|
493
532
|
return withPlugins(config, [
|
|
494
533
|
[withSplashScreenAndroid, pluginConfig],
|
|
495
|
-
[
|
|
534
|
+
[withCustomiOSSplash, pluginConfig],
|
|
496
535
|
]);
|
|
497
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;
|