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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-custom-splash",
3
- "version": "2.1.5",
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",
@@ -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
- [withSplashScreenIOS, pluginConfig],
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;