@applicaster/quick-brick-native-apple 6.2.0 → 6.2.2

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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "QuickBrickApple",
3
- "version": "6.2.0",
3
+ "version": "6.2.2",
4
4
  "platforms": {
5
5
  "ios": "14.0",
6
6
  "tvos": "14.0"
@@ -16,7 +16,7 @@
16
16
  "authors": "Applicaster LTD.",
17
17
  "source": {
18
18
  "git": "https://github.com/applicaster/Zapp-Frameworks.git",
19
- "tag": "@@applicaster/quick-brick-native-apple/6.2.0"
19
+ "tag": "@@applicaster/quick-brick-native-apple/6.2.2"
20
20
  },
21
21
  "requires_arc": true,
22
22
  "source_files": "universal/**/*.{m,swift}",
@@ -30,12 +30,28 @@ class FocusableGroupManager {
30
30
  if let itemsGroup = itemsGroups[groupId] {
31
31
  newItemsGroup = itemsGroup
32
32
  }
33
+
33
34
  newItemsGroup[itemId] = item
34
35
  itemsGroups[groupId] = newItemsGroup
35
36
  notifyGroupView(groupID: groupId)
36
37
  return true
37
38
  }
38
39
 
40
+ class func unregisterFusableItem(itemId: String, groupId: String) {
41
+ var newItemsGroup: [String: FocusableView] = [:]
42
+ if let itemsGroup = itemsGroups[groupId] {
43
+ newItemsGroup = itemsGroup
44
+ }
45
+
46
+ newItemsGroup[itemId] = nil
47
+ itemsGroups[groupId] = newItemsGroup
48
+ }
49
+
50
+ class func unregisterFusableGroup(itemId: String) {
51
+ itemsGroups[itemId] = nil
52
+ focusableGroups[itemId] = nil
53
+ }
54
+
39
55
  /// Register FocusableGroup at storage
40
56
  ///
41
57
  /// - Parameter item: FocusableGroup instance
@@ -40,6 +40,13 @@ RCT_EXPORT_VIEW_PROPERTY(onViewPress, RCTBubblingEventBlock)
40
40
  RCT_EXPORT_VIEW_PROPERTY(onViewFocus, RCTBubblingEventBlock)
41
41
  RCT_EXPORT_VIEW_PROPERTY(onViewBlur, RCTBubblingEventBlock)
42
42
  RCT_EXPORT_VIEW_PROPERTY(focusable, BOOL);
43
+
44
+ //TODO: We need only in cases where we can not focus with focusable group
45
+ RCT_EXPORT_VIEW_PROPERTY(nextTvosFocusLeft, NSNumber)
46
+ RCT_EXPORT_VIEW_PROPERTY(nextTvosFocusRight, NSNumber)
47
+ RCT_EXPORT_VIEW_PROPERTY(nextTvosFocusUp, NSNumber)
48
+ RCT_EXPORT_VIEW_PROPERTY(nextTvosFocusDown, NSNumber)
49
+
43
50
  @end
44
51
  #endif
45
52
 
@@ -79,6 +79,16 @@ public class FocusableGroupView: RCTTVView {
79
79
  /// Note: In case Initial init when app start not calling shouldFocusUpdate
80
80
  var isGroupWasFocusedByUser = false
81
81
 
82
+ override public func removeFromSuperview() {
83
+ super.removeFromSuperview()
84
+ guard let itemId else {
85
+ return
86
+ }
87
+
88
+ // Removing when react native releases the view
89
+ FocusableGroupManager.unregisterFusableGroup(itemId: itemId)
90
+ }
91
+
82
92
  /// View connected to GroupView was updated
83
93
  ///
84
94
  /// - Parameter groupItems: dictionary connected to group view
@@ -0,0 +1,60 @@
1
+ //
2
+ // FocusableView+FocusGuide.swift
3
+ // QuickBrickApple
4
+ //
5
+ // Created by Anton Kononenko on 6/27/23.
6
+ //
7
+
8
+ import Foundation
9
+
10
+ class FocusGuideDebugView: UIView {
11
+ init(focusGuide: UIFocusGuide) {
12
+ super.init(frame: focusGuide.layoutFrame)
13
+ backgroundColor = UIColor.green.withAlphaComponent(0.15)
14
+ layer.borderColor = UIColor.green.withAlphaComponent(0.3).cgColor
15
+ layer.borderWidth = 1
16
+ }
17
+
18
+ required init?(coder _: NSCoder) {
19
+ nil
20
+ }
21
+ }
22
+
23
+ extension FocusableView {
24
+ @discardableResult
25
+ func addFocusGuide(from origin: UIView, to destination: UIView, direction: UIRectEdge, debugMode: Bool = false) -> UIFocusGuide {
26
+ addFocusGuide(from: origin, to: [destination], direction: direction, debugMode: debugMode)
27
+ }
28
+
29
+ @discardableResult
30
+ func addFocusGuide(from origin: UIView, to destinations: [UIView], direction: UIRectEdge, debugMode: Bool = false) -> UIFocusGuide {
31
+ let focusGuide = UIFocusGuide()
32
+ addLayoutGuide(focusGuide)
33
+ focusGuide.preferredFocusEnvironments = destinations
34
+ focusGuide.widthAnchor.constraint(equalTo: origin.widthAnchor).isActive = true
35
+ focusGuide.heightAnchor.constraint(equalTo: origin.heightAnchor).isActive = true
36
+
37
+ switch direction {
38
+ case .bottom:
39
+ focusGuide.topAnchor.constraint(equalTo: origin.bottomAnchor).isActive = true
40
+ focusGuide.leftAnchor.constraint(equalTo: origin.leftAnchor).isActive = true
41
+ case .top:
42
+ focusGuide.bottomAnchor.constraint(equalTo: origin.topAnchor).isActive = true
43
+ focusGuide.leftAnchor.constraint(equalTo: origin.leftAnchor).isActive = true
44
+ case .left:
45
+ focusGuide.topAnchor.constraint(equalTo: origin.topAnchor).isActive = true
46
+ focusGuide.rightAnchor.constraint(equalTo: origin.leftAnchor).isActive = true
47
+ case .right:
48
+ focusGuide.topAnchor.constraint(equalTo: origin.topAnchor).isActive = true
49
+ focusGuide.leftAnchor.constraint(equalTo: origin.rightAnchor).isActive = true
50
+ default:
51
+ break
52
+ }
53
+
54
+ if debugMode {
55
+ addSubview(FocusGuideDebugView(focusGuide: focusGuide))
56
+ }
57
+
58
+ return focusGuide
59
+ }
60
+ }
@@ -12,6 +12,8 @@ import UIKit
12
12
 
13
13
  /// RCTTVView subclass that has api how to conects to FocusableGroup
14
14
  public class FocusableView: ParallaxView {
15
+ weak var module: FocusableViewModule?
16
+
15
17
  @objc public var onViewFocus: RCTBubblingEventBlock?
16
18
  @objc public var onViewPress: RCTBubblingEventBlock?
17
19
  @objc public var onViewBlur: RCTBubblingEventBlock?
@@ -45,12 +47,105 @@ public class FocusableView: ParallaxView {
45
47
  }
46
48
  }
47
49
 
50
+ var isFocusLayoutConfigured = false
51
+ override public func layoutSubviews() {
52
+ super.layoutSubviews()
53
+
54
+ if isFocusLayoutConfigured == false {
55
+ configureFocusLayout()
56
+ isFocusLayoutConfigured = true
57
+ }
58
+ }
59
+
60
+ // TODO: Example of solution for future with groupId and focuse id
61
+ // func hintLeftFocusId() {
62
+ // if let onFocusLeft = dictFromReactNative,
63
+ // let groupId = onFocusLeft["groupId"],
64
+ // let id = onFocusLeft["id"],
65
+ // let view = FocusableGroupManager.item(byGroupId: groupId,
66
+ // andItemId: id) {
67
+ // _ = addFocusGuide(from: self,
68
+ // to: view,
69
+ // direction: .left,
70
+ // debugMode: true)
71
+ // }
72
+ // }
73
+
74
+ override public func removeFromSuperview() {
75
+ super.removeFromSuperview()
76
+ guard let itemId, let groupId else {
77
+ return
78
+ }
79
+
80
+ // Removing when react native releases the view
81
+ FocusableGroupManager.unregisterFusableItem(itemId: itemId, groupId: groupId)
82
+ }
83
+
84
+ func addFocusGuideIfNeeded(tag: NSNumber?,
85
+ direction: UIRectEdge) {
86
+ if let tag,
87
+ let view = module?.viewForTag(tag: tag) {
88
+ _ = addFocusGuide(from: self,
89
+ to: view,
90
+ direction: direction,
91
+ debugMode: false)
92
+ }
93
+ }
94
+
95
+ func configureFocusLayout() {
96
+ addFocusGuideIfNeeded(tag: nextTvosFocusLeft, direction: .left)
97
+ addFocusGuideIfNeeded(tag: nextTvosFocusRight, direction: .right)
98
+ addFocusGuideIfNeeded(tag: nextTvosFocusUp, direction: .top)
99
+ addFocusGuideIfNeeded(tag: nextTvosFocusDown, direction: .bottom)
100
+ }
101
+
102
+ @objc public var nextTvosFocusLeft: NSNumber? {
103
+ didSet {
104
+ // This check needed in case refs assigned on later phase, after view ready
105
+ if nextTvosFocusLeft != oldValue,
106
+ isFocusLayoutConfigured {
107
+ addFocusGuideIfNeeded(tag: nextTvosFocusLeft, direction: .left)
108
+ }
109
+ }
110
+ }
111
+
112
+ @objc public var nextTvosFocusRight: NSNumber? {
113
+ didSet {
114
+ // This check needed in case refs assigned on later phase, after view ready
115
+ if nextTvosFocusRight != oldValue,
116
+ isFocusLayoutConfigured {
117
+ addFocusGuideIfNeeded(tag: nextTvosFocusRight, direction: .right)
118
+ }
119
+ }
120
+ }
121
+
122
+ @objc public var nextTvosFocusUp: NSNumber? {
123
+ didSet {
124
+ // This check needed in case refs assigned on later phase, after view ready
125
+ if nextTvosFocusUp != oldValue,
126
+ isFocusLayoutConfigured {
127
+ addFocusGuideIfNeeded(tag: nextTvosFocusUp, direction: .top)
128
+ }
129
+ }
130
+ }
131
+
132
+ @objc public var nextTvosFocusDown: NSNumber? {
133
+ didSet {
134
+ // This check needed in case refs assigned on later phase, after view ready
135
+ if nextTvosFocusDown != oldValue,
136
+ isFocusLayoutConfigured {
137
+ addFocusGuideIfNeeded(tag: nextTvosFocusDown, direction: .bottom)
138
+ }
139
+ }
140
+ }
141
+
48
142
  /// Define if this view is preffered tv focused View
49
143
  @objc public var forceFocus: Bool = false {
50
144
  didSet {
51
145
  guard forceFocus else {
52
146
  return
53
147
  }
148
+
54
149
  DispatchQueue.main.async { [weak self] in
55
150
  FocusableGroupManager.updateFocus(self?.groupId,
56
151
  itemId: self?.itemId,
@@ -26,6 +26,13 @@ public class FocusableViewModule: RCTViewManager {
26
26
  }
27
27
 
28
28
  override public func view() -> UIView? {
29
- FocusableView()
29
+ let view = FocusableView()
30
+ view.module = self
31
+
32
+ return view
33
+ }
34
+
35
+ public func viewForTag<T: UIView>(tag: NSNumber) -> T? {
36
+ bridge.uiManager.view(forReactTag: tag) as? T
30
37
  }
31
38
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@applicaster/quick-brick-native-apple",
3
- "version": "6.2.0",
3
+ "version": "6.2.2",
4
4
  "description": "iOS and tvOS native code for QuickBrick applications. This package is used to provide native logic for QuickBrick",
5
5
  "scripts": {
6
6
  "test": "echo \"Error: no test specified\" && exit 1"