@sbaiahmed1/react-native-blur 4.6.1-beta.5 → 4.6.1

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/README.md CHANGED
@@ -7,7 +7,7 @@ A modern React Native library providing **six specialized components** for advan
7
7
  <div align="center">
8
8
  <p>
9
9
  <img src="https://img.shields.io/npm/v/@sbaiahmed1/react-native-blur?style=for-the-badge&color=blue" alt="npm version" />
10
- <img src="https://img.shields.io/npm/dt/@sbaiahmed1/react-native-blur?style=for-the-badge&color=green" alt="downloads" />
10
+ <img src="https://img.shields.io/npm/dm/%40sbaiahmed1%2Freact-native-blur?style=for-the-badge" alt="downloads per month" />
11
11
  <img src="https://img.shields.io/github/license/sbaiahmed1/react-native-blur?style=for-the-badge&color=orange" alt="license" />
12
12
  <img src="https://img.shields.io/github/stars/sbaiahmed1/react-native-blur?style=for-the-badge&color=yellow" alt="stars" />
13
13
  </p>
@@ -1001,6 +1001,7 @@ MIT
1001
1001
 
1002
1002
  <div align="center">
1003
1003
  <img src="https://img.shields.io/github/contributors/sbaiahmed1/react-native-blur?style=for-the-badge" alt="contributors" />
1004
+ <img src="https://img.shields.io/npm/dt/@sbaiahmed1/react-native-blur?style=for-the-badge&color=green" alt="downloads" />
1004
1005
  <img src="https://img.shields.io/github/last-commit/sbaiahmed1/react-native-blur?style=for-the-badge" alt="last commit" />
1005
1006
  <img src="https://img.shields.io/github/issues/sbaiahmed1/react-native-blur?style=for-the-badge" alt="issues" />
1006
1007
  <img src="https://img.shields.io/github/issues-pr/sbaiahmed1/react-native-blur?style=for-the-badge" alt="pull requests" />
@@ -1,23 +1,35 @@
1
+ // ReactNativeBlurViewHelper.swift
2
+
3
+ import SwiftUI
1
4
  import UIKit
2
5
 
6
+ // MARK: - Objective-C Bridging Helpers
7
+
3
8
  @objc public class ReactNativeBlurViewHelper: NSObject {
9
+
10
+ /// Creates and returns a blur view.
4
11
  @objc public static func createBlurViewWithFrame(_ frame: CGRect) -> AdvancedBlurView {
5
12
  return AdvancedBlurView(frame: frame)
6
13
  }
7
14
 
15
+ /// Updates the blur view with a new blur amount.
8
16
  @objc public static func updateBlurView(_ blurView: AdvancedBlurView, withBlurAmount blurAmount: Double) {
9
17
  blurView.blurAmount = blurAmount
10
18
  }
11
19
 
20
+ /// Updates the blur view with a new blur type.
12
21
  @objc public static func updateBlurView(_ blurView: AdvancedBlurView, withBlurType blurType: String) {
13
22
  blurView.blurTypeString = blurType
14
23
  }
15
24
 
25
+ /// Updates the blur view with a new reduced transparency fallback color.
16
26
  @objc public static func updateBlurView(_ blurView: AdvancedBlurView, withReducedTransparencyFallbackColor reducedTransparencyFallbackColor: UIColor) {
17
27
  blurView.reducedTransparencyFallbackColor = reducedTransparencyFallbackColor
18
28
  }
19
29
 
30
+ /// Updates the blur view with a new ignoreSafeArea value.
20
31
  @objc public static func updateBlurView(_ blurView: AdvancedBlurView, withIgnoringSafeArea ignoreSafeArea: Bool) {
21
32
  blurView.ignoreSafeArea = ignoreSafeArea
22
33
  }
23
34
  }
35
+
@@ -1,8 +1,11 @@
1
+ import SwiftUI
1
2
  import UIKit
2
3
 
