Skip to content

Commit 5e85d88

Browse files
authored
feat(ios): background-image support for action bar (NativeScript#10645)
1 parent 24ad6e4 commit 5e85d88

File tree

6 files changed

+202
-77
lines changed

6 files changed

+202
-77
lines changed

packages/core/ui/action-bar/index.ios.ts

Lines changed: 143 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@ import { IOSActionItemSettings, ActionItem as ActionItemDefinition } from '.';
22
import { ActionItemBase, ActionBarBase, isVisible, flatProperty, iosIconRenderingModeProperty, traceMissingIcon } from './action-bar-common';
33
import { View } from '../core/view';
44
import { Color } from '../../color';
5-
import { colorProperty, backgroundColorProperty, backgroundInternalProperty } from '../styling/style-properties';
5+
import { ios as iosBackground } from '../styling/background';
6+
import { LinearGradient } from '../styling/linear-gradient';
7+
import { colorProperty, backgroundInternalProperty, backgroundColorProperty, backgroundImageProperty } from '../styling/style-properties';
8+
import { ios as iosViewUtils } from '../utils';
69
import { ImageSource } from '../../image-source';
710
import { layout, iOSNativeHelper, isFontIconURI } from '../../utils';
811
import { accessibilityHintProperty, accessibilityLabelProperty, accessibilityLanguageProperty, accessibilityValueProperty } from '../../accessibility/accessibility-properties';
@@ -12,6 +15,10 @@ export * from './action-bar-common';
1215
const majorVersion = iOSNativeHelper.MajorVersion;
1316
const UNSPECIFIED = layout.makeMeasureSpec(0, layout.UNSPECIFIED);
1417

18+
interface NSUINavigationBar extends UINavigationBar {
19+
gradientLayer?: CAGradientLayer;
20+
}
21+
1522
function loadActionIcon(item: ActionItemDefinition): any /* UIImage */ {
1623
let is = null;
1724
let img = null;
@@ -140,6 +147,15 @@ export class ActionBar extends ActionBarBase {
140147
return this.ios;
141148
}
142149

150+
public disposeNativeView() {
151+
const navBar = this.navBar as NSUINavigationBar;
152+
if (navBar?.gradientLayer) {
153+
navBar.gradientLayer = null;
154+
}
155+
156+
super.disposeNativeView();
157+
}
158+
143159
public _addChildFromBuilder(name: string, value: any) {
144160
if (value instanceof NavigationButton) {
145161
this.navigationButton = value;
@@ -151,12 +167,12 @@ export class ActionBar extends ActionBarBase {
151167
}
152168

153169
public get _getActualSize(): { width: number; height: number } {
154-
const navBar = this.ios;
155-
if (!navBar) {
170+
const nativeView = this.ios;
171+
if (!nativeView) {
156172
return { width: 0, height: 0 };
157173
}
158174

159-
const frame = navBar.frame;
175+
const frame = nativeView.frame;
160176
const size = frame.size;
161177
const width = layout.toDevicePixels(size.width);
162178
const height = layout.toDevicePixels(size.height);
@@ -273,7 +289,7 @@ export class ActionBar extends ActionBarBase {
273289
this.populateMenuItems(navigationItem);
274290

275291
// update colors explicitly - they may have to be cleared form a previous page
276-
this.updateColors(navigationBar);
292+
this.updateFills(navigationBar);
277293

278294
// the 'flat' property may have changed in between pages
279295
this.updateFlatness(navigationBar);
@@ -345,12 +361,14 @@ export class ActionBar extends ActionBarBase {
345361
return barButtonItem;
346362
}
347363

348-
private updateColors(navBar: UINavigationBar) {
364+
private updateFills(navBar: UINavigationBar) {
349365
const color = this.color;
350366
this.setColor(navBar, color);
351367

352-
const bgColor = <Color>this.backgroundColor;
353-
this.setBackgroundColor(navBar, bgColor);
368+
this._setBackgroundColor(navBar, this.style.backgroundColor);
369+
this._createBackgroundUIImage(navBar, this.style.backgroundImage, (image: UIImage) => {
370+
this._setBackgroundImage(navBar, image);
371+
});
354372
}
355373

356374
private setColor(navBar: UINavigationBar, color?: Color) {
@@ -373,20 +391,112 @@ export class ActionBar extends ActionBarBase {
373391
}
374392
}
375393

376-
private setBackgroundColor(navBar: UINavigationBar, color?: UIColor | Color) {
394+
private _setBackgroundColor(navBar: UINavigationBar, color?: UIColor | Color) {
395+
if (!navBar) {
396+
return;
397+
}
398+
399+
const nativeColor = color instanceof Color ? color.ios : color;
400+
if (__VISIONOS__ || majorVersion >= 15) {
401+
const appearance = this._getAppearance(navBar);
402+
// appearance.configureWithOpaqueBackground();
403+
appearance.backgroundColor = nativeColor;
404+
this._updateAppearance(navBar, appearance);
405+
} else {
406+
// legacy styling
407+
navBar.barTintColor = nativeColor;
408+
}
409+
}
410+
411+
private _getBackgroundColor(navBar: UINavigationBar) {
412+
if (!navBar) {
413+
return null;
414+
}
415+
416+
let color: UIColor;
417+
418+
if (__VISIONOS__ || majorVersion >= 15) {
419+
const appearance = this._getAppearance(navBar);
420+
color = appearance.backgroundColor;
421+
} else {
422+
// legacy styling
423+
color = navBar.barTintColor;
424+
}
425+
426+
return color;
427+
}
428+
429+
private _setBackgroundImage(navBar: UINavigationBar, image: UIImage) {
377430
if (!navBar) {
378431
return;
379432
}
380433

381-
const color_ = color instanceof Color ? color.ios : color;
382434
if (__VISIONOS__ || majorVersion >= 15) {
383435
const appearance = this._getAppearance(navBar);
384436
// appearance.configureWithOpaqueBackground();
385-
appearance.backgroundColor = color_;
437+
appearance.backgroundImage = image;
386438
this._updateAppearance(navBar, appearance);
387439
} else {
388440
// legacy styling
389-
navBar.barTintColor = color_;
441+
442+
// Set a blank image in case image is null and flatness is enabled
443+
if (this.flat && !image) {
444+
image = UIImage.new();
445+
}
446+
447+
navBar.setBackgroundImageForBarMetrics(image, UIBarMetrics.Default);
448+
}
449+
}
450+
451+
private _getBackgroundImage(navBar: UINavigationBar) {
452+
if (!navBar) {
453+
return null;
454+
}
455+
456+
let image: UIImage;
457+
458+
if (__VISIONOS__ || majorVersion >= 15) {
459+
const appearance = this._getAppearance(navBar);
460+
image = appearance.backgroundImage;
461+
} else {
462+
// legacy styling
463+
image = navBar.backgroundImageForBarMetrics(UIBarMetrics.Default);
464+
}
465+
466+
return image;
467+
}
468+
469+
private _createBackgroundUIImage(navBar: NSUINavigationBar, value: string | LinearGradient, callback: (image: UIImage) => void): void {
470+
if (!navBar) {
471+
return;
472+
}
473+
474+
if (value) {
475+
if (value instanceof LinearGradient) {
476+
if (!navBar.gradientLayer) {
477+
navBar.gradientLayer = CAGradientLayer.new();
478+
}
479+
480+
iosViewUtils.drawGradient(navBar, navBar.gradientLayer, value);
481+
482+
const renderer = UIGraphicsImageRenderer.alloc().initWithSize(navBar.bounds.size);
483+
const img = renderer.imageWithActions((context: UIGraphicsRendererContext) => {
484+
navBar.gradientLayer.renderInContext(context.CGContext);
485+
});
486+
487+
callback(img);
488+
// Return here to avoid unnecessary cleanups
489+
return;
490+
}
491+
492+
// Background image
493+
iosBackground.createUIImageFromURI(this, value, false, callback);
494+
} else {
495+
callback(null);
496+
}
497+
498+
if (navBar.gradientLayer) {
499+
navBar.gradientLayer = null;
390500
}
391501
}
392502

@@ -411,7 +521,10 @@ export class ActionBar extends ActionBarBase {
411521
appearance.shadowColor = UIColor.clearColor;
412522
this._updateAppearance(navBar, appearance);
413523
} else {
414-
navBar.setBackgroundImageForBarMetrics(UIImage.new(), UIBarMetrics.Default);
524+
// Do not apply blank image if background image is already set
525+
if (!this.backgroundImage) {
526+
navBar.setBackgroundImageForBarMetrics(UIImage.new(), UIBarMetrics.Default);
527+
}
415528
navBar.shadowImage = UIImage.new();
416529
navBar.translucent = false;
417530
}
@@ -424,7 +537,11 @@ export class ActionBar extends ActionBarBase {
424537
this._updateAppearance(navBar, appearance);
425538
}
426539
} else {
427-
navBar.setBackgroundImageForBarMetrics(null, null);
540+
// Do not apply blank image if background image is already set
541+
if (!this.backgroundImage) {
542+
// Bar metrics is needed even when unsetting the image
543+
navBar.setBackgroundImageForBarMetrics(null, UIBarMetrics.Default);
544+
}
428545
navBar.shadowImage = null;
429546
navBar.translucent = true;
430547
}
@@ -507,13 +624,21 @@ export class ActionBar extends ActionBarBase {
507624
}
508625

509626
[backgroundColorProperty.getDefault](): UIColor {
510-
// This getter is never called.
511-
// CssAnimationProperty use default value form their constructor.
512-
return null;
627+
return this._getBackgroundColor(this.navBar);
513628
}
514629
[backgroundColorProperty.setNative](color: UIColor | Color) {
630+
this._setBackgroundColor(this.navBar, color);
631+
}
632+
633+
[backgroundImageProperty.getDefault](): UIImage {
634+
return this._getBackgroundImage(this.navBar);
635+
}
636+
[backgroundImageProperty.setNative](value: string | LinearGradient) {
515637
const navBar = this.navBar;
516-
this.setBackgroundColor(navBar, color);
638+
639+
this._createBackgroundUIImage(navBar, value, (image: UIImage) => {
640+
this._setBackgroundImage(navBar, image);
641+
});
517642
}
518643

519644
[backgroundInternalProperty.getDefault](): UIColor {
@@ -524,7 +649,6 @@ export class ActionBar extends ActionBarBase {
524649
}
525650

526651
[flatProperty.setNative](value: boolean) {
527-
// tslint:disable-line
528652
const navBar = this.navBar;
529653
if (navBar) {
530654
this.updateFlatness(navBar);

packages/core/ui/styling/background.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { View } from '../core/view';
33
import { BackgroundRepeat } from '../../css/parser';
44
import { LinearGradient } from '../styling/linear-gradient';
55
import { BoxShadow } from './box-shadow';
6+
import { Background as BackgroundDefinition } from './background-common';
67

78
export * from './background-common';
89

@@ -78,6 +79,7 @@ export namespace ios {
7879
export function createBackgroundUIColor(view: View, callback: (uiColor: any /* UIColor */) => void, flip?: boolean): void;
7980
export function drawBackgroundVisualEffects(view: View): void;
8081
export function clearBackgroundVisualEffects(view: View): void;
82+
export function createUIImageFromURI(view: View, imageURI: string, flip: boolean, callback: (image: any) => void): void;
8183
export function generateClipPath(view: View, bounds: CGRect): any;
8284
export function generateShadowLayerPaths(view: View, bounds: CGRect): { maskPath: any; shadowPath: any };
8385
export function getUniformBorderRadius(view: View, bounds: CGRect): number;

0 commit comments

Comments
 (0)