@sbaiahmed1/react-native-blur 4.0.0 → 4.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 (45) hide show
  1. package/README.md +155 -47
  2. package/android/build.gradle +1 -1
  3. package/android/src/main/java/com/sbaiahmed1/reactnativeblur/BlurType.kt +63 -0
  4. package/android/src/main/java/com/sbaiahmed1/reactnativeblur/ReactNativeBlurPackage.kt +1 -0
  5. package/android/src/main/java/com/sbaiahmed1/reactnativeblur/ReactNativeBlurView.kt +63 -43
  6. package/android/src/main/java/com/sbaiahmed1/reactnativeblur/ReactNativeBlurViewManager.kt +14 -26
  7. package/android/src/main/java/com/sbaiahmed1/reactnativeblur/ReactNativeProgressiveBlurView.kt +320 -0
  8. package/android/src/main/java/com/sbaiahmed1/reactnativeblur/ReactNativeProgressiveBlurViewManager.kt +81 -0
  9. package/ios/Helpers/BlurStyleHelpers.swift +20 -0
  10. package/ios/Helpers/ReactNativeProgressiveBlurViewHelper.swift +39 -0
  11. package/ios/ReactNativeLiquidGlassView.mm +8 -1
  12. package/ios/ReactNativeProgressiveBlurView.h +14 -0
  13. package/ios/ReactNativeProgressiveBlurView.mm +213 -0
  14. package/ios/ReactNativeProgressiveBlurViewManager.h +4 -0
  15. package/ios/ReactNativeProgressiveBlurViewManager.mm +137 -0
  16. package/ios/Views/LiquidGlassContainerView.swift +8 -30
  17. package/ios/Views/ProgressiveBlurView.swift +124 -0
  18. package/ios/Views/VariableBlurView.swift +142 -0
  19. package/lib/module/BlurView.js +23 -12
  20. package/lib/module/BlurView.js.map +1 -1
  21. package/lib/module/LiquidGlassView.js +3 -1
  22. package/lib/module/LiquidGlassView.js.map +1 -1
  23. package/lib/module/ProgressiveBlurView.js +98 -0
  24. package/lib/module/ProgressiveBlurView.js.map +1 -0
  25. package/lib/module/ReactNativeBlurViewNativeComponent.ts +11 -1
  26. package/lib/module/ReactNativeProgressiveBlurViewNativeComponent.ts +45 -0
  27. package/lib/module/index.js +2 -0
  28. package/lib/module/index.js.map +1 -1
  29. package/lib/typescript/src/BlurView.d.ts.map +1 -1
  30. package/lib/typescript/src/LiquidGlassView.d.ts.map +1 -1
  31. package/lib/typescript/src/ProgressiveBlurView.d.ts +97 -0
  32. package/lib/typescript/src/ProgressiveBlurView.d.ts.map +1 -0
  33. package/lib/typescript/src/ReactNativeBlurViewNativeComponent.d.ts +1 -1
  34. package/lib/typescript/src/ReactNativeBlurViewNativeComponent.d.ts.map +1 -1
  35. package/lib/typescript/src/ReactNativeProgressiveBlurViewNativeComponent.d.ts +14 -0
  36. package/lib/typescript/src/ReactNativeProgressiveBlurViewNativeComponent.d.ts.map +1 -0
  37. package/lib/typescript/src/index.d.ts +4 -0
  38. package/lib/typescript/src/index.d.ts.map +1 -1
  39. package/package.json +5 -5
  40. package/src/BlurView.tsx +21 -17
  41. package/src/LiquidGlassView.tsx +3 -4
  42. package/src/ProgressiveBlurView.tsx +161 -0
  43. package/src/ReactNativeBlurViewNativeComponent.ts +11 -1
  44. package/src/ReactNativeProgressiveBlurViewNativeComponent.ts +45 -0
  45. package/src/index.tsx +6 -0
