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.
- package/LICENSE +20 -0
- package/LiquidGlass.podspec +21 -0
- package/README.md +144 -0
- package/ios/LiquidGlassContainerView.h +15 -0
- package/ios/LiquidGlassContainerView.mm +70 -0
- package/ios/LiquidGlassContainerView.swift +28 -0
- package/ios/LiquidGlassModule.h +9 -0
- package/ios/LiquidGlassModule.mm +47 -0
- package/ios/LiquidGlassView.h +14 -0
- package/ios/LiquidGlassView.mm +170 -0
- package/ios/LiquidGlassView.swift +93 -0
- package/lib/module/LiquidGlassContainerView.ios.js +4 -0
- package/lib/module/LiquidGlassContainerView.ios.js.map +1 -0
- package/lib/module/LiquidGlassContainerView.js +9 -0
- package/lib/module/LiquidGlassContainerView.js.map +1 -0
- package/lib/module/LiquidGlassTabBar.ios.js +254 -0
- package/lib/module/LiquidGlassTabBar.ios.js.map +1 -0
- package/lib/module/LiquidGlassTabBar.js +70 -0
- package/lib/module/LiquidGlassTabBar.js.map +1 -0
- package/lib/module/LiquidGlassView.ios.js +4 -0
- package/lib/module/LiquidGlassView.ios.js.map +1 -0
- package/lib/module/LiquidGlassView.js +9 -0
- package/lib/module/LiquidGlassView.js.map +1 -0
- package/lib/module/LiquidGlassViewContainerNativeComponent.ts +15 -0
- package/lib/module/LiquidGlassViewNativeComponent.ts +38 -0
- package/lib/module/NativeLiquidGlassModule.js +5 -0
- package/lib/module/NativeLiquidGlassModule.js.map +1 -0
- package/lib/module/index.js +19 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/isLiquidGlassSupported.ios.js +5 -0
- package/lib/module/isLiquidGlassSupported.ios.js.map +1 -0
- package/lib/module/isLiquidGlassSupported.js +4 -0
- package/lib/module/isLiquidGlassSupported.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/typescript/package.json +1 -0
- package/lib/typescript/src/LiquidGlassContainerView.d.ts +6 -0
- package/lib/typescript/src/LiquidGlassContainerView.d.ts.map +1 -0
- package/lib/typescript/src/LiquidGlassContainerView.ios.d.ts +2 -0
- package/lib/typescript/src/LiquidGlassContainerView.ios.d.ts.map +1 -0
- package/lib/typescript/src/LiquidGlassTabBar.d.ts +47 -0
- package/lib/typescript/src/LiquidGlassTabBar.d.ts.map +1 -0
- package/lib/typescript/src/LiquidGlassTabBar.ios.d.ts +59 -0
- package/lib/typescript/src/LiquidGlassTabBar.ios.d.ts.map +1 -0
- package/lib/typescript/src/LiquidGlassView.d.ts +6 -0
- package/lib/typescript/src/LiquidGlassView.d.ts.map +1 -0
- package/lib/typescript/src/LiquidGlassView.ios.d.ts +2 -0
- package/lib/typescript/src/LiquidGlassView.ios.d.ts.map +1 -0
- package/lib/typescript/src/LiquidGlassViewContainerNativeComponent.d.ts +11 -0
- package/lib/typescript/src/LiquidGlassViewContainerNativeComponent.d.ts.map +1 -0
- package/lib/typescript/src/LiquidGlassViewNativeComponent.d.ts +33 -0
- package/lib/typescript/src/LiquidGlassViewNativeComponent.d.ts.map +1 -0
- package/lib/typescript/src/NativeLiquidGlassModule.d.ts +10 -0
- package/lib/typescript/src/NativeLiquidGlassModule.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +20 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/lib/typescript/src/isLiquidGlassSupported.d.ts +2 -0
- package/lib/typescript/src/isLiquidGlassSupported.d.ts.map +1 -0
- package/lib/typescript/src/isLiquidGlassSupported.ios.d.ts +2 -0
- package/lib/typescript/src/isLiquidGlassSupported.ios.d.ts.map +1 -0
- package/package.json +169 -0
- package/src/LiquidGlassContainerView.ios.tsx +1 -0
- package/src/LiquidGlassContainerView.tsx +7 -0
- package/src/LiquidGlassTabBar.ios.tsx +369 -0
- package/src/LiquidGlassTabBar.tsx +119 -0
- package/src/LiquidGlassView.ios.tsx +1 -0
- package/src/LiquidGlassView.tsx +7 -0
- package/src/LiquidGlassViewContainerNativeComponent.ts +15 -0
- package/src/LiquidGlassViewNativeComponent.ts +38 -0
- package/src/NativeLiquidGlassModule.ts +13 -0
- package/src/index.tsx +26 -0
- package/src/isLiquidGlassSupported.ios.ts +4 -0
- 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
|
+
[](https://github.com/kashif8299/rn-liquid-glass-bottom-tabs/blob/main/LICENSE)
|
|
5
|
+
[](https://www.npmjs.org/package/rn-liquid-glass-bottom-tabs)
|
|
6
|
+
[](https://www.npmjs.org/package/rn-liquid-glass-bottom-tabs)
|
|
7
|
+
[](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,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 @@
|
|
|
1
|
+
{"version":3,"names":["default","LiquidGlassContainerView"],"sourceRoot":"../../src","sources":["LiquidGlassContainerView.ios.tsx"],"mappings":";;AAAA,SAASA,OAAO,IAAIC,wBAAwB,QAAQ,2CAA2C","ignoreList":[]}
|
|
@@ -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":[]}
|