aix 0.7.1-alpha.1 → 0.7.1-alpha.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.
- package/ios/HybridAix.swift +46 -22
- package/package.json +1 -1
package/ios/HybridAix.swift
CHANGED
|
@@ -134,6 +134,7 @@ class HybridAix: HybridAixSpec, AixContext, KeyboardNotificationsDelegate {
|
|
|
134
134
|
blankView = nil
|
|
135
135
|
cells.removeAllObjects()
|
|
136
136
|
lastReportedBlankViewSize = (size: .zero, index: 0)
|
|
137
|
+
lastCalculatedBlankSize = 0
|
|
137
138
|
}
|
|
138
139
|
}
|
|
139
140
|
|
|
@@ -230,11 +231,7 @@ class HybridAix: HybridAixSpec, AixContext, KeyboardNotificationsDelegate {
|
|
|
230
231
|
private var boundsObservation: NSKeyValueObservation?
|
|
231
232
|
|
|
232
233
|
// MARK: - Context References (weak to avoid retain cycles)
|
|
233
|
-
weak var blankView: HybridAixCellView? = nil
|
|
234
|
-
didSet {
|
|
235
|
-
// Could add observers or callbacks here when blank view changes
|
|
236
|
-
}
|
|
237
|
-
}
|
|
234
|
+
weak var blankView: HybridAixCellView? = nil
|
|
238
235
|
|
|
239
236
|
weak var composerView: HybridAixComposer? = nil
|
|
240
237
|
|
|
@@ -324,33 +321,58 @@ class HybridAix: HybridAixSpec, AixContext, KeyboardNotificationsDelegate {
|
|
|
324
321
|
return 0
|
|
325
322
|
}
|
|
326
323
|
|
|
324
|
+
/// Cache the last successfully calculated blank size to avoid jumps when cells are temporarily missing
|
|
325
|
+
private var lastCalculatedBlankSize: CGFloat = 0
|
|
326
|
+
|
|
327
327
|
private func calculateBlankSize(keyboardHeight: CGFloat, additionalContentInsetBottom: CGFloat) -> CGFloat {
|
|
328
|
-
guard let scrollView, let blankView else { return
|
|
328
|
+
guard let scrollView, let blankView else { return lastCalculatedBlankSize }
|
|
329
329
|
|
|
330
|
+
let blankViewIndex = Int(blankView.index)
|
|
331
|
+
let endIndex = blankViewIndex - 1
|
|
330
332
|
let startIndex: Int
|
|
331
|
-
|
|
333
|
+
|
|
332
334
|
if let penultimateCellIndex {
|
|
333
|
-
|
|
335
|
+
let penultimate = Int(penultimateCellIndex)
|
|
336
|
+
// Ensure penultimateCellIndex is valid (not beyond current cells)
|
|
337
|
+
// If penultimateCellIndex >= blankViewIndex, it means props are out of sync with cells
|
|
338
|
+
if penultimate >= blankViewIndex {
|
|
339
|
+
// Props are ahead of cells - use previous behavior (last cell before blank)
|
|
340
|
+
startIndex = endIndex
|
|
341
|
+
} else {
|
|
342
|
+
startIndex = penultimate
|
|
343
|
+
}
|
|
334
344
|
} else {
|
|
335
345
|
startIndex = endIndex
|
|
336
346
|
}
|
|
337
347
|
|
|
338
348
|
var cellsBeforeBlankViewHeight: CGFloat = 0
|
|
339
|
-
|
|
349
|
+
var hasMissingCells = false
|
|
350
|
+
if startIndex >= 0 && startIndex <= endIndex {
|
|
340
351
|
for i in startIndex...endIndex {
|
|
341
|
-
|
|
352
|
+
if let cell = getCell(index: i) {
|
|
342
353
|
cellsBeforeBlankViewHeight += cell.view.frame.height
|
|
354
|
+
} else {
|
|
355
|
+
hasMissingCells = true
|
|
343
356
|
}
|
|
344
357
|
}
|
|
345
358
|
}
|
|
346
359
|
|
|
347
|
-
|
|
360
|
+
// If any required cells are missing, return the last known good value to avoid jumps
|
|
361
|
+
// When all cells register, we'll calculate correctly
|
|
362
|
+
if hasMissingCells {
|
|
363
|
+
return lastCalculatedBlankSize
|
|
364
|
+
}
|
|
348
365
|
|
|
349
|
-
|
|
350
|
-
// The blank size fills the remaining space so the last message can scroll to the top
|
|
366
|
+
let blankViewHeight = blankView.view.frame.height
|
|
351
367
|
let visibleAreaHeight = scrollView.bounds.height - keyboardHeight - composerHeight - additionalContentInsetBottom
|
|
352
368
|
let inset = visibleAreaHeight - blankViewHeight - cellsBeforeBlankViewHeight
|
|
353
|
-
|
|
369
|
+
|
|
370
|
+
// Cache this value for when cells are temporarily missing
|
|
371
|
+
lastCalculatedBlankSize = inset
|
|
372
|
+
|
|
373
|
+
// Return raw inset value (can be negative when cells are taller than visible area)
|
|
374
|
+
// The clamping happens in contentInsetBottom to ensure total inset is never negative
|
|
375
|
+
return inset
|
|
354
376
|
}
|
|
355
377
|
|
|
356
378
|
/// Calculate the blank size - the space needed to push content up
|
|
@@ -362,7 +384,9 @@ class HybridAix: HybridAixSpec, AixContext, KeyboardNotificationsDelegate {
|
|
|
362
384
|
/// The content inset for the bottom of the scroll view
|
|
363
385
|
|
|
364
386
|
private func calculateContentInsetBottom(keyboardHeight: CGFloat, blankSize: CGFloat, additionalContentInsetBottom: CGFloat) -> CGFloat {
|
|
365
|
-
|
|
387
|
+
// Clamp to 0 here instead of in calculateBlankSize to preserve correct math
|
|
388
|
+
// when keyboard is open and blankSize goes negative
|
|
389
|
+
return max(0, blankSize + keyboardHeight + composerHeight + additionalContentInsetBottom)
|
|
366
390
|
}
|
|
367
391
|
var contentInsetBottom: CGFloat {
|
|
368
392
|
return calculateContentInsetBottom(keyboardHeight: self.keyboardHeight, blankSize: self.blankSize, additionalContentInsetBottom: self.additionalContentInsetBottom)
|
|
@@ -596,9 +620,7 @@ class HybridAix: HybridAixSpec, AixContext, KeyboardNotificationsDelegate {
|
|
|
596
620
|
|
|
597
621
|
func reportBlankViewSizeChange(size: CGSize, index: Int) {
|
|
598
622
|
let didAlreadyUpdate = size.height == lastReportedBlankViewSize.size.height && size.width == lastReportedBlankViewSize.size.width && index == lastReportedBlankViewSize.index
|
|
599
|
-
if didAlreadyUpdate {
|
|
600
|
-
return
|
|
601
|
-
}
|
|
623
|
+
if didAlreadyUpdate { return }
|
|
602
624
|
|
|
603
625
|
lastReportedBlankViewSize = (size: size, index: index)
|
|
604
626
|
|
|
@@ -630,14 +652,16 @@ class HybridAix: HybridAixSpec, AixContext, KeyboardNotificationsDelegate {
|
|
|
630
652
|
func registerCell(_ cell: HybridAixCellView) {
|
|
631
653
|
cells.setObject(cell, forKey: NSNumber(value: cell.index))
|
|
632
654
|
|
|
633
|
-
// If this cell is marked as last, update our blank view reference
|
|
634
655
|
if cell.isLast {
|
|
635
656
|
blankView = cell
|
|
636
657
|
|
|
637
|
-
// Trigger initial setup
|
|
638
|
-
// the cell was in window, so we need to report here to ensure setup happens
|
|
658
|
+
// Trigger initial setup
|
|
639
659
|
let currentSize = cell.view.bounds.size
|
|
640
660
|
reportBlankViewSizeChange(size: currentSize, index: Int(cell.index))
|
|
661
|
+
} else if didScrollToEndInitially {
|
|
662
|
+
// A content cell registered after initial setup - recalculate insets
|
|
663
|
+
// since this cell's height affects the blank size calculation
|
|
664
|
+
applyAllInsets()
|
|
641
665
|
}
|
|
642
666
|
}
|
|
643
667
|
|
|
@@ -647,7 +671,7 @@ class HybridAix: HybridAixSpec, AixContext, KeyboardNotificationsDelegate {
|
|
|
647
671
|
// If this was our blank view, clear it
|
|
648
672
|
if blankView === cell {
|
|
649
673
|
blankView = nil
|
|
650
|
-
|
|
674
|
+
}
|
|
651
675
|
}
|
|
652
676
|
|
|
653
677
|
func registerComposerView(_ composerView: HybridAixComposer) {
|