@@ -0,0 +1,213 @@
1
+ #import "ReactNativeProgressiveBlurView.h"
2
+
3
+ #import <react/renderer/components/ReactNativeBlurViewSpec/ComponentDescriptors.h>
4
+ #import <react/renderer/components/ReactNativeBlurViewSpec/EventEmitters.h>
5
+ #import <react/renderer/components/ReactNativeBlurViewSpec/Props.h>
6
+ #import <react/renderer/components/ReactNativeBlurViewSpec/RCTComponentViewHelpers.h>
7
+
8
+ #import "RCTFabricComponentsPlugins.h"
9
+
10
+ #if __has_include("ReactNativeBlur-Swift.h")
11
+ #import "ReactNativeBlur-Swift.h"
12
+ #else
13
+ #import <ReactNativeBlur/ReactNativeBlur-Swift.h>
14
+ #endif
15
+
16
+ using namespace facebook::react;
17
+
18
+ @interface ReactNativeProgressiveBlurView () <RCTReactNativeProgressiveBlurViewViewProtocol>
19
+ @end
20
+
21
+ @implementation ReactNativeProgressiveBlurView {
22
+ ProgressiveBlurView *_progressiveBlurView;
23
+ Props::Shared _props;
24
+ }
25
+
26
+ + (UIColor *)colorFromString:(NSString *)colorString {
27
+ // Input validation
28
+ if (!colorString || [colorString isEqualToString:@""] || colorString.length == 0) {
29
+ return [UIColor clearColor];
30
+ }
31
+
32
+ if (colorString.length > 50) {
33
+ NSLog(@"[ReactNativeProgressiveBlurView] Warning: Color string too long, using default clear color");
34
+ return [UIColor clearColor];
35
+ }
36
+
37
+ // Handle common color names
38
+ NSDictionary *colorMap = @{
39
+ @"red": [UIColor redColor],
40
+ @"blue": [UIColor blueColor],
41
+ @"green": [UIColor greenColor],
42
+ @"yellow": [UIColor yellowColor],
43
+ @"orange": [UIColor orangeColor],
44
+ @"purple": [UIColor purpleColor],
45
+ @"black": [UIColor blackColor],
46
+ @"white": [UIColor whiteColor],
47
+ @"gray": [UIColor grayColor],
48
+ @"clear": [UIColor clearColor],
49
+ @"transparent": [UIColor clearColor]
50
+ };
51
+
52
+ UIColor *namedColor = colorMap[colorString.lowercaseString];
53
+ if (namedColor) {
54
+ return namedColor;
55
+ }
56
+
57
+ // Handle hex colors
58
+ NSString *hexString = colorString;
59
+ if ([hexString hasPrefix:@"#"]) {
60
+ if (hexString.length < 2) {
61
+ NSLog(@"[ReactNativeProgressiveBlurView] Warning: Invalid hex color format '%@'", colorString);
62
+ return [UIColor clearColor];
63
+ }
64
+ hexString = [hexString substringFromIndex:1];
65
+ }
66
+
67
+ NSCharacterSet *hexCharacterSet = [NSCharacterSet characterSetWithCharactersInString:@"0123456789ABCDEFabcdef"];
68
+ NSCharacterSet *invalidCharacters = [hexCharacterSet invertedSet];
69
+ if ([hexString rangeOfCharacterFromSet:invalidCharacters].location != NSNotFound) {
70
+ NSLog(@"[ReactNativeProgressiveBlurView] Warning: Invalid hex color format '%@'", colorString);
71
+ return [UIColor clearColor];
72
+ }
73
+
74
+ if (hexString.length == 6) {
75
+ unsigned int hexValue;
76
+ NSScanner *scanner = [NSScanner scannerWithString:hexString];
77
+ if ([scanner scanHexInt:&hexValue] && [scanner isAtEnd]) {
78
+ return [UIColor colorWithRed:((hexValue & 0xFF0000) >> 16) / 255.0
79
+ green:((hexValue & 0x00FF00) >> 8) / 255.0
80
+ blue:(hexValue & 0x0000FF) / 255.0
81
+ alpha:1.0];
82
+ }
83
+ } else if (hexString.length == 8) {
84
+ unsigned long long hexValue;
85
+ NSScanner *scanner = [NSScanner scannerWithString:hexString];
86
+ if ([scanner scanHexLongLong:&hexValue] && [scanner isAtEnd]) {
87
+ return [UIColor colorWithRed:((hexValue & 0xFF000000) >> 24) / 255.0
88
+ green:((hexValue & 0x00FF0000) >> 16) / 255.0
89
+ blue:((hexValue & 0x0000FF00) >> 8) / 255.0
90
+ alpha:(hexValue & 0x000000FF) / 255.0];
91
+ }
92
+ } else if (hexString.length == 3) {
93
+ unsigned int hexValue;
94
+ NSScanner *scanner = [NSScanner scannerWithString:hexString];
95
+ if ([scanner scanHexInt:&hexValue] && [scanner isAtEnd]) {
96
+ unsigned int r = (hexValue & 0xF00) >> 8;
97
+ unsigned int g = (hexValue & 0x0F0) >> 4;
98
+ unsigned int b = (hexValue & 0x00F);
99
+ return [UIColor colorWithRed:(r | (r << 4)) / 255.0
100
+ green:(g | (g << 4)) / 255.0
101
+ blue:(b | (b << 4)) / 255.0
102
+ alpha:1.0];
103
+ }
104
+ }
105
+
106
+ NSLog(@"[ReactNativeProgressiveBlurView] Warning: Could not parse color '%@'", colorString);
107
+ return [UIColor clearColor];
108
+ }
109
+
110
+ + (ComponentDescriptorProvider)componentDescriptorProvider
111
+ {
112
+ return concreteComponentDescriptorProvider<ReactNativeProgressiveBlurViewComponentDescriptor>();
113
+ }
114
+
115
+ - (instancetype)initWithFrame:(CGRect)frame
116
+ {
117
+ if (self = [super initWithFrame:frame]) {
118
+ static const auto defaultProps = std::make_shared<const ReactNativeProgressiveBlurViewProps>();
119
+ _props = defaultProps;
120
+
121
+ const auto &pbvProps = *std::static_pointer_cast<const ReactNativeProgressiveBlurViewProps>(defaultProps);
122
+
123
+ _progressiveBlurView = [ReactNativeProgressiveBlurViewHelper createProgressiveBlurViewWithFrame:frame];
124
+
125
+ // Set initial properties from default props
126
+ [ReactNativeProgressiveBlurViewHelper updateProgressiveBlurView:_progressiveBlurView withBlurAmount:pbvProps.blurAmount];
127
+
128
+ if (pbvProps.blurType != facebook::react::ReactNativeProgressiveBlurViewBlurType::Xlight) {
129
+ NSString *blurTypeString = [[NSString alloc] initWithUTF8String:toString(pbvProps.blurType).c_str()];
130
+ [ReactNativeProgressiveBlurViewHelper updateProgressiveBlurView:_progressiveBlurView withBlurType:blurTypeString];
131
+ }
132
+
133
+ if (pbvProps.direction != facebook::react::ReactNativeProgressiveBlurViewDirection::BlurredTopClearBottom) {
134
+ NSString *directionString = [[NSString alloc] initWithUTF8String:toString(pbvProps.direction).c_str()];
135
+ [ReactNativeProgressiveBlurViewHelper updateProgressiveBlurView:_progressiveBlurView withDirection:directionString];
136
+ }
137
+
138
+ [ReactNativeProgressiveBlurViewHelper updateProgressiveBlurView:_progressiveBlurView withStartOffset:pbvProps.startOffset];
139
+
140
+ if (!pbvProps.reducedTransparencyFallbackColor.empty()) {
141
+ NSString *fallbackColorString = [[NSString alloc] initWithUTF8String:pbvProps.reducedTransparencyFallbackColor.c_str()];
142
+ UIColor *fallbackColor = [ReactNativeProgressiveBlurView colorFromString:fallbackColorString];
143
+ [ReactNativeProgressiveBlurViewHelper updateProgressiveBlurView:_progressiveBlurView withReducedTransparencyFallbackColor:fallbackColor];
144
+ }
145
+
146
+ [self addSubview:_progressiveBlurView];
147
+ }
148
+ return self;
149
+ }
150
+
151
+ - (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps
152
+ {
153
+ const auto &oldViewProps = *std::static_pointer_cast<ReactNativeProgressiveBlurViewProps const>(_props);
154
+ const auto &newViewProps = *std::static_pointer_cast<ReactNativeProgressiveBlurViewProps const>(props);
155
+
156
+ if (oldViewProps.blurAmount != newViewProps.blurAmount) {
157
+ [ReactNativeProgressiveBlurViewHelper updateProgressiveBlurView:_progressiveBlurView withBlurAmount:newViewProps.blurAmount];
158
+ }
159
+
160
+ if (oldViewProps.blurType != newViewProps.blurType) {
161
+ NSString *blurTypeString = [[NSString alloc] initWithUTF8String:toString(newViewProps.blurType).c_str()];
162
+ [ReactNativeProgressiveBlurViewHelper updateProgressiveBlurView:_progressiveBlurView withBlurType:blurTypeString];
163
+ }
164
+
165
+ if (oldViewProps.direction != newViewProps.direction) {
166
+ NSString *directionString = [[NSString alloc] initWithUTF8String:toString(newViewProps.direction).c_str()];
167
+ [ReactNativeProgressiveBlurViewHelper updateProgressiveBlurView:_progressiveBlurView withDirection:directionString];
168
+ }
169
+
170
+ if (oldViewProps.startOffset != newViewProps.startOffset) {
171
+ [ReactNativeProgressiveBlurViewHelper updateProgressiveBlurView:_progressiveBlurView withStartOffset:newViewProps.startOffset];
172
+ }
173
+
174
+ if (oldViewProps.reducedTransparencyFallbackColor != newViewProps.reducedTransparencyFallbackColor) {
175
+ if (!newViewProps.reducedTransparencyFallbackColor.empty()) {
176
+ NSString *fallbackColorString = [[NSString alloc] initWithUTF8String:newViewProps.reducedTransparencyFallbackColor.c_str()];
177
+ UIColor *fallbackColor = [ReactNativeProgressiveBlurView colorFromString:fallbackColorString];
178
+ [ReactNativeProgressiveBlurViewHelper updateProgressiveBlurView:_progressiveBlurView withReducedTransparencyFallbackColor:fallbackColor];
179
+ }
180
+ }
181
+
182
+ _props = props;
183
+ [super updateProps:props oldProps:oldProps];
184
+ }
185
+
186
+ - (void)layoutSubviews
187
+ {
188
+ [super layoutSubviews];
189
+ _progressiveBlurView.frame = self.bounds;
190
+ }
191
+
192
+ - (void)mountChildComponentView:(UIView<RCTComponentViewProtocol> *)childComponentView index:(NSInteger)index
193
+ {
194
+ [_progressiveBlurView addSubview:childComponentView];
195
+ }
196
+
197
+ - (void)unmountChildComponentView:(UIView<RCTComponentViewProtocol> *)childComponentView index:(NSInteger)index
198
+ {
199
+ [childComponentView removeFromSuperview];
200
+ }
201
+
202
+ - (void)dealloc
203
+ {
204
+ [_progressiveBlurView removeFromSuperview];
205
+ _progressiveBlurView = nil;
206
+ }
207
+
208
+ @end
209
+
210
+ Class<RCTComponentViewProtocol> ProgressiveBlurryViewCls(void)
211
+ {
212
+ return ReactNativeProgressiveBlurView.class;
213
+ }
@@ -0,0 +1,4 @@
1
+ #import <React/RCTViewManager.h>
2
+
3
+ @interface ReactNativeProgressiveBlurViewManager : RCTViewManager
4
+ @end
@@ -0,0 +1,137 @@
1
+ #import "ReactNativeProgressiveBlurViewManager.h"
2
+
3
+ #if __has_include("ReactNativeBlur-Swift.h")
4
+ #import "ReactNativeBlur-Swift.h"
5
+ #else
6
+ #import <ReactNativeBlur/ReactNativeBlur-Swift.h>
7
+ #endif
8
+
9
+ #import <React/RCTUIManager.h>
10
+ #import <React/RCTBridge.h>
11
+
12
+ @interface ReactNativeProgressiveBlurViewManager ()
13
+
14
+ @end
15
+
16
+ @implementation ReactNativeProgressiveBlurViewManager
17
+
18
+ RCT_EXPORT_MODULE(ReactNativeProgressiveBlurView)
19
+
20
+ - (UIView *)view
21
+ {
22
+ return [ReactNativeProgressiveBlurViewHelper createProgressiveBlurViewWithFrame:CGRectZero];
23
+ }
24
+
25
+ // Export properties
26
+
27
+ RCT_CUSTOM_VIEW_PROPERTY(blurAmount, NSNumber, ProgressiveBlurView)
28
+ {
29
+ double amount = json ? [RCTConvert double:json] : 20.0;
30
+ [ReactNativeProgressiveBlurViewHelper updateProgressiveBlurView:view withBlurAmount:amount];
31
+ }
32
+
33
+ RCT_CUSTOM_VIEW_PROPERTY(blurType, NSString, ProgressiveBlurView)
34
+ {
35
+ NSString *blurType = json ? [RCTConvert NSString:json] : @"regular";
36
+ [ReactNativeProgressiveBlurViewHelper updateProgressiveBlurView:view withBlurType:blurType];
37
+ }
38
+
39
+ RCT_CUSTOM_VIEW_PROPERTY(direction, NSString, ProgressiveBlurView)
40
+ {
41
+ NSString *direction = json ? [RCTConvert NSString:json] : @"blurredTopClearBottom";
42
+ [ReactNativeProgressiveBlurViewHelper updateProgressiveBlurView:view withDirection:direction];
43
+ }
44
+
45
+ RCT_CUSTOM_VIEW_PROPERTY(startOffset, NSNumber, ProgressiveBlurView)
46
+ {
47
+ double offset = json ? [RCTConvert double:json] : 0.0;
48
+ [ReactNativeProgressiveBlurViewHelper updateProgressiveBlurView:view withStartOffset:offset];
49
+ }
50
+
51
+ RCT_CUSTOM_VIEW_PROPERTY(reducedTransparencyFallbackColor, NSString, ProgressiveBlurView)
52
+ {
53
+ NSString *colorString = json ? [RCTConvert NSString:json] : @"#FFFFFF";
54
+ UIColor *color = [self colorFromString:colorString];
55
+ [ReactNativeProgressiveBlurViewHelper updateProgressiveBlurView:view withReducedTransparencyFallbackColor:color];
56
+ }
57
+
58
+ // Color parsing helper method
59
+ - (UIColor *)colorFromString:(NSString *)colorString {
60
+ if (!colorString || [colorString isEqualToString:@""] || colorString.length == 0) {
61
+ return [UIColor clearColor];
62
+ }
63
+
64
+ if (colorString.length > 50) {
65
+ NSLog(@"[ReactNativeProgressiveBlurViewManager] Warning: Color string too long");
66
+ return [UIColor clearColor];
67
+ }
68
+
69
+ NSDictionary *colorMap = @{
70
+ @"red": [UIColor redColor],
71
+ @"blue": [UIColor blueColor],
72
+ @"green": [UIColor greenColor],
73
+ @"yellow": [UIColor yellowColor],
74
+ @"orange": [UIColor orangeColor],
75
+ @"purple": [UIColor purpleColor],
76
+ @"black": [UIColor blackColor],
77
+ @"white": [UIColor whiteColor],
78
+ @"gray": [UIColor grayColor],
79
+ @"clear": [UIColor clearColor],
80
+ @"transparent": [UIColor clearColor]
81
+ };
82
+
83
+ UIColor *namedColor = colorMap[colorString.lowercaseString];
84
+ if (namedColor) {
85
+ return namedColor;
86
+ }
87
+
88
+ NSString *hexString = colorString;
89
+ if ([hexString hasPrefix:@"#"]) {
90
+ if (hexString.length < 2) {
91
+ return [UIColor clearColor];
92
+ }
93
+ hexString = [hexString substringFromIndex:1];
94
+ }
95
+
96
+ NSCharacterSet *hexCharacterSet = [NSCharacterSet characterSetWithCharactersInString:@"0123456789ABCDEFabcdef"];
97
+ NSCharacterSet *invalidCharacters = [hexCharacterSet invertedSet];
98
+ if ([hexString rangeOfCharacterFromSet:invalidCharacters].location != NSNotFound) {
99
+ return [UIColor clearColor];
100
+ }
101
+
102
+ if (hexString.length == 6) {
103
+ unsigned int hexValue;
104
+ NSScanner *scanner = [NSScanner scannerWithString:hexString];
105
+ if ([scanner scanHexInt:&hexValue] && [scanner isAtEnd]) {
106
+ return [UIColor colorWithRed:((hexValue & 0xFF0000) >> 16) / 255.0
107
+ green:((hexValue & 0x00FF00) >> 8) / 255.0
108
+ blue:(hexValue & 0x0000FF) / 255.0
109
+ alpha:1.0];
110
+ }
111
+ } else if (hexString.length == 8) {
112
+ unsigned long long hexValue;
113
+ NSScanner *scanner = [NSScanner scannerWithString:hexString];
114
+ if ([scanner scanHexLongLong:&hexValue] && [scanner isAtEnd]) {
115
+ return [UIColor colorWithRed:((hexValue & 0xFF000000) >> 24) / 255.0
116
+ green:((hexValue & 0x00FF0000) >> 16) / 255.0
117
+ blue:((hexValue & 0x0000FF00) >> 8) / 255.0
118
+ alpha:(hexValue & 0x000000FF) / 255.0];
119
+ }
120
+ } else if (hexString.length == 3) {
121
+ unsigned int hexValue;
122
+ NSScanner *scanner = [NSScanner scannerWithString:hexString];
123
+ if ([scanner scanHexInt:&hexValue] && [scanner isAtEnd]) {
124
+ unsigned int r = (hexValue & 0xF00) >> 8;
125
+ unsigned int g = (hexValue & 0x0F0) >> 4;
126
+ unsigned int b = (hexValue & 0x00F);
127
+ return [UIColor colorWithRed:(r | (r << 4)) / 255.0
128
+ green:(g | (g << 4)) / 255.0
129
+ blue:(b | (b << 4)) / 255.0
130
+ alpha:1.0];
131
+ }
132
+ }
133
+
134
+ return [UIColor clearColor];
135
+ }
136
+
137
+ @end
@@ -64,6 +64,10 @@ import UIKit
64
64
  let effectView = UIVisualEffectView()
65
65
  effectView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
66
66
  effectView.frame = bounds
67
+
68
+ // Don't clip bounds to allow interactive glass animations to be visible
69
+ effectView.clipsToBounds = false
70
+
67
71
  addSubview(effectView)
68
72
  self.glassEffectView = effectView
69
73
 
@@ -74,30 +78,6 @@ import UIKit
74
78
 
75
79
  @objc public func setBorderRadius(_ radius: CGFloat) {
76
80
  allBorderRadius = radius
77
- topLeftRadius = radius
78
- topRightRadius = radius
79
- bottomLeftRadius = radius
80
- bottomRightRadius = radius
81
- updateBorderRadius()
82
- }
83
-
84
- @objc public func setBorderTopLeftRadius(_ radius: CGFloat) {
85
- topLeftRadius = radius
86
- updateBorderRadius()
87
- }
88
-
89
- @objc public func setBorderTopRightRadius(_ radius: CGFloat) {
90
- topRightRadius = radius
91
- updateBorderRadius()
92
- }
93
-
94
- @objc public func setBorderBottomLeftRadius(_ radius: CGFloat) {
95
- bottomLeftRadius = radius
96
- updateBorderRadius()
97
- }
98
-
99
- @objc public func setBorderBottomRightRadius(_ radius: CGFloat) {
100
- bottomRightRadius = radius
101
81
  updateBorderRadius()
102
82
  }
103
83
 
@@ -129,17 +109,16 @@ import UIKit
129
109
  } else {
130
110
  backgroundColor = reducedTransparencyFallbackColor
131
111
  layer.cornerRadius = allBorderRadius
132
- layer.masksToBounds = true
133
112
  }
