This is an automated email from the ASF dual-hosted git repository. GitToTheHub pushed a commit to branch pr-ios-switch-spinner-to-progress-bar in repository https://gitbox.apache.org/repos/asf/cordova-plugin-inappbrowser.git
commit b04c1a56b8a78b50315b6c86ca62c076737aec77 Author: Manuel Beck <[email protected]> AuthorDate: Thu Aug 13 20:13:29 2026 +0200 feat(ios): switch from spinner to progress bar - PR #1169 implemented a progress bar for Android. Today it's more common to use a progress bar instead of a spinner to indicate loading of web page as it is the default for the Safari app. This aligns it with Android and use the same option `hideloadingindicator` as for Android. The old option `hidespinner` is still supported and will be an alias for `hideloadingindicator`. --- README.md | 5 ++-- src/ios/CDVInAppBrowserOptions.h | 2 +- src/ios/CDVInAppBrowserOptions.m | 8 ++++- src/ios/CDVWKInAppBrowser.h | 2 +- src/ios/CDVWKInAppBrowser.m | 64 +++++++++++++++++++++++++++------------- 5 files changed, 54 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index 9f0bd3f5..f40671f9 100644 --- a/README.md +++ b/README.md @@ -158,9 +158,8 @@ instance, or the system browser. - __presentationstyle__: Set to `pagesheet`, `formsheet` or `fullscreen` to set the [presentation style](https://developer.apple.com/documentation/uikit/uimodalpresentationstyle) (defaults to `fullscreen`). - __transitionstyle__: Set to `fliphorizontal`, `crossdissolve` or `coververtical` to set the [transition style](https://developer.apple.com/documentation/uikit/uimodaltransitionstyle) (defaults to `coververtical`). - __toolbarposition__: Set to `top` or `bottom` (default is `bottom`). Causes the toolbar to be at the top or bottom of the window. - - __hidespinner__: Set to `yes` or `no` to change the visibility of the loading indicator (defaults to `no`). - - + - __hideloadingindicator__: Set to `yes` to hide the loading progress bar. Set to `no` or omit it to show the bar while pages load (default is `no`). + - __hidespinner__: Deprecated alias for `hideloadingindicator`. ### Supported Platforms - Android diff --git a/src/ios/CDVInAppBrowserOptions.h b/src/ios/CDVInAppBrowserOptions.h index 83df264c..743c4ffe 100644 --- a/src/ios/CDVInAppBrowserOptions.h +++ b/src/ios/CDVInAppBrowserOptions.h @@ -34,7 +34,7 @@ @property (nonatomic, assign) BOOL cleardata; @property (nonatomic, assign) BOOL clearcache; @property (nonatomic, assign) BOOL clearsessioncache; -@property (nonatomic, assign) BOOL hidespinner; +@property (nonatomic, assign) BOOL hideloadingindicator; @property (nonatomic, copy) NSString *presentationstyle; @property (nonatomic, copy) NSString *transitionstyle; diff --git a/src/ios/CDVInAppBrowserOptions.m b/src/ios/CDVInAppBrowserOptions.m index cc77788a..332f052d 100644 --- a/src/ios/CDVInAppBrowserOptions.m +++ b/src/ios/CDVInAppBrowserOptions.m @@ -32,7 +32,7 @@ self.cleardata = NO; self.clearcache = NO; self.clearsessioncache = NO; - self.hidespinner = NO; + self.hideloadingindicator = NO; self.enableviewportscale = NO; self.mediaplaybackrequiresuseraction = NO; @@ -66,6 +66,12 @@ NSString *value = [keyvalue objectAtIndex:1]; NSString *value_lc = [value lowercaseString]; + // In version 7.0.1 this was renamed to "hideloadingindicator" + // but we still support the old name for backwards compatibility. + if ([key isEqualToString:@"hidespinner"]) { + key = @"hideloadingindicator"; + } + BOOL isBoolean = [value_lc isEqualToString:@"yes"] || [value_lc isEqualToString:@"no"]; NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init]; [numberFormatter setAllowsFloats:YES]; diff --git a/src/ios/CDVWKInAppBrowser.h b/src/ios/CDVWKInAppBrowser.h index a5ba51b6..5b15e638 100644 --- a/src/ios/CDVWKInAppBrowser.h +++ b/src/ios/CDVWKInAppBrowser.h @@ -72,7 +72,7 @@ typedef NSDictionary CDVSettingsDictionary; @property (nonatomic, strong) IBOutlet UILabel *addressLabel; @property (nonatomic, strong) IBOutlet UIBarButtonItem *backButton; @property (nonatomic, strong) IBOutlet UIBarButtonItem *forwardButton; -@property (nonatomic, strong) IBOutlet UIActivityIndicatorView *spinner; +@property (nonatomic, strong) IBOutlet UIProgressView *progressView; @property (nonatomic, strong) IBOutlet UIView *toolbarBackground; @property (nonatomic, strong) IBOutlet UIToolbar *toolbar; @property (nonatomic, strong) IBOutlet CDVWKInAppBrowserUIDelegate *webViewUIDelegate; diff --git a/src/ios/CDVWKInAppBrowser.m b/src/ios/CDVWKInAppBrowser.m index 7638af60..01907266 100644 --- a/src/ios/CDVWKInAppBrowser.m +++ b/src/ios/CDVWKInAppBrowser.m @@ -736,6 +736,7 @@ static UIBarButtonSystemItem CDVWKInAppBrowserCloseButtonSystemItem(void) [self.inAppBrowserViewController.webView removeFromSuperview]; [self.inAppBrowserViewController.webView setUIDelegate:nil]; [self.inAppBrowserViewController.webView setNavigationDelegate:nil]; + [self.inAppBrowserViewController.webView removeObserver:self.inAppBrowserViewController forKeyPath:@"estimatedProgress"]; self.inAppBrowserViewController.webView = nil; // Set navigationDelegate to nil to ensure no callbacks are received from it. @@ -759,6 +760,11 @@ static UIBarButtonSystemItem CDVWKInAppBrowserCloseButtonSystemItem(void) CGFloat lastReducedStatusBarHeight = 0.0; BOOL isExiting = NO; +- (void)dealloc +{ + [self.webView removeObserver:self forKeyPath:@"estimatedProgress"]; +} + - (id)initWithBrowserOptions:(CDVInAppBrowserOptions *)browserOptions andSettings:(CDVSettingsDictionary *)settings { self = [super init]; @@ -838,7 +844,7 @@ BOOL isExiting = NO; #endif [self.view addSubview:self.webView]; - // The WebView should be behind the other elements like toolbar, address label, spinner + // The WebView should be behind the other elements like toolbar, address label, and progress bar // Since the WebView is added first, this is already the case. // sendSubviewToBack is normally not necessary. [self.view sendSubviewToBack:self.webView]; @@ -901,19 +907,14 @@ BOOL isExiting = NO; self.addressLabel.translatesAutoresizingMaskIntoConstraints = NO; [self.addressBackgroundView addSubview:self.addressLabel]; - self.spinner = [[UIActivityIndicatorView alloc] initWithFrame:CGRectZero]; - self.spinner.clearsContextBeforeDrawing = NO; - self.spinner.clipsToBounds = NO; - self.spinner.contentMode = UIViewContentModeScaleToFill; - self.spinner.hidden = NO; - self.spinner.hidesWhenStopped = YES; - self.spinner.multipleTouchEnabled = NO; - self.spinner.opaque = NO; - self.spinner.userInteractionEnabled = NO; - [self.spinner stopAnimating]; - [self.view addSubview:self.spinner]; + self.progressView = [[UIProgressView alloc] initWithProgressViewStyle:UIProgressViewStyleDefault]; + self.progressView.hidden = YES; + self.progressView.userInteractionEnabled = NO; + [self.view addSubview:self.progressView]; // We add our own constraints, they should not be determined from the frame. - self.spinner.translatesAutoresizingMaskIntoConstraints = NO; + self.progressView.translatesAutoresizingMaskIntoConstraints = NO; + // Observe WebKit's estimated load progress to update the native progress indicator. + [self.webView addObserver:self forKeyPath:@"estimatedProgress" options:NSKeyValueObservingOptionNew context:nil]; // NOTE: On iOS 26 using `UIBarButtonItem initWithBarButtonSystemItem:` gives constraint warnings, // which is a known UIKit bug. @@ -1027,9 +1028,12 @@ BOOL isExiting = NO; [self.addressLabel.trailingAnchor constraintEqualToAnchor:self.addressBackgroundView.layoutMarginsGuide.trailingAnchor] ]]; - // Center spinner in WebView - [self.spinner.centerXAnchor constraintEqualToAnchor:self.webView.centerXAnchor].active = YES; - [self.spinner.centerYAnchor constraintEqualToAnchor:self.webView.centerYAnchor].active = YES; + // Position the progress bar at the top edge of the WebView. + [NSLayoutConstraint activateConstraints:@[ + [self.progressView.topAnchor constraintEqualToAnchor:self.webView.topAnchor], + [self.progressView.leadingAnchor constraintEqualToAnchor:self.webView.leadingAnchor], + [self.progressView.trailingAnchor constraintEqualToAnchor:self.webView.trailingAnchor] + ]]; // Define vertical constraints, in order from top to bottom // Don't extend behind the status bar like also the Safari app does and restraint @@ -1219,6 +1223,21 @@ BOOL isExiting = NO; } } +/** + * Mirrors WKWebView's estimated loading progress in the native progress indicator. + */ +- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey, id> *)change context:(void *)context +{ + if (object == self.webView && [keyPath isEqualToString:@"estimatedProgress"]) { + if (!_browserOptions.hideloadingindicator) { + self.progressView.progress = self.webView.estimatedProgress; + } + return; + } + + [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; +} + - (void)goBack:(id)sender { [self.webView goBack]; @@ -1246,12 +1265,15 @@ BOOL isExiting = NO; - (void)webView:(WKWebView *)theWebView didStartProvisionalNavigation:(WKNavigation *)navigation { NSLog(@"didStartProvisionalNavigation"); - // Loading URL, start spinner, update back/forward + // Loading URL, show progress bar, update back/forward self.addressLabel.text = NSLocalizedString(@"Loading...", nil); self.backButton.enabled = theWebView.canGoBack; self.forwardButton.enabled = theWebView.canGoForward; - if (!_browserOptions.hidespinner) [self.spinner startAnimating]; + if (!_browserOptions.hideloadingindicator) { + self.progressView.progress = 0.0; + self.progressView.hidden = NO; + } return [self.navigationDelegate didStartProvisionalNavigation:theWebView]; } @@ -1274,12 +1296,12 @@ decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler - (void)webView:(WKWebView *)theWebView didFinishNavigation:(WKNavigation *)navigation { NSLog(@"didFinishNavigation"); - // Update URL, stop spinner, update back/forward + // Update URL, hide progress bar, update back/forward self.addressLabel.text = self.currentURL.absoluteString; self.backButton.enabled = theWebView.canGoBack; self.forwardButton.enabled = theWebView.canGoForward; theWebView.scrollView.contentInset = UIEdgeInsetsZero; - [self.spinner stopAnimating]; + self.progressView.hidden = YES; [self.navigationDelegate didFinishNavigation:theWebView]; } @@ -1287,7 +1309,7 @@ decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler { self.backButton.enabled = theWebView.canGoBack; self.forwardButton.enabled = theWebView.canGoForward; - [self.spinner stopAnimating]; + self.progressView.hidden = YES; BOOL isBeforeloadEnabled = self.navigationDelegate != nil && [self.navigationDelegate isBeforeloadEnabled]; --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
