@craft-native/ios 0.0.330 → 0.0.340
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/package.json
CHANGED
|
@@ -60,7 +60,7 @@ final class CraftNativeFlatList: UICollectionView, UICollectionViewDelegateFlowL
|
|
|
60
60
|
/// The row this cell showed is gone; its views stay in the cell, and
|
|
61
61
|
/// the next row it shows reuses them when it has the same shape.
|
|
62
62
|
func recycle() {
|
|
63
|
-
if let identity = representedIdentity { onRecycle?(identity) }
|
|
63
|
+
if let identity = representedIdentity, showsHostedView { onRecycle?(identity) }
|
|
64
64
|
representedIdentity = nil
|
|
65
65
|
onRecycle = nil
|
|
66
66
|
}
|
|
@@ -81,6 +81,7 @@ final class CraftNativeFlatList: UICollectionView, UICollectionViewDelegateFlowL
|
|
|
81
81
|
// since moved to another cell is that cell's now, and pulling it
|
|
82
82
|
// from there blanked a row on screen.
|
|
83
83
|
if let shown = hostedView, shown !== view, shown.superview === contentView { shown.removeFromSuperview() }
|
|
84
|
+
NSLayoutConstraint.deactivate(hostedConstraints)
|
|
84
85
|
hostedView = view
|
|
85
86
|
view.translatesAutoresizingMaskIntoConstraints = false
|
|
86
87
|
contentView.addSubview(view)
|
|
@@ -110,6 +111,8 @@ final class CraftNativeFlatList: UICollectionView, UICollectionViewDelegateFlowL
|
|
|
110
111
|
func releaseHostedView() {
|
|
111
112
|
recycle()
|
|
112
113
|
if let shown = hostedView, shown.superview === contentView { shown.removeFromSuperview() }
|
|
114
|
+
NSLayoutConstraint.deactivate(hostedConstraints)
|
|
115
|
+
hostedConstraints = []
|
|
113
116
|
hostedView = nil
|
|
114
117
|
}
|
|
115
118
|
|
|
@@ -195,8 +198,11 @@ final class CraftNativeFlatList: UICollectionView, UICollectionViewDelegateFlowL
|
|
|
195
198
|
private var endReachedThreshold = 0.1
|
|
196
199
|
private var endReachedSignature: String?
|
|
197
200
|
private var dataContentSignature = ""
|
|
201
|
+
private var dataOrdinals: [Int] = []
|
|
202
|
+
private var dataItemCount = 0
|
|
198
203
|
private var columns = 1
|
|
199
204
|
private var isApplyingSnapshot = false
|
|
205
|
+
private var snapshotNeedsReset = false
|
|
200
206
|
private var pendingApply: (() -> Void)?
|
|
201
207
|
private var pendingThemeRefresh = false
|
|
202
208
|
private let nativeRefreshControl = UIRefreshControl()
|
|
@@ -224,6 +230,10 @@ final class CraftNativeFlatList: UICollectionView, UICollectionViewDelegateFlowL
|
|
|
224
230
|
backgroundColor = .clear
|
|
225
231
|
alwaysBounceVertical = true
|
|
226
232
|
delegate = self
|
|
233
|
+
// The renderer keeps one live view per keyed row. UIKit's speculative
|
|
234
|
+
// cell prefetch can otherwise move that view out of its visible cell.
|
|
235
|
+
// UICollectionView still recycles and self-sizes the visible window.
|
|
236
|
+
isPrefetchingEnabled = false
|
|
227
237
|
keyboardTapGesture.cancelsTouchesInView = false
|
|
228
238
|
keyboardTapGesture.delegate = self
|
|
229
239
|
keyboardTapGesture.addTarget(self, action: #selector(keyboardTap))
|
|
@@ -382,13 +392,15 @@ final class CraftNativeFlatList: UICollectionView, UICollectionViewDelegateFlowL
|
|
|
382
392
|
let padding = nonNegative(raw?["padding"]) ?? 0
|
|
383
393
|
let horizontal = nonNegative(raw?["paddingHorizontal"]) ?? padding
|
|
384
394
|
let vertical = nonNegative(raw?["paddingVertical"]) ?? padding
|
|
385
|
-
|
|
395
|
+
let inset = UIEdgeInsets(
|
|
386
396
|
top: nonNegative(raw?["paddingTop"]) ?? vertical,
|
|
387
397
|
left: nonNegative(raw?["paddingLeft"]) ?? horizontal,
|
|
388
398
|
bottom: nonNegative(raw?["paddingBottom"]) ?? vertical,
|
|
389
399
|
right: nonNegative(raw?["paddingRight"]) ?? horizontal
|
|
390
400
|
)
|
|
391
|
-
|
|
401
|
+
guard contentInset != inset else { return }
|
|
402
|
+
contentInset = inset
|
|
403
|
+
scrollIndicatorInsets = inset
|
|
392
404
|
collectionViewLayout.invalidateLayout()
|
|
393
405
|
}
|
|
394
406
|
|
|
@@ -479,7 +491,9 @@ final class CraftNativeFlatList: UICollectionView, UICollectionViewDelegateFlowL
|
|
|
479
491
|
self.removeItem = removeItem
|
|
480
492
|
self.endReached = endReached
|
|
481
493
|
self.endReachedThreshold = max(0, endReachedThreshold)
|
|
482
|
-
|
|
494
|
+
let nextColumns = max(1, columns)
|
|
495
|
+
let layoutChanged = self.columns != nextColumns || flowLayout.scrollDirection != (horizontal ? .horizontal : .vertical)
|
|
496
|
+
self.columns = nextColumns
|
|
483
497
|
|
|
484
498
|
layoutIfNeeded()
|
|
485
499
|
let oldItems = itemsByIdentity
|
|
@@ -496,6 +510,19 @@ final class CraftNativeFlatList: UICollectionView, UICollectionViewDelegateFlowL
|
|
|
496
510
|
return Item(identity: identity, node: node, signature: signature, isChrome: role != nil && role != "item")
|
|
497
511
|
}
|
|
498
512
|
if inverted { next.reverse() }
|
|
513
|
+
// Reapplying an identical keyed tree during a timer or badge update
|
|
514
|
+
// must not reconfigure cells, invalidate self-sizing, or reset a drag.
|
|
515
|
+
if !snapshotNeedsReset, !layoutChanged, next.count == items.count,
|
|
516
|
+
zip(next, items).allSatisfy({ $0.identity == $1.identity && $0.signature == $1.signature }) {
|
|
517
|
+
finishApplyingSnapshot()
|
|
518
|
+
return
|
|
519
|
+
}
|
|
520
|
+
snapshotNeedsReset = false
|
|
521
|
+
dataItemCount = 0
|
|
522
|
+
dataOrdinals = next.map { item in
|
|
523
|
+
if !item.isChrome { dataItemCount += 1 }
|
|
524
|
+
return dataItemCount - 1
|
|
525
|
+
}
|
|
499
526
|
items = next
|
|
500
527
|
itemsByIdentity = Dictionary(uniqueKeysWithValues: next.map { ($0.identity, $0) })
|
|
501
528
|
|
|
@@ -504,7 +531,7 @@ final class CraftNativeFlatList: UICollectionView, UICollectionViewDelegateFlowL
|
|
|
504
531
|
alwaysBounceVertical = !horizontal
|
|
505
532
|
showsHorizontalScrollIndicator = horizontal
|
|
506
533
|
showsVerticalScrollIndicator = !horizontal
|
|
507
|
-
flowLayout.invalidateLayout()
|
|
534
|
+
if layoutChanged { flowLayout.invalidateLayout() }
|
|
508
535
|
|
|
509
536
|
let nextIds = Set(itemsByIdentity.keys)
|
|
510
537
|
for identity in oldItems.keys where !nextIds.contains(identity) { removeItem(identity) }
|
|
@@ -550,9 +577,12 @@ final class CraftNativeFlatList: UICollectionView, UICollectionViewDelegateFlowL
|
|
|
550
577
|
}
|
|
551
578
|
|
|
552
579
|
func discardAll() {
|
|
580
|
+
snapshotNeedsReset = true
|
|
553
581
|
visibleCells.compactMap { $0 as? Cell }.forEach { $0.releaseHostedView() }
|
|
554
582
|
itemsByIdentity.keys.forEach { recycleItem?($0) }
|
|
555
583
|
items = []
|
|
584
|
+
dataOrdinals = []
|
|
585
|
+
dataItemCount = 0
|
|
556
586
|
itemsByIdentity = [:]
|
|
557
587
|
renderItem = nil
|
|
558
588
|
recycleItem = nil
|
|
@@ -638,11 +668,11 @@ final class CraftNativeFlatList: UICollectionView, UICollectionViewDelegateFlowL
|
|
|
638
668
|
guard !isApplyingSnapshot, pendingApply == nil,
|
|
639
669
|
let endReached = endReached,
|
|
640
670
|
let lastVisible = indexPathsForVisibleItems.map(\.item).max() else { return }
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
let remaining =
|
|
645
|
-
let thresholdItems = max(1, Int(ceil(Double(
|
|
671
|
+
guard dataItemCount > 0, dataOrdinals.indices.contains(lastVisible) else { return }
|
|
672
|
+
let lastDataOrdinal = dataOrdinals[lastVisible]
|
|
673
|
+
guard lastDataOrdinal >= 0 else { return }
|
|
674
|
+
let remaining = dataItemCount - lastDataOrdinal - 1
|
|
675
|
+
let thresholdItems = max(1, Int(ceil(Double(dataItemCount) * endReachedThreshold)))
|
|
646
676
|
guard remaining <= thresholdItems else { return }
|
|
647
677
|
let signature = dataContentSignature
|
|
648
678
|
guard endReachedSignature != signature else { return }
|
|
@@ -6919,10 +6919,12 @@ final class CraftNativeScreenController: UIViewController, UIScrollViewDelegate,
|
|
|
6919
6919
|
}
|
|
6920
6920
|
// The app script has no screen, so no bar and no tab bar to change.
|
|
6921
6921
|
guard !isAppScript else { return }
|
|
6922
|
+
// Prepared tabs have a window and a top controller too. Keep their
|
|
6923
|
+
// options, but only the focused screen may change the shared chrome.
|
|
6922
6924
|
if options.keys.contains("tabAccessory") {
|
|
6923
6925
|
tabAccessory = options["tabAccessory"] as? [String: Any]
|
|
6924
6926
|
tabAccessoryDeclared = true
|
|
6925
|
-
if navigationController?.topViewController === self,
|
|
6927
|
+
if navigationController?.topViewController === self, isShownOnScreen {
|
|
6926
6928
|
CraftChrome.shared.updateTabAccessory(tabAccessory, owner: self)
|
|
6927
6929
|
}
|
|
6928
6930
|
}
|
|
@@ -6941,7 +6943,7 @@ final class CraftNativeScreenController: UIViewController, UIScrollViewDelegate,
|
|
|
6941
6943
|
}
|
|
6942
6944
|
if let hidden = options["tabBarHidden"] as? Bool {
|
|
6943
6945
|
tabBarHidden = hidden
|
|
6944
|
-
if navigationController?.topViewController === self,
|
|
6946
|
+
if navigationController?.topViewController === self, isShownOnScreen {
|
|
6945
6947
|
CraftChrome.shared.setHiddenByScreen(hidden)
|
|
6946
6948
|
}
|
|
6947
6949
|
}
|