134
113
  }
135
114
 
136
115
  private func updateBorderRadius() {
137
116
  if #available(iOS 26.0, *) {
138
117
  #if compiler(>=6.2)
139
- let topLeft = UICornerRadius(floatLiteral: Double(topLeftRadius))
140
- let topRight = UICornerRadius(floatLiteral: Double(topRightRadius))
141
- let bottomLeft = UICornerRadius(floatLiteral: Double(bottomLeftRadius))
142
- let bottomRight = UICornerRadius(floatLiteral: Double(bottomRightRadius))
118
+ let topLeft = UICornerRadius(floatLiteral: Double(allBorderRadius))
119
+ let topRight = UICornerRadius(floatLiteral: Double(allBorderRadius))
120
+ let bottomLeft = UICornerRadius(floatLiteral: Double(allBorderRadius))
121
+ let bottomRight = UICornerRadius(floatLiteral: Double(allBorderRadius))
143
122
 
144
123
  glassEffectView?.cornerConfiguration = .corners(
145
124
  topLeftRadius: topLeft,
@@ -153,7 +132,6 @@ import UIKit
153
132
  #endif
154
133
  } else {
155
134
  layer.cornerRadius = allBorderRadius
156
- layer.masksToBounds = true
157
135
  }
158
136
  }
