Touchgui/plugins/org.apache.cordova.splashscreen/src/ios/CDVSplashScreen.m

changeset 0
e8ccd40d0ef6
equal deleted inserted replaced
-1:000000000000 0:aa436f1b981c
1 /*
2 Licensed to the Apache Software Foundation (ASF) under one
3 or more contributor license agreements. See the NOTICE file
4 distributed with this work for additional information
5 regarding copyright ownership. The ASF licenses this file
6 to you under the Apache License, Version 2.0 (the
7 "License"); you may not use this file except in compliance
8 with the License. You may obtain a copy of the License at
9
10 http://www.apache.org/licenses/LICENSE-2.0
11
12 Unless required by applicable law or agreed to in writing,
13 software distributed under the License is distributed on an
14 "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 KIND, either express or implied. See the License for the
16 specific language governing permissions and limitations
17 under the License.
18 */
19
20 #import "CDVSplashScreen.h"
21 #import <Cordova/CDVViewController.h>
22 #import <Cordova/CDVScreenOrientationDelegate.h>
23
24 #define kSplashScreenDurationDefault 0.25f
25
26
27 @implementation CDVSplashScreen
28
29 - (void)pluginInitialize
30 {
31 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(pageDidLoad) name:CDVPageDidLoadNotification object:self.webView];
32
33 [self setVisible:YES];
34 }
35
36 - (void)show:(CDVInvokedUrlCommand*)command
37 {
38 [self setVisible:YES];
39 }
40
41 - (void)hide:(CDVInvokedUrlCommand*)command
42 {
43 [self setVisible:NO];
44 }
45
46 - (void)pageDidLoad
47 {
48 id autoHideSplashScreenValue = [self.commandDelegate.settings objectForKey:[@"AutoHideSplashScreen" lowercaseString]];
49
50 // if value is missing, default to yes
51 if ((autoHideSplashScreenValue == nil) || [autoHideSplashScreenValue boolValue]) {
52 [self setVisible:NO];
53 }
54 }
55
56 - (void)observeValueForKeyPath:(NSString*)keyPath ofObject:(id)object change:(NSDictionary*)change context:(void*)context
57 {
58 [self updateImage];
59 }
60
61 - (void)createViews
62 {
63 /*
64 * The Activity View is the top spinning throbber in the status/battery bar. We init it with the default Grey Style.
65 *
66 * whiteLarge = UIActivityIndicatorViewStyleWhiteLarge
67 * white = UIActivityIndicatorViewStyleWhite
68 * gray = UIActivityIndicatorViewStyleGray
69 *
70 */
71 NSString* topActivityIndicator = [self.commandDelegate.settings objectForKey:[@"TopActivityIndicator" lowercaseString]];
72 UIActivityIndicatorViewStyle topActivityIndicatorStyle = UIActivityIndicatorViewStyleGray;
73
74 if ([topActivityIndicator isEqualToString:@"whiteLarge"]) {
75 topActivityIndicatorStyle = UIActivityIndicatorViewStyleWhiteLarge;
76 } else if ([topActivityIndicator isEqualToString:@"white"]) {
77 topActivityIndicatorStyle = UIActivityIndicatorViewStyleWhite;
78 } else if ([topActivityIndicator isEqualToString:@"gray"]) {
79 topActivityIndicatorStyle = UIActivityIndicatorViewStyleGray;
80 }
81
82 UIView* parentView = self.viewController.view;
83 parentView.userInteractionEnabled = NO; // disable user interaction while splashscreen is shown
84 _activityView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:topActivityIndicatorStyle];
85 _activityView.center = CGPointMake(parentView.bounds.size.width / 2, parentView.bounds.size.height / 2);
86 _activityView.autoresizingMask = UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleLeftMargin
87 | UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleRightMargin;
88 [_activityView startAnimating];
89
90 // Set the frame & image later.
91 _imageView = [[UIImageView alloc] init];
92 [parentView addSubview:_imageView];
93
94 id showSplashScreenSpinnerValue = [self.commandDelegate.settings objectForKey:[@"ShowSplashScreenSpinner" lowercaseString]];
95 // backwards compatibility - if key is missing, default to true
96 if ((showSplashScreenSpinnerValue == nil) || [showSplashScreenSpinnerValue boolValue]) {
97 [parentView addSubview:_activityView];
98 }
99
100 // Frame is required when launching in portrait mode.
101 // Bounds for landscape since it captures the rotation.
102 [parentView addObserver:self forKeyPath:@"frame" options:0 context:nil];
103 [parentView addObserver:self forKeyPath:@"bounds" options:0 context:nil];
104
105 [self updateImage];
106 }
107
108 - (void)destroyViews
109 {
110 [_imageView removeFromSuperview];
111 [_activityView removeFromSuperview];
112 _imageView = nil;
113 _activityView = nil;
114 _curImageName = nil;
115
116 self.viewController.view.userInteractionEnabled = YES; // re-enable user interaction upon completion
117 [self.viewController.view removeObserver:self forKeyPath:@"frame"];
118 [self.viewController.view removeObserver:self forKeyPath:@"bounds"];
119 }
120
121 - (CDV_iOSDevice) getCurrentDevice
122 {
123 CDV_iOSDevice device;
124
125 UIScreen* mainScreen = [UIScreen mainScreen];
126 CGFloat mainScreenHeight = mainScreen.bounds.size.height;
127 CGFloat mainScreenWidth = mainScreen.bounds.size.width;
128
129 int limit = MAX(mainScreenHeight,mainScreenWidth);
130
131 device.iPad = (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad);
132 device.iPhone = (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone);
133 device.retina = ([mainScreen scale] == 2.0);
134 device.iPhone5 = (device.iPhone && limit == 568.0);
135 // note these below is not a true device detect, for example if you are on an
136 // iPhone 6/6+ but the app is scaled it will prob set iPhone5 as true, but
137 // this is appropriate for detecting the runtime screen environment
138 device.iPhone6 = (device.iPhone && limit == 667.0);
139 device.iPhone6Plus = (device.iPhone && limit == 736.0);
140
141 return device;
142 }
143
144 - (NSString*)getImageName:(UIInterfaceOrientation)currentOrientation delegate:(id<CDVScreenOrientationDelegate>)orientationDelegate device:(CDV_iOSDevice)device
145 {
146 // Use UILaunchImageFile if specified in plist. Otherwise, use Default.
147 NSString* imageName = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"UILaunchImageFile"];
148
149 NSUInteger supportedOrientations = [orientationDelegate supportedInterfaceOrientations];
150
151 // Checks to see if the developer has locked the orientation to use only one of Portrait or Landscape
152 BOOL supportsLandscape = (supportedOrientations & UIInterfaceOrientationMaskLandscape);
153 BOOL supportsPortrait = (supportedOrientations & UIInterfaceOrientationMaskPortrait || supportedOrientations & UIInterfaceOrientationMaskPortraitUpsideDown);
154 // this means there are no mixed orientations in there
155 BOOL isOrientationLocked = !(supportsPortrait && supportsLandscape);
156
157 if (imageName) {
158 imageName = [imageName stringByDeletingPathExtension];
159 } else {
160 imageName = @"Default";
161 }
162
163 if (device.iPhone5) { // does not support landscape
164 imageName = [imageName stringByAppendingString:@"-568h"];
165 } else if (device.iPhone6) { // does not support landscape
166 imageName = [imageName stringByAppendingString:@"-667h"];
167 } else if (device.iPhone6Plus) { // supports landscape
168 if (isOrientationLocked) {
169 imageName = [imageName stringByAppendingString:(supportsLandscape ? @"-Landscape" : @"")];
170 } else {
171 switch (currentOrientation) {
172 case UIInterfaceOrientationLandscapeLeft:
173 case UIInterfaceOrientationLandscapeRight:
174 imageName = [imageName stringByAppendingString:@"-Landscape"];
175 break;
176 default:
177 break;
178 }
179 }
180 imageName = [imageName stringByAppendingString:@"-736h"];
181
182 } else if (device.iPad) { // supports landscape
183 if (isOrientationLocked) {
184 imageName = [imageName stringByAppendingString:(supportsLandscape ? @"-Landscape" : @"-Portrait")];
185 } else {
186 switch (currentOrientation) {
187 case UIInterfaceOrientationLandscapeLeft:
188 case UIInterfaceOrientationLandscapeRight:
189 imageName = [imageName stringByAppendingString:@"-Landscape"];
190 break;
191
192 case UIInterfaceOrientationPortrait:
193 case UIInterfaceOrientationPortraitUpsideDown:
194 default:
195 imageName = [imageName stringByAppendingString:@"-Portrait"];
196 break;
197 }
198 }
199 }
200
201 return imageName;
202 }
203
204 // Sets the view's frame and image.
205 - (void)updateImage
206 {
207 NSString* imageName = [self getImageName:self.viewController.interfaceOrientation delegate:(id<CDVScreenOrientationDelegate>)self.viewController device:[self getCurrentDevice]];
208
209 if (![imageName isEqualToString:_curImageName]) {
210 UIImage* img = [UIImage imageNamed:imageName];
211 _imageView.image = img;
212 _curImageName = imageName;
213 }
214
215 // Check that splash screen's image exists before updating bounds
216 if (_imageView.image) {
217 [self updateBounds];
218 } else {
219 NSLog(@"WARNING: The splashscreen image named %@ was not found", imageName);
220 }
221 }
222
223 - (void)updateBounds
224 {
225 UIImage* img = _imageView.image;
226 CGRect imgBounds = (img) ? CGRectMake(0, 0, img.size.width, img.size.height) : CGRectZero;
227
228 CGSize screenSize = [self.viewController.view convertRect:[UIScreen mainScreen].bounds fromView:nil].size;
229 UIInterfaceOrientation orientation = self.viewController.interfaceOrientation;
230 CGAffineTransform imgTransform = CGAffineTransformIdentity;
231
232 /* If and only if an iPhone application is landscape-only as per
233 * UISupportedInterfaceOrientations, the view controller's orientation is
234 * landscape. In this case the image must be rotated in order to appear
235 * correctly.
236 */
237 if (UIInterfaceOrientationIsLandscape(orientation) && !CDV_IsIPad()) {
238 imgTransform = CGAffineTransformMakeRotation(M_PI / 2);
239 imgBounds.size = CGSizeMake(imgBounds.size.height, imgBounds.size.width);
240 }
241
242 // There's a special case when the image is the size of the screen.
243 if (CGSizeEqualToSize(screenSize, imgBounds.size)) {
244 CGRect statusFrame = [self.viewController.view convertRect:[UIApplication sharedApplication].statusBarFrame fromView:nil];
245 if (!(IsAtLeastiOSVersion(@"7.0"))) {
246 imgBounds.origin.y -= statusFrame.size.height;
247 }
248 } else if (imgBounds.size.width > 0) {
249 CGRect viewBounds = self.viewController.view.bounds;
250 CGFloat imgAspect = imgBounds.size.width / imgBounds.size.height;
251 CGFloat viewAspect = viewBounds.size.width / viewBounds.size.height;
252 // This matches the behaviour of the native splash screen.
253 CGFloat ratio;
254 if (viewAspect > imgAspect) {
255 ratio = viewBounds.size.width / imgBounds.size.width;
256 } else {
257 ratio = viewBounds.size.height / imgBounds.size.height;
258 }
259 imgBounds.size.height *= ratio;
260 imgBounds.size.width *= ratio;
261 }
262
263 _imageView.transform = imgTransform;
264 _imageView.frame = imgBounds;
265 }
266
267 - (void)setVisible:(BOOL)visible
268 {
269 if (visible == _visible) {
270 return;
271 }
272 _visible = visible;
273
274 id fadeSplashScreenValue = [self.commandDelegate.settings objectForKey:[@"FadeSplashScreen" lowercaseString]];
275 id fadeSplashScreenDuration = [self.commandDelegate.settings objectForKey:[@"FadeSplashScreenDuration" lowercaseString]];
276
277 float fadeDuration = fadeSplashScreenDuration == nil ? kSplashScreenDurationDefault : [fadeSplashScreenDuration floatValue];
278
279 if ((fadeSplashScreenValue == nil) || ![fadeSplashScreenValue boolValue]) {
280 fadeDuration = 0;
281 }
282
283 // Never animate the showing of the splash screen.
284 if (visible) {
285 if (_imageView == nil) {
286 [self createViews];
287 }
288 } else if (fadeDuration == 0) {
289 [self destroyViews];
290 } else {
291 [UIView transitionWithView:self.viewController.view
292 duration:fadeDuration
293 options:UIViewAnimationOptionTransitionNone
294 animations:^(void) {
295 [_imageView setAlpha:0];
296 [_activityView setAlpha:0];
297 }
298 completion:^(BOOL finished) {
299 if (finished) {
300 [self destroyViews];
301 }
302 }
303 ];
304 }
305 }
306
307 @end

mercurial