rn-liquid-glass-bottom-tabs 0.1.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.
Files changed (72) hide show
  1. package/LICENSE +20 -0
  2. package/LiquidGlass.podspec +21 -0
  3. package/README.md +144 -0
  4. package/ios/LiquidGlassContainerView.h +15 -0
  5. package/ios/LiquidGlassContainerView.mm +70 -0
  6. package/ios/LiquidGlassContainerView.swift +28 -0
  7. package/ios/LiquidGlassModule.h +9 -0
  8. package/ios/LiquidGlassModule.mm +47 -0
  9. package/ios/LiquidGlassView.h +14 -0
  10. package/ios/LiquidGlassView.mm +170 -0
  11. package/ios/LiquidGlassView.swift +93 -0
  12. package/lib/module/LiquidGlassContainerView.ios.js +4 -0
  13. package/lib/module/LiquidGlassContainerView.ios.js.map +1 -0
  14. package/lib/module/LiquidGlassContainerView.js +9 -0
  15. package/lib/module/LiquidGlassContainerView.js.map +1 -0
  16. package/lib/module/LiquidGlassTabBar.ios.js +254 -0
  17. package/lib/module/LiquidGlassTabBar.ios.js.map +1 -0
  18. package/lib/module/LiquidGlassTabBar.js +70 -0
  19. package/lib/module/LiquidGlassTabBar.js.map +1 -0
  20. package/lib/module/LiquidGlassView.ios.js +4 -0
  21. package/lib/module/LiquidGlassView.ios.js.map +1 -0
  22. package/lib/module/LiquidGlassView.js +9 -0
  23. package/lib/module/LiquidGlassView.js.map +1 -0
  24. package/lib/module/LiquidGlassViewContainerNativeComponent.ts +15 -0
  25. package/lib/module/LiquidGlassViewNativeComponent.ts +38 -0
  26. package/lib/module/NativeLiquidGlassModule.js +5 -0
  27. package/lib/module/NativeLiquidGlassModule.js.map +1 -0
  28. package/lib/module/index.js +19 -0
  29. package/lib/module/index.js.map +1 -0
  30. package/lib/module/isLiquidGlassSupported.ios.js +5 -0
  31. package/lib/module/isLiquidGlassSupported.ios.js.map +1 -0
  32. package/lib/module/isLiquidGlassSupported.js +4 -0
  33. package/lib/module/isLiquidGlassSupported.js.map +1 -0
  34. package/lib/module/package.json +1 -0
  35. package/lib/typescript/package.json +1 -0
  36. package/lib/typescript/src/LiquidGlassContainerView.d.ts +6 -0
  37. package/lib/typescript/src/LiquidGlassContainerView.d.ts.map +1 -0
  38. package/lib/typescript/src/LiquidGlassContainerView.ios.d.ts +2 -0
  39. package/lib/typescript/src/LiquidGlassContainerView.ios.d.ts.map +1 -0
  40. package/lib/typescript/src/LiquidGlassTabBar.d.ts +47 -0
  41. package/lib/typescript/src/LiquidGlassTabBar.d.ts.map +1 -0
  42. package/lib/typescript/src/LiquidGlassTabBar.ios.d.ts +59 -0
  43. package/lib/typescript/src/LiquidGlassTabBar.ios.d.ts.map +1 -0
  44. package/lib/typescript/src/LiquidGlassView.d.ts +6 -0
  45. package/lib/typescript/src/LiquidGlassView.d.ts.map +1 -0
  46. package/lib/typescript/src/LiquidGlassView.ios.d.ts +2 -0
  47. package/lib/typescript/src/LiquidGlassView.ios.d.ts.map +1 -0
  48. package/lib/typescript/src/LiquidGlassViewContainerNativeComponent.d.ts +11 -0
  49. package/lib/typescript/src/LiquidGlassViewContainerNativeComponent.d.ts.map +1 -0
  50. package/lib/typescript/src/LiquidGlassViewNativeComponent.d.ts +33 -0
  51. package/lib/typescript/src/LiquidGlassViewNativeComponent.d.ts.map +1 -0
  52. package/lib/typescript/src/NativeLiquidGlassModule.d.ts +10 -0
  53. package/lib/typescript/src/NativeLiquidGlassModule.d.ts.map +1 -0
  54. package/lib/typescript/src/index.d.ts +20 -0
  55. package/lib/typescript/src/index.d.ts.map +1 -0
  56. package/lib/typescript/src/isLiquidGlassSupported.d.ts +2 -0
  57. package/lib/typescript/src/isLiquidGlassSupported.d.ts.map +1 -0
  58. package/lib/typescript/src/isLiquidGlassSupported.ios.d.ts +2 -0
  59. package/lib/typescript/src/isLiquidGlassSupported.ios.d.ts.map +1 -0
  60. package/package.json +169 -0
  61. package/src/LiquidGlassContainerView.ios.tsx +1 -0
  62. package/src/LiquidGlassContainerView.tsx +7 -0
  63. package/src/LiquidGlassTabBar.ios.tsx +369 -0
  64. package/src/LiquidGlassTabBar.tsx +119 -0
  65. package/src/LiquidGlassView.ios.tsx +1 -0
  66. package/src/LiquidGlassView.tsx +7 -0
  67. package/src/LiquidGlassViewContainerNativeComponent.ts +15 -0
  68. package/src/LiquidGlassViewNativeComponent.ts +38 -0
  69. package/src/NativeLiquidGlassModule.ts +13 -0
  70. package/src/index.tsx +26 -0
  71. package/src/isLiquidGlassSupported.ios.ts +4 -0
  72. package/src/isLiquidGlassSupported.ts +1 -0
