@applicaster/quick-brick-native-apple 6.9.9 → 6.11.0-alpha.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/apple/QuickBrickApple.podspec.json +2 -2
- package/apple/tvos/ReactNativeModulesExportstvOS.m +6 -7
- package/apple/tvos/Views/FocusableGroupView/FocusableGroupStateNotifier.swift +38 -0
- package/apple/tvos/Views/FocusableGroupView/FocusableGroupView.swift +101 -177
- package/apple/tvos/Views/FocusableGroupView/FocusableGroupViewConsts.swift +1 -1
- package/apple/tvos/Views/FocusableView/FocusableView.swift +7 -3
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "QuickBrickApple",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.11.0-alpha.0",
|
|
4
4
|
"platforms": {
|
|
5
5
|
"ios": "16.0",
|
|
6
6
|
"tvos": "16.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.
|
|
19
|
+
"tag": "@@applicaster/quick-brick-native-apple/6.11.0-alpha.0"
|
|
20
20
|
},
|
|
21
21
|
"requires_arc": true,
|
|
22
22
|
"source_files": "universal/**/*.{m,swift}",
|
|
@@ -18,14 +18,13 @@ RCT_EXTERN_METHOD(setPreferredFocus:(NSString *)groupId itemId:(NSString *)itemI
|
|
|
18
18
|
@end
|
|
19
19
|
|
|
20
20
|
@interface RCT_EXTERN_MODULE(FocusableGroupViewModule, RCTViewManager)
|
|
21
|
-
RCT_EXPORT_VIEW_PROPERTY(
|
|
22
|
-
RCT_EXPORT_VIEW_PROPERTY(
|
|
21
|
+
RCT_EXPORT_VIEW_PROPERTY(onGroupFocus, RCTDirectEventBlock)
|
|
22
|
+
RCT_EXPORT_VIEW_PROPERTY(onGroupBlur, RCTDirectEventBlock)
|
|
23
23
|
RCT_EXPORT_VIEW_PROPERTY(itemId, NSString);
|
|
24
24
|
RCT_EXPORT_VIEW_PROPERTY(groupId, NSString);
|
|
25
25
|
RCT_EXPORT_VIEW_PROPERTY(initialItemId, NSString);
|
|
26
|
-
RCT_EXPORT_VIEW_PROPERTY(
|
|
26
|
+
RCT_EXPORT_VIEW_PROPERTY(isPreferredFocusEnvironmentDisabled, BOOL);
|
|
27
27
|
RCT_EXPORT_VIEW_PROPERTY(isFocusDisabled, BOOL);
|
|
28
|
-
RCT_EXPORT_VIEW_PROPERTY(dependantGroupIds, NSArray)
|
|
29
28
|
@end
|
|
30
29
|
|
|
31
30
|
@interface RCT_EXTERN_MODULE(FocusableViewModule, RCTViewManager)
|
|
@@ -35,9 +34,9 @@ RCT_EXPORT_VIEW_PROPERTY(forceFocus, BOOL)
|
|
|
35
34
|
RCT_EXPORT_VIEW_PROPERTY(preferredFocus, BOOL)
|
|
36
35
|
RCT_EXPORT_VIEW_PROPERTY(isParallaxDisabled, BOOL)
|
|
37
36
|
RCT_EXPORT_VIEW_PROPERTY(isPressDisabled, BOOL)
|
|
38
|
-
RCT_EXPORT_VIEW_PROPERTY(onViewPress,
|
|
39
|
-
RCT_EXPORT_VIEW_PROPERTY(onViewFocus,
|
|
40
|
-
RCT_EXPORT_VIEW_PROPERTY(onViewBlur,
|
|
37
|
+
RCT_EXPORT_VIEW_PROPERTY(onViewPress, RCTDirectEventBlock)
|
|
38
|
+
RCT_EXPORT_VIEW_PROPERTY(onViewFocus, RCTDirectEventBlock)
|
|
39
|
+
RCT_EXPORT_VIEW_PROPERTY(onViewBlur, RCTDirectEventBlock)
|
|
41
40
|
RCT_EXPORT_VIEW_PROPERTY(focusable, BOOL);
|
|
42
41
|
|
|
43
42
|
//TODO: We need only in cases where we can not focus with focusable group
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
//
|
|
2
|
+
// FocusableGroupStateNotifier.swift
|
|
3
|
+
// Pods
|
|
4
|
+
//
|
|
5
|
+
// Created by Anton Kononenko on 3/21/25.
|
|
6
|
+
|
|
7
|
+
public protocol FocusableGroupStateNotifier: AnyObject {
|
|
8
|
+
var isActive: Bool { get }
|
|
9
|
+
func updateFocus(currentlyActive: Bool,
|
|
10
|
+
context: UIFocusUpdateContext)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
enum FocusableGroupNotifierActionType {
|
|
14
|
+
case onBlur
|
|
15
|
+
case onFocus
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
typealias NotifierActionType = (_ type: FocusableGroupNotifierActionType, _ context: UIFocusUpdateContext) -> Void
|
|
19
|
+
|
|
20
|
+
class FocusableGroupStateNotifierDefault: FocusableGroupStateNotifier {
|
|
21
|
+
private(set) var isActive: Bool = false
|
|
22
|
+
|
|
23
|
+
let action: NotifierActionType
|
|
24
|
+
init(action: @escaping NotifierActionType) {
|
|
25
|
+
self.action = action
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
func updateFocus(currentlyActive: Bool, context: UIFocusUpdateContext) {
|
|
29
|
+
guard currentlyActive != isActive else { return }
|
|
30
|
+
|
|
31
|
+
isActive = currentlyActive
|
|
32
|
+
sendNotifierEvent(context: context)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
func sendNotifierEvent(context: UIFocusUpdateContext) {
|
|
36
|
+
action(isActive ? .onFocus : .onBlur, context)
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -12,17 +12,21 @@ import React
|
|
|
12
12
|
import UIKit
|
|
13
13
|
|
|
14
14
|
/// Focusable Group View that implements UIFocusGuide instance that catches focus event
|
|
15
|
+
///
|
|
15
16
|
public class FocusableGroupView: RCTTVView {
|
|
17
|
+
override public var debugDescription: String {
|
|
18
|
+
"GROUP_ID: \(groupId ?? "NONE"), ID: \(itemId ?? "NONE"), \(super.debugDescription)"
|
|
19
|
+
}
|
|
20
|
+
|
|
16
21
|
private var cancellables = Set<AnyCancellable>()
|
|
17
22
|
|
|
18
23
|
/// Completion that will be used when focus manager forcing to update focusable group
|
|
19
24
|
public var didFocusCallBack: (completion: () -> Void, focusableItemId: String)?
|
|
20
25
|
|
|
21
|
-
|
|
22
|
-
@objc public var onDidUpdateFocus: RCTBubblingEventBlock?
|
|
26
|
+
private var activeStateNotifier: FocusableGroupStateNotifier?
|
|
23
27
|
|
|
24
|
-
|
|
25
|
-
@objc public var
|
|
28
|
+
@objc public var onGroupFocus: RCTDirectEventBlock?
|
|
29
|
+
@objc public var onGroupBlur: RCTDirectEventBlock?
|
|
26
30
|
|
|
27
31
|
/// Define if focus enabled for current view
|
|
28
32
|
@objc public var isFocusDisabled: Bool = false
|
|
@@ -30,11 +34,15 @@ public class FocusableGroupView: RCTTVView {
|
|
|
30
34
|
/// Current group will try to find initial index id in groups
|
|
31
35
|
@objc public var dependantGroupIds: [String]?
|
|
32
36
|
|
|
33
|
-
/// Check if preferred focus
|
|
34
|
-
@objc public var
|
|
37
|
+
/// Check if preferred focus environment disabled
|
|
38
|
+
@objc public var isPreferredFocusDisabled: Bool = false
|
|
35
39
|
|
|
36
40
|
/// ID of the item that must be focused as init focus
|
|
37
|
-
@objc public var initialItemId: String?
|
|
41
|
+
@objc public var initialItemId: String? {
|
|
42
|
+
didSet {
|
|
43
|
+
updateInitialItemId()
|
|
44
|
+
}
|
|
45
|
+
}
|
|
38
46
|
|
|
39
47
|
/// ID of the Connected GroupView provided by React-Native env
|
|
40
48
|
@objc public var itemId: String? {
|
|
@@ -54,38 +62,14 @@ public class FocusableGroupView: RCTTVView {
|
|
|
54
62
|
FocusableGroupManager.shared
|
|
55
63
|
}
|
|
56
64
|
|
|
57
|
-
/// If this variable not nil it overrides default
|
|
58
|
-
@MainActor private var
|
|
59
|
-
didSet {
|
|
60
|
-
var rootView: UIView? = self
|
|
61
|
-
|
|
62
|
-
while rootView?.superview != nil {
|
|
63
|
-
if let unwrapedRootView = rootView,
|
|
64
|
-
let superView = unwrapedRootView.superview {
|
|
65
|
-
rootView = superView
|
|
66
|
-
if let superFocusGroup = rootView as? FocusableGroupView {
|
|
67
|
-
if let newFocusData = customPrefferedFocusEnvironment as? [FocusableView],
|
|
68
|
-
let superFocusGroupFocusEnvironment = superFocusGroup.customPrefferedFocusEnvironment as? [FocusableView] {
|
|
69
|
-
let groupIdsToStay = newFocusData.map(\.groupId)
|
|
70
|
-
let filteredGroups = superFocusGroupFocusEnvironment.filter { !groupIdsToStay.contains($0.groupId) }
|
|
71
|
-
superFocusGroup.customPrefferedFocusEnvironment = filteredGroups + newFocusData
|
|
72
|
-
} else {
|
|
73
|
-
superFocusGroup.customPrefferedFocusEnvironment = customPrefferedFocusEnvironment
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
/// Check if group has an initial focus
|
|
82
|
-
/// Note: In case Initial init when app start not calling shouldFocusUpdate
|
|
83
|
-
private var isGroupWasFocusedByUser = false
|
|
65
|
+
/// If this variable not nil it overrides default preferred focus environment
|
|
66
|
+
@MainActor private var customPreferredFocusEnvironment: [UIFocusEnvironment]?
|
|
84
67
|
|
|
85
68
|
/// Manager that connects View instance to FocusableGroupViewModule
|
|
86
69
|
private weak var module: FocusableGroupViewModule?
|
|
87
70
|
public func setModule(_ module: FocusableGroupViewModule) {
|
|
88
71
|
self.module = module
|
|
72
|
+
activeStateNotifier = FocusableGroupStateNotifierDefault(action: notifyReactNativeFocusUpdate)
|
|
89
73
|
}
|
|
90
74
|
|
|
91
75
|
override init(bridge: RCTBridge) {
|
|
@@ -117,22 +101,17 @@ public class FocusableGroupView: RCTTVView {
|
|
|
117
101
|
setupFocus()
|
|
118
102
|
}
|
|
119
103
|
|
|
120
|
-
|
|
121
|
-
if focusGuide == nil {
|
|
122
|
-
focusGuide = UIFocusGuide()
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
/// Update customPrefferedFocusEnvironment
|
|
104
|
+
/// Update customPreferredFocusEnvironment
|
|
127
105
|
///
|
|
128
|
-
/// - Parameter view: view instance that should be
|
|
106
|
+
/// - Parameter view: view instance that should be preferred
|
|
129
107
|
@MainActor func updatePreferredFocusEnv(with view: UIFocusEnvironment) {
|
|
130
|
-
|
|
108
|
+
focusGuide.preferredFocusEnvironments = [view]
|
|
109
|
+
customPreferredFocusEnvironment = [view]
|
|
131
110
|
}
|
|
132
111
|
|
|
133
112
|
/// Setup Focus Guide for use
|
|
134
113
|
private func setupFocus() {
|
|
135
|
-
|
|
114
|
+
focusGuide = UIFocusGuide()
|
|
136
115
|
|
|
137
116
|
addLayoutGuide(focusGuide)
|
|
138
117
|
|
|
@@ -143,117 +122,53 @@ public class FocusableGroupView: RCTTVView {
|
|
|
143
122
|
|
|
144
123
|
manager.focusableItemsUpdatedAddedToGroupUpdate
|
|
145
124
|
.filter { $0.groupId == self.groupId }
|
|
146
|
-
.sink { [weak self]
|
|
125
|
+
.sink { [weak self] _ in
|
|
147
126
|
guard let self else { return }
|
|
148
|
-
|
|
127
|
+
updateInitialItemId()
|
|
149
128
|
}.store(in: &cancellables)
|
|
150
129
|
}
|
|
151
130
|
|
|
152
|
-
///
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
guard
|
|
157
|
-
isGroupWasFocusedByUser == false,
|
|
158
|
-
let initialItemId,
|
|
159
|
-
let initialView = groupItems[initialItemId]
|
|
160
|
-
else {
|
|
161
|
-
return
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
let focusedView = customPrefferedFocusEnvironment?.first as? UIView
|
|
165
|
-
if focusedView != initialView || focusedView == nil {
|
|
166
|
-
updatePreferredFocusEnv(with: initialView)
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
/// Reset focus to initial state if needed
|
|
171
|
-
///
|
|
172
|
-
/// - Parameter nextFocusedItem: next item that should be focused
|
|
173
|
-
/// - Returns: true if reser succeed
|
|
174
|
-
private func resetFocusPrefferedEnvironmentIfNeeded(nextFocusedItem: UIFocusItem?) async -> Bool {
|
|
175
|
-
var retVal = false
|
|
176
|
-
guard let nextFocusedItem else {
|
|
177
|
-
return retVal
|
|
178
|
-
}
|
|
131
|
+
/// Update initial item id
|
|
132
|
+
@MainActor private func updateInitialItemId() {
|
|
133
|
+
Task(priority: .userInitiated) { [weak self] in
|
|
134
|
+
guard let self else { return }
|
|
179
135
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
let initialItemView = await manager.getItem(withId: groupId,
|
|
186
|
-
inGroup: initialItemId) {
|
|
187
|
-
updatePreferredFocusEnv(with: initialItemView)
|
|
188
|
-
retVal = true
|
|
189
|
-
}
|
|
190
|
-
} else {
|
|
191
|
-
retVal = true
|
|
192
|
-
}
|
|
136
|
+
guard
|
|
137
|
+
let initialItemId,
|
|
138
|
+
let initialView = await manager.getItem(withId: initialItemId, inGroup: initialItemId)
|
|
139
|
+
else {
|
|
140
|
+
return
|
|
193
141
|
}
|
|
194
|
-
}
|
|
195
|
-
return retVal
|
|
196
|
-
}
|
|
197
142
|
|
|
198
|
-
|
|
199
|
-
///
|
|
200
|
-
/// - Returns: true if can focus otherwise false
|
|
201
|
-
private func tryTakePrefferedViewFromDependantGroups() async -> Bool {
|
|
202
|
-
guard let initialItemId,
|
|
203
|
-
let dependantGroupIds,
|
|
204
|
-
|
|
205
|
-
let firstGroupId = dependantGroupIds.first,
|
|
206
|
-
let initialView = await manager.getItem(withId: firstGroupId,
|
|
207
|
-
inGroup: initialItemId)
|
|
208
|
-
else {
|
|
209
|
-
return false
|
|
143
|
+
updatePreferredFocusEnv(with: initialView)
|
|
210
144
|
}
|
|
211
|
-
|
|
212
|
-
updatePreferredFocusEnv(with: initialView)
|
|
213
|
-
|
|
214
|
-
return true
|
|
215
145
|
}
|
|
216
146
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
return
|
|
223
|
-
}
|
|
224
|
-
let focusHeading = focusHeadingToString(focusHeading: context.focusHeading)
|
|
225
|
-
var params: [String: Any] = [GroupViewUpdateEvents.focusHeading: focusHeading]
|
|
226
|
-
|
|
227
|
-
params[GroupViewUpdateEvents.isFocusingByUser] = focusHeading != FocusHeadingTextValues.manualUpdate
|
|
147
|
+
private func createFocusEventParams(context: UIFocusUpdateContext, isActive: Bool) -> [String: Any] {
|
|
148
|
+
var params: [String: Any] = [
|
|
149
|
+
GroupViewUpdateEvents.focusHeading: focusHeadingToString(focusHeading: context.focusHeading),
|
|
150
|
+
GroupViewUpdateEvents.isFocusingByUser: focusHeadingToString(focusHeading: context.focusHeading) != FocusHeadingTextValues.manualUpdate,
|
|
151
|
+
]
|
|
228
152
|
|
|
229
153
|
params[GroupViewUpdateEvents.previouslyFocusedItem] = dataForFocusItem(focusItem: context.previouslyFocusedView)
|
|
230
154
|
params[GroupViewUpdateEvents.nextFocusedItem] = dataForFocusItem(focusItem: context.nextFocusedView)
|
|
231
|
-
params[GroupViewUpdateEvents.
|
|
155
|
+
params[GroupViewUpdateEvents.preferredFocusEnvironment] = dataForFocusItem(focusItem: customPreferredFocusEnvironment?.first)
|
|
232
156
|
params[GroupViewUpdateEvents.groupId] = groupId
|
|
157
|
+
params[GroupViewUpdateEvents.isActive] = isActive
|
|
158
|
+
params[GroupViewUpdateEvents.isFocusDisabled] = isFocusDisabled
|
|
159
|
+
params[GroupViewUpdateEvents.itemId] = itemId
|
|
233
160
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
}
|
|
161
|
+
return params
|
|
162
|
+
}
|
|
237
163
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
let initialItemId {
|
|
246
|
-
if let groupId {
|
|
247
|
-
let groupItems = await manager.getItems(forGroup: groupId)
|
|
248
|
-
if let initialItemView = groupItems[initialItemId] {
|
|
249
|
-
updatePreferredFocusEnv(with: initialItemView)
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
}
|
|
164
|
+
private func notifyReactNativeFocusUpdate(_ type: FocusableGroupNotifierActionType,
|
|
165
|
+
_ context: UIFocusUpdateContext) {
|
|
166
|
+
switch type {
|
|
167
|
+
case .onBlur:
|
|
168
|
+
onGroupBlur?(createFocusEventParams(context: context, isActive: false))
|
|
169
|
+
case .onFocus:
|
|
170
|
+
onGroupFocus?(createFocusEventParams(context: context, isActive: true))
|
|
253
171
|
}
|
|
254
|
-
params[GroupViewUpdateEvents.isFocusDisabled] = isFocusDisabled
|
|
255
|
-
|
|
256
|
-
bubbleEventBlock(params)
|
|
257
172
|
}
|
|
258
173
|
|
|
259
174
|
/// Data for Focus Item that will be passed to React-Native env
|
|
@@ -272,11 +187,11 @@ public class FocusableGroupView: RCTTVView {
|
|
|
272
187
|
return retVal.keys.count == 0 ? nil : retVal
|
|
273
188
|
}
|
|
274
189
|
|
|
275
|
-
/// Update
|
|
190
|
+
/// Update preferred Focus View for focus guide if next focusable view is part of this focus guide
|
|
276
191
|
///
|
|
277
|
-
/// - Parameter
|
|
278
|
-
@MainActor func updatePreferredFocusView(
|
|
279
|
-
guard let nextFocusView =
|
|
192
|
+
/// - Parameter nextFocusItem: next focus item instance
|
|
193
|
+
@MainActor func updatePreferredFocusView(nextFocusItem: UIFocusEnvironment?) {
|
|
194
|
+
guard let nextFocusView = nextFocusItem,
|
|
280
195
|
let asView = nextFocusView as? UIView,
|
|
281
196
|
asView.isDescendant(of: self)
|
|
282
197
|
else {
|
|
@@ -290,8 +205,8 @@ public class FocusableGroupView: RCTTVView {
|
|
|
290
205
|
///
|
|
291
206
|
/// - Parameter nextFocuseItem: next item to focus
|
|
292
207
|
/// - Returns: true if next focus item part of this group instance
|
|
293
|
-
private func focusItemIsDescendant(
|
|
294
|
-
guard let nextFocusView =
|
|
208
|
+
private func focusItemIsDescendant(nextFocusItem: Any?) -> Bool {
|
|
209
|
+
guard let nextFocusView = nextFocusItem as? UIView,
|
|
295
210
|
nextFocusView.isDescendant(of: self)
|
|
296
211
|
else {
|
|
297
212
|
return false
|
|
@@ -325,53 +240,62 @@ public class FocusableGroupView: RCTTVView {
|
|
|
325
240
|
// MARK: Focus Engine
|
|
326
241
|
|
|
327
242
|
override public var preferredFocusEnvironments: [UIFocusEnvironment] {
|
|
328
|
-
|
|
329
|
-
return customPrefferedFocusEnvironment
|
|
330
|
-
}
|
|
331
|
-
return super.preferredFocusEnvironments
|
|
243
|
+
customPreferredFocusEnvironment ?? super.preferredFocusEnvironments
|
|
332
244
|
}
|
|
333
245
|
|
|
334
|
-
override public func shouldUpdateFocus(in
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
Task.detached(priority: .userInitiated) { [weak self] in
|
|
338
|
-
guard let self else { return }
|
|
339
|
-
await sendUpdateFocusEventToReactNative(bubbleEventBlock: onWillUpdateFocus,
|
|
340
|
-
context: context)
|
|
341
|
-
}
|
|
246
|
+
override public func shouldUpdateFocus(in _: UIFocusUpdateContext) -> Bool {
|
|
247
|
+
isFocusDisabled == false
|
|
248
|
+
}
|
|
342
249
|
|
|
343
|
-
|
|
344
|
-
|
|
250
|
+
// This was added because on fast scroll preferred focus was not called,
|
|
251
|
+
// that cause to select wrong item. On future must be removed if resolved
|
|
252
|
+
private func needsFocusUpdateDueToWrongFocus(context: UIFocusUpdateContext,
|
|
253
|
+
with coordinator: UIFocusAnimationCoordinator) -> Bool {
|
|
254
|
+
guard
|
|
255
|
+
let preferredFocusedItem = customPreferredFocusEnvironment?.first as? FocusableView,
|
|
256
|
+
let nextFocusedItem = context.nextFocusedItem as? FocusableView,
|
|
257
|
+
let previouslyFocusedItem = context.previouslyFocusedItem as? FocusableView else { return false }
|
|
345
258
|
|
|
346
|
-
|
|
259
|
+
let previousFocusedItemNotFromSameGroup = previouslyFocusedItem.groupId != nextFocusedItem.groupId
|
|
260
|
+
let nextFocusedBelongsToCurrentGroup = nextFocusedItem.groupId == itemId
|
|
261
|
+
let nextFocusItemNotPreferredFocusedItem = nextFocusedItem.itemId != preferredFocusedItem.itemId
|
|
347
262
|
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
263
|
+
guard previousFocusedItemNotFromSameGroup,
|
|
264
|
+
nextFocusedBelongsToCurrentGroup,
|
|
265
|
+
nextFocusItemNotPreferredFocusedItem else {
|
|
266
|
+
return false
|
|
352
267
|
}
|
|
353
268
|
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
269
|
+
coordinator.addCoordinatedAnimations({
|
|
270
|
+
let duration: TimeInterval = UIView.inheritedAnimationDuration
|
|
271
|
+
UIView.animate(withDuration: duration,
|
|
272
|
+
delay: 0.0,
|
|
273
|
+
options: [],
|
|
274
|
+
animations: {
|
|
275
|
+
self.setNeedsFocusUpdate()
|
|
276
|
+
self.updateFocusIfNeeded()
|
|
277
|
+
}, completion: nil)
|
|
278
|
+
}, completion: nil)
|
|
359
279
|
|
|
360
|
-
if focusItemIsDescendant(nextFocuseItem: context.nextFocusedItem) == false {
|
|
361
|
-
isFocusDisabled = false
|
|
362
|
-
}
|
|
363
280
|
return true
|
|
364
281
|
}
|
|
365
282
|
|
|
366
283
|
override public func didUpdateFocus(in context: UIFocusUpdateContext, with coordinator: UIFocusAnimationCoordinator) {
|
|
367
|
-
|
|
368
|
-
tryDidFocusCallCallback(context: context)
|
|
284
|
+
guard isFocusDisabled == false else { return }
|
|
369
285
|
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
286
|
+
guard needsFocusUpdateDueToWrongFocus(context: context,
|
|
287
|
+
with: coordinator) == false else {
|
|
288
|
+
return
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
if isPreferredFocusDisabled == false {
|
|
292
|
+
updatePreferredFocusView(nextFocusItem: context.nextFocusedItem)
|
|
374
293
|
}
|
|
294
|
+
|
|
295
|
+
let isActive = focusItemIsDescendant(nextFocusItem: context.nextFocusedItem)
|
|
296
|
+
activeStateNotifier?.updateFocus(currentlyActive: isActive, context: context)
|
|
297
|
+
|
|
298
|
+
tryDidFocusCallCallback(context: context)
|
|
375
299
|
}
|
|
376
300
|
|
|
377
301
|
/// Try to send a callback in case focus manager request callback during force focus update
|
|
@@ -22,7 +22,7 @@ public enum GroupViewUpdateEvents {
|
|
|
22
22
|
static let focusHeading = "focusHeading"
|
|
23
23
|
static let previouslyFocusedItem = "previouslyFocusedItem"
|
|
24
24
|
static let nextFocusedItem = "nextFocusedItem"
|
|
25
|
-
static let
|
|
25
|
+
static let preferredFocusEnvironment = "preferredFocusEnvironment"
|
|
26
26
|
static let groupId = "groupId"
|
|
27
27
|
static let itemId = "itemId"
|
|
28
28
|
static let isActive = "isActive"
|
|
@@ -13,6 +13,10 @@ import UIKit
|
|
|
13
13
|
|
|
14
14
|
/// RCTTVView subclass that has api how to conects to FocusableGroup
|
|
15
15
|
public class FocusableView: ParallaxView {
|
|
16
|
+
override public var debugDescription: String {
|
|
17
|
+
"GROUP_ID: \(groupId ?? "NONE"), ID: \(itemId ?? "NONE"), \(super.debugDescription)"
|
|
18
|
+
}
|
|
19
|
+
|
|
16
20
|
private var cancellables = Set<AnyCancellable>()
|
|
17
21
|
|
|
18
22
|
private weak var module: FocusableViewModule?
|
|
@@ -22,9 +26,9 @@ public class FocusableView: ParallaxView {
|
|
|
22
26
|
|
|
23
27
|
@MainActor private weak var focusableGroup: FocusableGroupProtocol?
|
|
24
28
|
|
|
25
|
-
@objc public var onViewFocus:
|
|
26
|
-
@objc public var onViewPress:
|
|
27
|
-
@objc public var onViewBlur:
|
|
29
|
+
@objc public var onViewFocus: RCTDirectEventBlock?
|
|
30
|
+
@objc public var onViewPress: RCTDirectEventBlock?
|
|
31
|
+
@objc public var onViewBlur: RCTDirectEventBlock?
|
|
28
32
|
@objc public var isParallaxDisabled: Bool = false {
|
|
29
33
|
didSet {
|
|
30
34
|
parallaxEffectOptions.parallaxMotionEffect.isDisabled = isParallaxDisabled
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@applicaster/quick-brick-native-apple",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.11.0-alpha.0",
|
|
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"
|