159
137
 
@@ -0,0 +1,124 @@
1
+ // ProgressiveBlurView.swift
2
+ // React Native wrapper for VariableBlurView
3
+
4
+ import SwiftUI
5
+ import UIKit
6
+
7
+ @objc public class ProgressiveBlurView: UIView {
8
+
9
+ private var variableBlurView: VariableBlurView?
10
+
11
+ @objc public var blurAmount: Double = 20.0 {
12
+ didSet {
13
+ updateBlur()
14
+ }
15
+ }
16
+
17
+ @objc public var direction: String = "blurredTopClearBottom" {
18
+ didSet {
19
+ updateBlur()
20
+ }
21
+ }
22
+
23
+ @objc public var startOffset: Double = 0.0 {
24
+ didSet {
25
+ updateBlur()
26
+ }
27
+ }
28
+
29
+ @objc public var blurTypeString: String = "regular" {
30
+ didSet {
31
+ updateBlur()
32
+ }
33
+ }
34
+
35
+ @objc public var reducedTransparencyFallbackColor: UIColor = .white {
36
+ didSet {
37
+ updateBlur()
38
+ }
39
+ }
40
+
41
+ public override init(frame: CGRect) {
42
+ super.init(frame: frame)
43
+ setupView()
44
+ }
45
+
46
+ required init?(coder: NSCoder) {
47
+ super.init(coder: coder)
48
+ setupView()
49
+ }
50
+
51
+ private func setupView() {
52
+ // Remove old view if exists
53
+ variableBlurView?.removeFromSuperview()
54
+
55
+ let blurStyle = blurStyleFromString(blurTypeString)
56
+ let blurDirection = VariableBlurDirection(fromString: direction)
57
+
58
+ let variableBlur = VariableBlurView(
59
+ maxBlurRadius: CGFloat(blurAmount),
60
+ direction: blurDirection,
61
+ startOffset: CGFloat(startOffset),
62
+ blurStyle: blurStyle
63
+ )
64
+
65
+ variableBlur.translatesAutoresizingMaskIntoConstraints = false
66
+ addSubview(variableBlur)
67
+
68
+ NSLayoutConstraint.activate([
69
+ variableBlur.topAnchor.constraint(equalTo: topAnchor),
70
+ variableBlur.leadingAnchor.constraint(equalTo: leadingAnchor),
71
+ variableBlur.trailingAnchor.constraint(equalTo: trailingAnchor),
72
+ variableBlur.bottomAnchor.constraint(equalTo: bottomAnchor)
73
+ ])
74
+
75
+ self.variableBlurView = variableBlur
76
+
77
+ // Handle reduced transparency
78
+ if UIAccessibility.isReduceTransparencyEnabled {
79
+ variableBlur.isHidden = true
80
+ backgroundColor = reducedTransparencyFallbackColor
81
+ } else {
82
+ variableBlur.isHidden = false
83
+ backgroundColor = .clear
84
+ }
85
+ }
86
+
87
+ private func updateBlur() {
88
+ guard let variableBlurView = variableBlurView else {
89
+ setupView()
90
+ return
91
+ }
92
+
93
+ let blurStyle = blurStyleFromString(blurTypeString)
94
+ let blurDirection = VariableBlurDirection(fromString: direction)
95
+
96
+ variableBlurView.updateBlur(
97
+ maxBlurRadius: CGFloat(blurAmount),
98
+ direction: blurDirection,
99
+ startOffset: CGFloat(startOffset),
100
+ blurStyle: blurStyle
101
+ )
102
+
103
+ // Handle reduced transparency
104
+ if UIAccessibility.isReduceTransparencyEnabled {
105
+ variableBlurView.isHidden = true
106
+ backgroundColor = reducedTransparencyFallbackColor
107
+ } else {
108
+ variableBlurView.isHidden = false
109
+ backgroundColor = .clear
110
+ }
111
+ }
112
+
113
+ public override func didMoveToWindow() {
114
+ super.didMoveToWindow()
115
+ if window != nil {
116
+ updateBlur()
117
+ }
118
+ }
119
+
120
+ deinit {
121
+ variableBlurView?.removeFromSuperview()
122
+ variableBlurView = nil
123
+ }
124
+ }
@@ -0,0 +1,142 @@
1
+ // VariableBlurView.swift
2
+ // Progressive/Variable Blur implementation based on VariableBlur library
3
+
4
+ import SwiftUI
5
+ import UIKit
6
+ import CoreImage.CIFilterBuiltins
7
+ import QuartzCore
8
+
9
+ public enum VariableBlurDirection: String {
10
+ case blurredTopClearBottom
11
+ case blurredBottomClearTop
12
+
13
+ init(fromString string: String) {
14
+ switch string.lowercased() {
15
+ case "blurredbottomcleartop", "bottomtotop", "bottom":
16
+ self = .blurredBottomClearTop
17
+ default:
18
+ self = .blurredTopClearBottom
19
+ }
20
+ }
21
+ }
22
+
23
+ open class VariableBlurView: UIVisualEffectView {
24
+
25
+ private var maxBlurRadius: CGFloat = 20
26
+ private var direction: VariableBlurDirection = .blurredTopClearBottom
27
+ private var startOffset: CGFloat = 0
28
+
29
+ public init(
30
+ maxBlurRadius: CGFloat = 20,
31
+ direction: VariableBlurDirection = .blurredTopClearBottom,
32
+ startOffset: CGFloat = 0,
33
+ blurStyle: UIBlurEffect.Style = .regular
34
+ ) {
35
+ self.maxBlurRadius = maxBlurRadius
36
+ self.direction = direction
37
+ self.startOffset = startOffset
38
+
39
+ super.init(effect: UIBlurEffect(style: blurStyle))
40
+
41
+ setupVariableBlur()
42
+ }
43
+
44
+ required public init?(coder: NSCoder) {
45
+ super.init(coder: coder)
46
+ setupVariableBlur()
47
+ }
48
+
49
+ public func updateBlur(
50
+ maxBlurRadius: CGFloat,
51
+ direction: VariableBlurDirection,
52
+ startOffset: CGFloat,
53
+ blurStyle: UIBlurEffect.Style = .regular
54
+ ) {
55
+ self.maxBlurRadius = maxBlurRadius
56
+ self.direction = direction
57
+ self.startOffset = startOffset
58
+ self.effect = UIBlurEffect(style: blurStyle)
59
+
60
+ setupVariableBlur()
61
+ }
62
+
63
+ private func setupVariableBlur() {
64
+ // Creates filter via runtime reflection
65
+ // This uses a private Core Animation filter called "variableBlur"
66
+ let clsName = String("retliFAC".reversed()) // CAFilter
67
+ guard let Cls = NSClassFromString(clsName) as? NSObject.Type else {
68
+ print("[VariableBlur] Error: Can't find filter class")
69
+ return
70
+ }
71
+
72
+ let selName = String(":epyThtiWretlif".reversed()) // filterWithType:
73
+ guard let variableBlur = Cls.self.perform(
74
+ NSSelectorFromString(selName),
75
+ with: "variableBlur"
76
+ )?.takeUnretainedValue() as? NSObject else {
77
+ print("[VariableBlur] Error: Can't create variableBlur filter")
78
+ return
79
+ }
80
+
81
+ let gradientImage = makeGradientImage(
82
+ startOffset: startOffset,
83
+ direction: direction
84
+ )
85
+
86
+ variableBlur.setValue(maxBlurRadius, forKey: "inputRadius")
87
+ variableBlur.setValue(gradientImage, forKey: "inputMaskImage")
88
+ variableBlur.setValue(true, forKey: "inputNormalizeEdges")
89
+
90
+ let backdropLayer = subviews.first?.layer
91
+ backdropLayer?.filters = [variableBlur]
92
+
93
+ // Hide the default visual effect view overlays
94
+ for subview in subviews.dropFirst() {
95
+ subview.alpha = 0
96
+ }
97
+ }
98
+
99
+ open override func didMoveToWindow() {
100
+ super.didMoveToWindow()
101
+ guard let window, let backdropLayer = subviews.first?.layer else { return }
102
+ backdropLayer.setValue(
103
+ window.traitCollection.displayScale,
104
+ forKey: "scale"
105
+ )
106
+ }
107
+
108
+ open override func traitCollectionDidChange(
109
+ _ previousTraitCollection: UITraitCollection?
110
+ ) {
111
+ super.traitCollectionDidChange(previousTraitCollection)
112
+ // Re-setup blur if needed when trait collection changes
113
+ if let previousTraitCollection = previousTraitCollection,
114
+ traitCollection.userInterfaceStyle != previousTraitCollection.userInterfaceStyle {
115
+ setupVariableBlur()
116
+ }
117
+ }
118
+
119
+ private func makeGradientImage(
120
+ width: CGFloat = 100,
121
+ height: CGFloat = 100,
122
+ startOffset: CGFloat,
123
+ direction: VariableBlurDirection
124
+ ) -> CGImage {
125
+ // Try smoothLinearGradient for smoother transitions
126
+ let ciGradientFilter = CIFilter.smoothLinearGradient()
127
+ ciGradientFilter.color0 = CIColor.black
128
+ ciGradientFilter.color1 = CIColor.clear
129
+ ciGradientFilter.point0 = CGPoint(x: 0, y: height)
130
+ ciGradientFilter.point1 = CGPoint(x: 0, y: startOffset * height)
131
+
132
+ if case .blurredBottomClearTop = direction {
133
+ ciGradientFilter.point0.y = 0
134
+ ciGradientFilter.point1.y = height - ciGradientFilter.point1.y
135
+ }
136
+
137
+ return CIContext().createCGImage(
138
+ ciGradientFilter.outputImage!,
139
+ from: CGRect(x: 0, y: 0, width: width, height: height)
140
+ )!
141
+ }
142
+ }