package/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Oskar Kwaśniewski
4
+ Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ of this software and associated documentation files (the "Software"), to deal
6
+ in the Software without restriction, including without limitation the rights
7
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the Software is
9
+ furnished to do so, subject to the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be included in all
12
+ copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ SOFTWARE.
@@ -0,0 +1,21 @@
1
+ require "json"
2
+
3
+ package = JSON.parse(File.read(File.join(__dir__, "package.json")))
4
+
5
+ Pod::Spec.new do |s|
6
+ s.name = "LiquidGlass"
7
+ s.version = package["version"]
8
+ s.summary = package["description"]
9
+ s.homepage = package["homepage"]
10
+ s.license = package["license"]
11
+ s.authors = package["author"]
12
+
13
+ s.platforms = { :ios => min_ios_version_supported, :tvos => min_ios_version_supported }
14
+ s.source = { :git => "https://github.com/callstack/liquid-glass.git", :tag => "#{s.version}" }
15
+
16
+ s.source_files = "ios/**/*.{h,m,mm,cpp,swift}"
17
+ s.private_header_files = "ios/**/*.h"
18
+
19
+
20
+ install_modules_dependencies(s)
21
+ end
package/README.md ADDED
@@ -0,0 +1,144 @@
1
+ <div align="center">
2
+ <h1>React Native Liquid Glass 🔍</h1>
3
+
4
+ [![mit licence](https://img.shields.io/dub/l/vibe-d.svg?style=for-the-badge)](https://github.com/kashif8299/rn-liquid-glass-bottom-tabs/blob/main/LICENSE)
5
+ [![npm version](https://img.shields.io/npm/v/rn-liquid-glass-bottom-tabs?style=for-the-badge)](https://www.npmjs.org/package/rn-liquid-glass-bottom-tabs)
6
+ [![npm downloads](https://img.shields.io/npm/dt/rn-liquid-glass-bottom-tabs.svg?style=for-the-badge)](https://www.npmjs.org/package/rn-liquid-glass-bottom-tabs)
7
+ [![npm downloads](https://img.shields.io/npm/dm/rn-liquid-glass-bottom-tabs.svg?style=for-the-badge)](https://www.npmjs.org/package/rn-liquid-glass-bottom-tabs)
8
+
9
+ `rn-liquid-glass-bottom-tabs` brings iOS 26 liquid glass effect to React Native apps on iOS.
10
+
11
+ https://github.com/user-attachments/assets/44c18136-8760-49f2-ae16-62557c3ae2e1
12
+
13
+ </div>
14
+
15
+ ## Features
16
+
17
+ - ✨ iOS 26 liquid glass visual effect
18
+ - 🎨 Customizable tint colors
19
+ - 🔧 Two effect modes: `clear` and `regular`
20
+
21
+ ## Documentation
22
+
23
+ ### Installation
24
+
25
+ ```bash
26
+ npm install rn-liquid-glass-bottom-tabs
27
+ # or
28
+ yarn add rn-liquid-glass-bottom-tabs
29
+ ```
30
+
31
+ > [!WARNING]
32
+ > Make sure to compile your app with Xcode >= 26. React Native 0.80+ is required.
33
+
34
+ > [!WARNING]
35
+ > This library is not supported in [Expo Go](https://expo.dev/go).
36
+
37
+
38
+
39
+ ### Usage
40
+
41
+ ```tsx
42
+ import {
43
+ LiquidGlassView,
44
+ LiquidGlassContainerView,
45
+ isLiquidGlassSupported,
46
+ } from 'rn-liquid-glass-bottom-tabs';
47
+
48
+ function MyComponent() {
49
+ return (
50
+ <LiquidGlassView
51
+ style={[
52
+ { width: 200, height: 100, borderRadius: 20 },
53
+ !isLiquidGlassSupported && { backgroundColor: 'rgba(255,255,255,0.5)' },
54
+ ]}
55
+ interactive
56
+ effect="clear"
57
+ >
58
+ <Text>Hello World</Text>
59
+ </LiquidGlassView>
60
+ );
61
+ }
62
+
63
+ // For combining multiple glass elements
64
+ function MergingGlassElements() {
65
+ return (
66
+ <LiquidGlassContainerView spacing={20}>
67
+ <LiquidGlassView style={{ width: 100, height: 100, borderRadius: 50 }} />
68
+ <LiquidGlassView style={{ width: 100, height: 100, borderRadius: 50 }} />
69
+ </LiquidGlassContainerView>
70
+ );
71
+ }
72
+ ```
73
+
74
+ To achieve automatic text color adaptation based on the background behind the glass view, use `PlatformColor` from `react-native`:
75
+
76
+ > [!NOTE]
77
+ > There appears to be a size limit for the glass to automatically adapt the text color. If the glass view height is >= 65 it won't automatically adapt to the material behind it.
78
+
79
+ https://github.com/user-attachments/assets/199bce70-dab4-43bc-9de1-605f561760e5
80
+
81
+ ```tsx
82
+ import { PlatformColor } from 'react-native';
83
+ import { LiquidGlassView } from 'rn-liquid-glass-bottom-tabs';
84
+
85
+ function MyComponent() {
86
+ return (
87
+ <LiquidGlassView style={{ padding: 20, borderRadius: 20 }}>
88
+ <Text style={{ color: PlatformColor('labelColor') }}>Hello World</Text>
89
+ </LiquidGlassView>
90
+ );
91
+ }
92
+ ```
93
+
94
+ > [!NOTE]
95
+ > On unsupported iOS version (below iOS 26), it will render a normal `View` without any effects.
96
+
97
+ ### API
98
+
99
+ #### `isLiquidGlassSupported`
100
+
101
+ A boolean constant that indicates whether the current device supports the liquid glass effect.
102
+
103
+ ```tsx
104
+ import { isLiquidGlassSupported } from 'rn-liquid-glass-bottom-tabs';
105
+
106
+ if (isLiquidGlassSupported) {
107
+ // Device supports liquid glass effect
108
+ } else {
109
+ // Provide fallback UI
110
+ }
111
+ ```
112
+
113
+ ### LiquidGlassView - Props
114
+
115
+ | Prop | Type | Default | Description |
116
+ | ------------- | ------------------------------- | ----------- | ----------------------------------------------------------------------------------------------------------------------------------- |
117
+ | `interactive` | `boolean` | `false` | Enables touch interaction effects when pressing the view |
118
+ | `effect` | `'clear' \| 'regular' \| 'none'` | `'regular'` | Visual effect mode:<br/>• `clear` - More transparent glass effect<br/>• `regular` - Standard glass blur effect<br/>• `none` - No glass effect (transparent view)<br/>**Note:** Changing this property animates the materialization/dematerialization of the glass effect |
119
+ | `tintColor` | `ColorValue` | `undefined` | Overlay color tint applied to the glass effect. Accepts any React Native color format (hex, rgba, named colors) |
120
+ | `colorScheme` | `'light' \| 'dark' \| 'system'` | `'system'` | Color scheme adaptation:<br/>• `light` - Light appearance<br/>• `dark` - Dark appearance<br/>• `system` - Follows system appearance |
121
+
122
+ ### LiquidGlassContainerView - Props
123
+
124
+ | Prop | Type | Default | Description |
125
+ | --------- | -------- | ------- | ----------------------------------------------------------------------------------------------------------- |
126
+ | `spacing` | `number` | `0` | The distance between child elements at which they begin to merge their glass effects into a combined effect |
127
+
128
+ ## Known issues
129
+
130
+ - `interactive` prop is not changed dynamically, it is only set on mount.
131
+
132
+ ## Made with ❤️ at Callstack
133
+
134
+ `liquid-glass` is an open source project and will always remain free to use. If you think it's cool, please star it 🌟.
135
+
136
+ [Callstack][callstack-readme-with-love] is a group of React and React Native geeks, contact us at [hello@callstack.com](mailto:hello@callstack.com) if you need any help with these or just want to say hi!
137
+
138
+ Like the project? ⚛️ [Join the team](https://callstack.com/careers/?utm_campaign=Senior_RN&utm_source=github&utm_medium=readme) who does amazing stuff for clients and drives React Native Open Source! 🔥
139
+
140
+ [callstack-readme-with-love]: https://callstack.com/?utm_source=github.com&utm_medium=referral&utm_campaign=liquid-glass&utm_term=readme-with-love
141
+ [version-badge]: https://img.shields.io/npm/v/rn-liquid-glass-bottom-tabs?style=for-the-badge
142
+ [version]: https://github.com/kashif8299/rn-liquid-glass-bottom-tabs/blob/main/LICENSE
143
+ [prs-welcome-badge]: https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=for-the-badge
144
+ [prs-welcome]: ./CONTRIBUTING.md
@@ -0,0 +1,15 @@
1
+ #import <React/RCTViewComponentView.h>
2
+ #import <UIKit/UIKit.h>
3
+
4
+ #ifndef LiquidGlassContainerViewNativeComponent_h
5
+ #define LiquidGlassContainerViewNativeComponent_h
6
+
7
+ NS_ASSUME_NONNULL_BEGIN
8
+
9
+ @interface LiquidGlassContainerView : RCTViewComponentView
10
+ @end
11
+
12
+ NS_ASSUME_NONNULL_END
13
+
14
+ #endif /* LiquidGlassContainerViewNativeComponent_h */
15
+
@@ -0,0 +1,70 @@
1
+ #import "LiquidGlassContainerView.h"
2
+
3
+ #import <react/renderer/components/LiquidGlassViewSpec/ComponentDescriptors.h>
4
+ #import <react/renderer/components/LiquidGlassViewSpec/EventEmitters.h>
5
+ #import <react/renderer/components/LiquidGlassViewSpec/Props.h>
6
+ #import <react/renderer/components/LiquidGlassViewSpec/RCTComponentViewHelpers.h>
7
+
8
+ #import "RCTFabricComponentsPlugins.h"
9
+
10
+ #if __has_include("LiquidGlass/LiquidGlass-Swift.h")
11
+ #import "LiquidGlass/LiquidGlass-Swift.h"
12
+ #else
13
+ #import "LiquidGlass-Swift.h"
14
+ #endif
15
+
16
+ using namespace facebook::react;
17
+
18
+ @interface LiquidGlassContainerView () <RCTLiquidGlassContainerViewViewProtocol>
19
+
20
+ @end
21
+
22
+ @implementation LiquidGlassContainerView {
23
+ LiquidGlassConatinerViewImpl * _view;
24
+ }
25
+
26
+ + (ComponentDescriptorProvider)componentDescriptorProvider
27
+ {
28
+ return concreteComponentDescriptorProvider<LiquidGlassContainerViewComponentDescriptor>();
29
+ }
30
+
31
+ - (instancetype)initWithFrame:(CGRect)frame
32
+ {
33
+ if (self = [super initWithFrame:frame]) {
34
+ static const auto defaultProps = std::make_shared<const LiquidGlassContainerViewProps>();
35
+ _props = defaultProps;
36
+
37
+ _view = [[LiquidGlassConatinerViewImpl alloc] init];
38
+
39
+ self.contentView = _view;
40
+ }
41
+
42
+ return self;
43
+ }
44
+
45
+ #if (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 260000) || \
46
+ (defined(__TV_OS_VERSION_MAX_ALLOWED) && __TV_OS_VERSION_MAX_ALLOWED >= 260000)
47
+
48
+ - (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps
49
+ {
50
+ const auto &oldViewProps = *std::static_pointer_cast<LiquidGlassContainerViewProps const>(_props);
51
+ const auto &newViewProps = *std::static_pointer_cast<LiquidGlassContainerViewProps const>(props);
52
+
53
+ if (oldViewProps.spacing != newViewProps.spacing) {
54
+ [_view setSpacing:newViewProps.spacing];
55
+ }
56
+
57
+ [super updateProps:props oldProps:oldProps];
58
+ }
59
+
60
+ - (void)mountChildComponentView:(UIView<RCTComponentViewProtocol> *)childComponentView index:(NSInteger)index {
61
+ [_view.contentView insertSubview:childComponentView atIndex:index];
62
+ }
63
+
64
+ - (void)unmountChildComponentView:(UIView<RCTComponentViewProtocol> *)childComponentView index:(NSInteger)index {
65
+ [childComponentView removeFromSuperview];
66
+ }
67
+ #endif
68
+
69
+ @end
70
+
@@ -0,0 +1,28 @@
1
+ import UIKit
2
+
3
+ #if compiler(>=6.2)
4
+
5
+ @available(iOS 26.0, tvOS 26.0, *)
6
+ @objc public class LiquidGlassConatinerViewImpl: UIVisualEffectView {
7
+ @objc public var spacing: CGFloat = 0 {
8
+ didSet {
9
+ setupView()
10
+ }
11
+ }
12
+
13
+ public override func layoutSubviews() {
14
+ setupView()
15
+ }
16
+
17
+ private func setupView() {
18
+ let effect = UIGlassContainerEffect()
19
+ effect.spacing = spacing
20
+ self.effect = effect
21
+ }
22
+ }
23
+
24
+ #else
25
+
26
+ @objc public class LiquidGlassConatinerViewImpl: UIView {}
27
+
28
+ #endif
@@ -0,0 +1,9 @@
1
+ #import <UIKit/UIKit.h>
2
+
3
+ #import <React/RCTBridge.h>
4
+ #import <LiquidGlassViewSpec/LiquidGlassViewSpec.h>
5
+
6
+ @interface LiquidGlassModule : NSObject <NativeLiquidGlassModuleSpec>
7
+
8
+ @end
9
+
@@ -0,0 +1,47 @@
1
+ #import "LiquidGlassModule.h"
2
+
3
+ @implementation LiquidGlassModule {
4
+ facebook::react::ModuleConstants<JS::NativeLiquidGlassModule::Constants> _constants;
5
+ }
6
+
7
+ - (void)initialize
8
+ {
9
+ #if (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 260000) || \
10
+ (defined(__TV_OS_VERSION_MAX_ALLOWED) && __TV_OS_VERSION_MAX_ALLOWED >= 260000)
11
+ if (@available(iOS 26.0, tvOS 26.0, *)) {
12
+ NSDictionary *infoPlist = [[NSBundle mainBundle] infoDictionary];
13
+ BOOL requiresDesignCompatibility = [infoPlist[@"UIDesignRequiresCompatibility"] boolValue];
14
+
15
+ _constants = facebook::react::typedConstants<JS::NativeLiquidGlassModule::Constants>({
16
+ .isLiquidGlassSupported = !requiresDesignCompatibility
17
+ });
18
+
19
+ return;
20
+ }
21
+ #endif
22
+
23
+ _constants = facebook::react::typedConstants<JS::NativeLiquidGlassModule::Constants>({
24
+ .isLiquidGlassSupported = NO
25
+ });
26
+ }
27
+
28
+ - (facebook::react::ModuleConstants<JS::NativeLiquidGlassModule::Constants>)constantsToExport
29
+ {
30
+ return (facebook::react::ModuleConstants<JS::NativeLiquidGlassModule::Constants>)[self getConstants];
31
+ }
32
+
33
+ + (NSString *)moduleName {
34
+ return @"NativeLiquidGlassModule";
35
+ }
36
+
37
+
38
+ - (facebook::react::ModuleConstants<JS::NativeLiquidGlassModule::Constants>)getConstants
39
+ {
40
+ return _constants;
41
+ }
42
+
43
+ - (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:(const facebook::react::ObjCTurboModule::InitParams &)params {
44
+ return std::make_shared<facebook::react::NativeLiquidGlassModuleSpecJSI>(params);
45
+ }
46
+
47
+ @end
@@ -0,0 +1,14 @@
1
+ #import <React/RCTViewComponentView.h>
2
+ #import <UIKit/UIKit.h>
3
+
4
+ #ifndef LiquidGlassViewNativeComponent_h
5
+ #define LiquidGlassViewNativeComponent_h
6
+
7
+ NS_ASSUME_NONNULL_BEGIN
8
+
9
+ @interface LiquidGlassView : RCTViewComponentView
10
+ @end
11
+
12
+ NS_ASSUME_NONNULL_END
13
+
14
+ #endif /* LiquidGlassViewNativeComponent_h */
@@ -0,0 +1,170 @@
1
+ #import "LiquidGlassView.h"
2
+
3
+ #import <react/renderer/components/LiquidGlassViewSpec/ComponentDescriptors.h>
4
+ #import <react/renderer/components/LiquidGlassViewSpec/EventEmitters.h>
5
+ #import <react/renderer/components/LiquidGlassViewSpec/Props.h>
6
+ #import <react/renderer/components/LiquidGlassViewSpec/RCTComponentViewHelpers.h>
7
+ #import "RCTImagePrimitivesConversions.h"
8
+
9
+ #import "RCTFabricComponentsPlugins.h"
10
+ #import "RCTConversions.h"
11
+
12
+ #if __has_include("LiquidGlass/LiquidGlass-Swift.h")
13
+ #import "LiquidGlass/LiquidGlass-Swift.h"
14
+ #else
15
+ #import "LiquidGlass-Swift.h"
16
+ #endif
17
+
18
+ using namespace facebook::react;
19
+
20
+ @interface LiquidGlassView () <RCTLiquidGlassViewViewProtocol>
21
+
22
+ @end
23
+
24
+ @implementation LiquidGlassView {
25
+ LiquidGlassViewImpl * _view;
26
+ BOOL _needsInvalidateLayer;
27
+ }
28
+
29
+ + (ComponentDescriptorProvider)componentDescriptorProvider
30
+ {
31
+ return concreteComponentDescriptorProvider<LiquidGlassViewComponentDescriptor>();
32
+ }
33
+
34
+ - (instancetype)initWithFrame:(CGRect)frame
35
+ {
36
+ if (self = [super initWithFrame:frame]) {
37
+ static const auto defaultProps = std::make_shared<const LiquidGlassViewProps>();
38
+ _props = defaultProps;
39
+
40
+ _view = [[LiquidGlassViewImpl alloc] init];
41
+
42
+ self.contentView = _view;
43
+ }
44
+
45
+ return self;
46
+ }
47
+
48
+ #if (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 260000) || \
49
+ (defined(__TV_OS_VERSION_MAX_ALLOWED) && __TV_OS_VERSION_MAX_ALLOWED >= 260000)
50
+ - (void)layoutSubviews {
51
+ [super layoutSubviews];
52
+ _view.layer.cornerRadius = self.layer.cornerRadius;
53
+ _view.layer.cornerCurve = self.layer.cornerCurve;
54
+ }
55
+
56
+ - (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps
57
+ {
58
+ const auto &oldViewProps = *std::static_pointer_cast<LiquidGlassViewProps const>(_props);
59
+ const auto &newViewProps = *std::static_pointer_cast<LiquidGlassViewProps const>(props);
60
+ BOOL needsSetup = NO;
61
+
62
+ if (oldViewProps.tintColor != newViewProps.tintColor) {
63
+ _view.effectTintColor = RCTUIColorFromSharedColor(newViewProps.tintColor);
64
+ needsSetup = YES;
65
+ }
66
+
67
+ if (oldViewProps.effect != newViewProps.effect) {
68
+ switch (newViewProps.effect) {
69
+ case LiquidGlassViewEffect::Regular:
70
+ [_view setStyle:LiquidGlassEffectRegular];
71
+ break;
72
+
73
+ case LiquidGlassViewEffect::Clear:
74
+ [_view setStyle:LiquidGlassEffectClear];
75
+ break;
76
+
77
+ case LiquidGlassViewEffect::None:
78
+ [_view setStyle:LiquidGlassEffectNone];
79
+ break;
80
+ }
81
+
82
+ needsSetup = YES;
83
+ }
84
+
85
+ if (oldViewProps.interactive != newViewProps.interactive) {
86
+ _view.interactive = newViewProps.interactive;
87
+ needsSetup = YES;
88
+ }
89
+
90
+ if (oldViewProps.colorScheme != newViewProps.colorScheme) {
91
+ switch (newViewProps.colorScheme) {
92
+ case LiquidGlassViewColorScheme::System:
93
+ _view.overrideUserInterfaceStyle = UIUserInterfaceStyleUnspecified;
94
+ break;
95
+
96
+ case LiquidGlassViewColorScheme::Dark:
97
+ _view.overrideUserInterfaceStyle = UIUserInterfaceStyleDark;
98
+ break;
99
+
100
+ case LiquidGlassViewColorScheme::Light:
101
+ _view.overrideUserInterfaceStyle = UIUserInterfaceStyleLight;
102
+ break;
103
+ }
104
+ needsSetup = YES;
105
+ }
106
+
107
+ // `border`
108
+ if (oldViewProps.borderStyles != newViewProps.borderStyles || oldViewProps.borderRadii != newViewProps.borderRadii ||
109
+ oldViewProps.borderColors != newViewProps.borderColors) {
110
+ _needsInvalidateLayer = YES;
111
+ }
112
+
113
+ if (needsSetup) {
114
+ [_view setupView];
115
+ }
116
+
117
+ [super updateProps:props oldProps:oldProps];
118
+ }
119
+
120
+ - (void)finalizeUpdates:(RNComponentViewUpdateMask)updateMask {
121
+ [super finalizeUpdates:updateMask];
122
+
123
+ if (!_needsInvalidateLayer) {
124
+ return;
125
+ }
126
+
127
+ _needsInvalidateLayer = NO;
128
+
129
+ if (@available(iOS 26.0, tvOS 26.0, *)) {
130
+ const auto borderMetrics = _props->resolveBorderMetrics(_layoutMetrics);
131
+
132
+
133
+ auto borderRadii = borderMetrics.borderRadii;
134
+
135
+ CGFloat topLeftRadius = MAX(borderRadii.topLeft.horizontal, borderRadii.topLeft.vertical);
136
+ CGFloat topRightRadius = MAX(borderRadii.topRight.horizontal, borderRadii.topRight.vertical);
137
+ CGFloat bottomLeftRadius = MAX(borderRadii.bottomLeft.horizontal, borderRadii.bottomLeft.vertical);
138
+ CGFloat bottomRightRadius = MAX(borderRadii.bottomRight.horizontal, borderRadii.bottomRight.vertical);
139
+
140
+ UICornerRadius *topLeft = [UICornerRadius fixedRadius:topLeftRadius];
141
+ UICornerRadius *topRight = [UICornerRadius fixedRadius:topRightRadius];
142
+ UICornerRadius *bottomLeft = [UICornerRadius fixedRadius:bottomLeftRadius];
143
+ UICornerRadius *bottomRight = [UICornerRadius fixedRadius:bottomRightRadius];
144
+
145
+ _view.cornerConfiguration = [UICornerConfiguration configurationWithTopLeftRadius:topLeft topRightRadius:topRight bottomLeftRadius:bottomLeft bottomRightRadius:bottomRight];
146
+ }
147
+ }
148
+
149
+ - (void)updateLayoutMetrics:(const LayoutMetrics &)layoutMetrics
150
+ oldLayoutMetrics:(const LayoutMetrics &)oldLayoutMetrics
151
+ {
152
+ [super updateLayoutMetrics:layoutMetrics oldLayoutMetrics:oldLayoutMetrics];
153
+
154
+ _needsInvalidateLayer = YES;
155
+
156
+ // Fixes an issue with padding set only on the external view (the container holding content view).
157
+ [_view setFrame:RCTCGRectFromRect(layoutMetrics.getPaddingFrame())];
158
+ }
159
+
160
+ - (void)mountChildComponentView:(UIView<RCTComponentViewProtocol> *)childComponentView index:(NSInteger)index {
161
+ [_view.contentView insertSubview:childComponentView atIndex:index];
162
+ }
163
+
164
+ - (void)unmountChildComponentView:(UIView<RCTComponentViewProtocol> *)childComponentView index:(NSInteger)index {
165
+ [childComponentView removeFromSuperview];
166
+ }
167
+
168
+ #endif
169
+
170
+ @end
@@ -0,0 +1,93 @@
1
+ import UIKit
2
+
3
+ @objc public enum LiquidGlassEffect: Int {
4
+ case regular
5
+ case clear
6
+ case none
7
+
8
+ #if compiler(>=6.2)
9
+ @available(iOS 26.0, tvOS 26.0, *)
10
+ var converted: UIGlassEffect.Style? {
11
+ switch self {
12
+ case .regular:
13
+ return .regular
14
+ case .clear:
15
+ return .clear
16
+ case .none:
17
+ return nil
18
+ }
19
+ }
20
+ #endif
21
+
22
+ }
23
+
24
+ #if compiler(>=6.2)
25
+
26
+ @available(iOS 26.0, tvOS 26.0, *)
27
+ @objc public class LiquidGlassViewImpl: UIVisualEffectView {
28
+ private var isFirstMount: Bool = true
29
+ @objc public var effectTintColor: UIColor?
30
+ @objc public var interactive: Bool = false
31
+ @objc public var style: LiquidGlassEffect = .regular
32
+
33
+ public override func layoutSubviews() {
34
+ if (self.effect != nil) { return }
35
+ setupView()
36
+
37
+ if isFirstMount {
38
+ isFirstMount = false
39
+ }
40
+ }
41
+
42
+
43
+ @objc public func setupView() {
44
+ guard #available(iOS 26.0, tvOS 26.0, *) else {
45
+ return
46
+ }
47
+
48
+ // Runtime check to ensure UIGlassEffect is available
49
+ // This handles cases where early iOS 26 beta releases may not have this API
50
+ guard let glassEffectClass = NSClassFromString("UIGlassEffect") as? NSObject.Type else {
51
+ return
52
+ }
53
+
54
+ // Verify that the effectWithStyle: selector is available
55
+ // This provides an additional safety check for early beta versions
56
+ guard glassEffectClass.responds(to: Selector(("effectWithStyle:"))) else {
57
+ return
58
+ }
59
+
60
+ guard let preferredStyle = style.converted else {
61
+ UIView.animate {
62
+ // TODO: Looks like only assigning nil is not working, check this after stable iOS 26 is rolled out.
63
+ self.effect = UIVisualEffect()
64
+ }
65
+ return
66
+ }
67
+
68
+ let glassEffect = UIGlassEffect(style: preferredStyle)
69
+ glassEffect.isInteractive = interactive
70
+ glassEffect.tintColor = effectTintColor
71
+
72
+ if isFirstMount {
73
+ self.effect = glassEffect
74
+ } else {
75
+ // Animate only the effect is changed after first mount.
76
+ UIView.animate { self.effect = glassEffect }
77
+ }
78
+
79
+ // UIGlassEffect can reconfigure the internal contentView in a way that
80
+ // disables user interaction when no subviews are present at the time the
81
+ // effect is applied. In React Native (Fabric), child component views may
82
+ // be mounted into contentView *after* layoutSubviews triggers setupView(),
83
+ // leaving contentView with userInteractionEnabled == false for the
84
+ // lifetime of this view. Force it back on so touches always reach children.
85
+ self.contentView.isUserInteractionEnabled = true
86
+ }
87
+ }
88
+
89
+ #else
90
+
91
+ @objc public class LiquidGlassViewImpl: UIView {}
92
+
93
+ #endif
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+
3
+ export { default as LiquidGlassContainerView } from './LiquidGlassViewContainerNativeComponent';
4
+ //# sourceMappingURL=LiquidGlassContainerView.ios.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["default","LiquidGlassContainerView"],"sourceRoot":"../../src","sources":["LiquidGlassContainerView.ios.tsx"],"mappings":";;AAAA,SAASA,OAAO,IAAIC,wBAAwB,QAAQ,2CAA2C","ignoreList":[]}
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+
3
+ import { View } from 'react-native';
4
+
5
+ /**
6
+ * A view that renders multiple glass elements into a combined effect.
7
+ */
8
+ export const LiquidGlassContainerView = View;
9
+ //# sourceMappingURL=LiquidGlassContainerView.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["View","LiquidGlassContainerView"],"sourceRoot":"../../src","sources":["LiquidGlassContainerView.tsx"],"mappings":";;AACA,SAASA,IAAI,QAAQ,cAAc;;AAEnC;AACA;AACA;AACA,OAAO,MAAMC,wBAA0D,GAAGD,IAAI","ignoreList":[]}