4
+ // MARK: - UIKit Wrapper for Blur
5
+
3
6
  @objc public class AdvancedBlurView: UIView {
4
- private let blurEffectView = BlurEffectView(effect: nil)
5
- private let fallbackView = UIView()
7
+
8
+ private var hostingController: UIHostingController<BasicColoredView>?
6
9
 
7
10
  @objc public var blurAmount: Double = 10.0 {
8
11
  didSet {
@@ -26,63 +29,79 @@ import UIKit
26
29
  didSet {
27
30
  updateView()
28
31
  }
29
- }
32
+ }
30
33
 
31
34
  public override init(frame: CGRect) {
32
35
  super.init(frame: frame)
33
- setupViews()
34
36
  }
35
37
 
36
38
  required init?(coder: NSCoder) {
37
39
  super.init(coder: coder)
38
- setupViews()
39
- }
40
-
41
- private func setupViews() {
42
- blurEffectView.isUserInteractionEnabled = false
43
- fallbackView.isUserInteractionEnabled = false
44
- addSubview(fallbackView)
45
- addSubview(blurEffectView)
46
- syncBackgroundLayersToBack()
47
- updateView()
48
40
  }
49
41
 
50
- private func syncBackgroundLayersToBack() {
51
- if fallbackView.superview === self {
52
- sendSubviewToBack(fallbackView)
53
- }
54
- if blurEffectView.superview === self {
55
- sendSubviewToBack(blurEffectView)
42
+ public override func layoutSubviews() {
43
+ super.layoutSubviews()
44
+ // Defer controller setup until we have a valid frame to avoid issues with initial render
45
+ // in complex layouts (e.g. FlashList with dynamic content)
46
+ if hostingController == nil && bounds.width > 0 && bounds.height > 0 {
47
+ setupHostingController()
56
48
  }
57
49
  }
58
50
 
59
- public override func didAddSubview(_ subview: UIView) {
60
- super.didAddSubview(subview)
61
- if subview !== blurEffectView && subview !== fallbackView {
62
- syncBackgroundLayersToBack()
51
+ private func setupHostingController() {
52
+ // Completely remove old hosting controller
53
+ if let oldHosting = hostingController {
54
+ oldHosting.view.removeFromSuperview()
55
+ oldHosting.removeFromParent()
63
56
  }
64
- }
57
+ hostingController = nil
65
58
 
66
- public override func layoutSubviews() {
67
- super.layoutSubviews()
68
- fallbackView.frame = bounds
69
- blurEffectView.frame = bounds
59
+ let blurStyle = blurStyleFromString(blurTypeString)
60
+ let swiftUIView = BasicColoredView(
61
+ blurAmount: blurAmount,
62
+ blurStyle: blurStyle,
63
+ ignoreSafeArea: ignoreSafeArea,
64
+ reducedTransparencyFallbackColor: reducedTransparencyFallbackColor
65
+ )
66
+
67
+ let hosting = UIHostingController(rootView: swiftUIView)
68
+ hosting.view.backgroundColor = .clear
69
+ hosting.view.translatesAutoresizingMaskIntoConstraints = false
70
+
71
+ // Insert at index 0 to ensure it stays behind any potential subviews (though usually this view has no children)
72
+ // This fixes the z-ordering bug where blur covers content
73
+ if !subviews.isEmpty {
74
+ insertSubview(hosting.view, at: 0)
75
+ } else {
76
+ addSubview(hosting.view)
77
+ }
78
+
79
+ NSLayoutConstraint.activate([
80
+ hosting.view.topAnchor.constraint(equalTo: topAnchor),
81
+ hosting.view.leadingAnchor.constraint(equalTo: leadingAnchor),
82
+ hosting.view.trailingAnchor.constraint(equalTo: trailingAnchor),
83
+ hosting.view.bottomAnchor.constraint(equalTo: bottomAnchor)
84
+ ])
85
+
86
+ self.hostingController = hosting
70
87
  }
71
88
 
72
89
  private func updateView() {
73
- if UIAccessibility.isReduceTransparencyEnabled {
74
- fallbackView.isHidden = false
75
- blurEffectView.isHidden = true
76
- fallbackView.backgroundColor = reducedTransparencyFallbackColor
77
- return
90
+ if let hosting = hostingController {
91
+ // Update the existing controller's root view to avoid expensive recreation
92
+ // This fixes performance bottlenecks and state synchronization issues
93
+ let blurStyle = blurStyleFromString(blurTypeString)
94
+ let swiftUIView = BasicColoredView(
95
+ blurAmount: blurAmount,
96
+ blurStyle: blurStyle,
97
+ ignoreSafeArea: ignoreSafeArea,
98
+ reducedTransparencyFallbackColor: reducedTransparencyFallbackColor
99
+ )
100
+ hosting.rootView = swiftUIView
101
+ hosting.view.setNeedsLayout()
102
+ } else {
103
+ setupHostingController()
78
104
  }
79
-
80
- fallbackView.isHidden = true
81
- blurEffectView.isHidden = false
82
- let blurStyle = blurStyleFromString(blurTypeString)
83
- let intensity = mapBlurAmountToIntensity(blurAmount)
84
- blurEffectView.updateBlur(style: blurStyle, intensity: intensity)
85
- blurEffectView.setNeedsDisplay()
86
105
  }
87
106
 
88
107
  public override func didMoveToSuperview() {
@@ -91,10 +110,13 @@ import UIKit
91
110
 
92
111
  public override func didMoveToWindow() {
93
112
  super.didMoveToWindow()
94
- if window != nil {
95
- updateView()
96
- }
97
113
  }
98
114
 
99
- deinit {}
115
+ deinit {
116
+ if let hosting = hostingController {
117
+ hosting.view.removeFromSuperview()
118
+ hosting.removeFromParent()
119
+ }
120
+ hostingController = nil
121
+ }
100
122
  }
@@ -1,68 +1,77 @@
1
+ // BlurEffectView.swift
2
+
3
+ import SwiftUI
1
4
  import UIKit
2
5
 
6
+ // MARK: - Blur View with proper intensity control
7
+
3
8
  class BlurEffectView: UIVisualEffectView {
4
9
  private var animator: UIViewPropertyAnimator?
5
- private var blurStyle: UIBlurEffect.Style = .systemMaterial {
6
- didSet {
7
- visualEffect = UIBlurEffect(style: blurStyle)
8
- }
9
- }
10
- private var intensity: Double = 0.1 {
11
- didSet {
12
- setNeedsDisplay()
13
- }
14
- }
15
- private var visualEffect: UIVisualEffect = UIBlurEffect(style: .systemMaterial) {
16
- didSet {
17
- setNeedsDisplay()
18
- }
19
- }
10
+ private var blurStyle: UIBlurEffect.Style = .systemMaterial
11
+ private var intensity: Double = 1.0
20
12
 
21
13
  override init(effect: UIVisualEffect?) {
22
- super.init(effect: nil)
14
+ super.init(effect: effect)
15
+ setupBlur()
23
16
  }
24
17
 
25
18
  required init?(coder: NSCoder) {
26
- return nil
19
+ super.init(coder: coder)
20
+ setupBlur()
27
21
  }
28
22
 
29
23
  func updateBlur(style: UIBlurEffect.Style, intensity: Double) {
30
- blurStyle = style
31
- self.intensity = min(max(intensity, 0.0), 1.0)
32
- setNeedsDisplay()
24
+ self.blurStyle = style
25
+ self.intensity = intensity
26
+ setupBlur()
33
27
  }
34
28
 
35
- override func draw(_ rect: CGRect) {
36
- super.draw(rect)
37
-
38
- // Avoid animator-based blur during Detox so tests don't hang waiting for app idleness.
39
- if isDetoxPresent() {
40
- effect = intensity > 0 ? visualEffect : nil
41
- return
29
+ private func setupBlur() {
30
+ // Clean up existing animator
31
+ if let animator = animator {
32
+ animator.stopAnimation(true)
33
+ animator.finishAnimation(at: .current)
42
34
  }
35
+ animator = nil
43
36
 
44
- if intensity <= 0 {
45
- effect = nil
46
- animator?.stopAnimation(true)
47
- return
48
- }
37
+ // Reset effect
38
+ effect = nil
49
39
 
50
- let normalizedIntensity = max(0.01, min(1.0, intensity))
40
+ // Create new animator
41
+ animator = UIViewPropertyAnimator(duration: 1, curve: .linear)
42
+ animator?.addAnimations { [weak self] in
43
+ self?.effect = UIBlurEffect(style: self?.blurStyle ?? .systemMaterial)
44
+ }
51
45
 
52
- effect = nil
53
- animator?.stopAnimation(true)
54
- animator = UIViewPropertyAnimator(duration: 1, curve: .linear) { [unowned self] in
55
- self.effect = visualEffect
46
+ // Set intensity
47
+ animator?.fractionComplete = intensity
48
+ // Stop the animation at the current state
49
+ DispatchQueue.main.async { [weak self] in
50
+ self?.animator?.stopAnimation(true)
51
+ self?.animator?.finishAnimation(at: .current)
56
52
  }
57
- animator?.fractionComplete = CGFloat(normalizedIntensity)
58
53
  }
59
54
 
60
55
  deinit {
61
- animator?.stopAnimation(true)
56
+ guard let animator = animator, animator.state == .active else { return }
57
+ animator.stopAnimation(true)
58
+ animator.finishAnimation(at: .current)
62
59
  }
63
60
  }
64
61
 
65
- private func isDetoxPresent() -> Bool {
66
- let args = ProcessInfo.processInfo.arguments
67
- return args.contains("-detoxServer") && args.contains("-detoxSessionId")
62
+ // MARK: - SwiftUI Blur Wrapper
63
+
64
+ struct Blur: UIViewRepresentable {
65
+ var style: UIBlurEffect.Style = .systemMaterial
66
+ var intensity: Double = 1.0
67
+
68
+ func makeUIView(context: Context) -> BlurEffectView {
69
+ let effectView = BlurEffectView(effect: nil)
70
+ effectView.updateBlur(style: style, intensity: intensity)
71
+ return effectView
72
+ }
73
+
74
+ func updateUIView(_ uiView: BlurEffectView, context: Context) {
75
+ uiView.updateBlur(style: style, intensity: intensity)
76
+ }
68
77
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sbaiahmed1/react-native-blur",
3
- "version": "4.6.1-beta.5",
3
+ "version": "4.6.1",
4
4
  "description": "React native modern blur view",
5
5
  "main": "./lib/module/index.js",
6
6
  "types": "./lib/typescript/src/index.d.ts",