@honeypathkar/react-native-lq-bottom-bar 0.1.0 → 0.1.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
@@ -288,6 +288,14 @@ choice would force every consuming app to add that repository to its own
288
288
  **Labels show but glyphs do not.** Your icon component is not forwarding its
289
289
  ref to an `<Svg>`. See the warning section above — this is nearly always it.
290
290
 
291
+ **The bar is lit on the wrong tab.** Fixed in 0.1.1. The bar restores its
292
+ highlight to `selectedIndex` after every press, to honour the report-not-commit
293
+ contract; before 0.1.1 it did so synchronously inside UIKit's own selection
294
+ callback, where UIKit overrode it. The highlight stuck to the tapped tab, and
295
+ since `selectedIndex` had not changed value nothing re-applied it — so the bar
296
+ stayed out of step with the app until some other prop change happened to
297
+ correct it. Upgrade.
298
+
291
299
  **A white or mismatched strip under the bar.** The bar is in flow and the
292
300
  translucent material has only your root view behind it. Give that container a
293
301
  background colour, or move to the overlaid layout.
@@ -43,7 +43,24 @@ public final class LQBottomBarView: UIView, UITabBarDelegate {
43
43
 
44
44
  /// `[{ title, icon }]`, set from JS. Re-builds the items in place.
45
45
  @objc public var items: NSArray = [] {
46
- didSet { rebuildItems() }
46
+ didSet {
47
+ /*
48
+ REBUILD ONLY WHEN THE ITEMS ACTUALLY CHANGED.
49
+
50
+ The JS side rebuilds its `items` array on every render, so this prop
51
+ arrives constantly with identical contents. Acting on each one calls
52
+ `setItems`, which THROWS AWAY the existing `UITabBarItem`s and the
53
+ selection animation running on them — a rebuild landing mid-animation
54
+ is how a press ends up looking like it half-happened.
55
+
56
+ `isEqual` on the arrays is a deep compare of the dictionaries inside,
57
+ which is what "the same bar" means here. The icon values are cached
58
+ data URIs on the JS side, so equal renders really do produce equal
59
+ strings rather than merely equivalent ones.
60
+ */
61
+ if items.isEqual(oldValue) { return }
62
+ rebuildItems()
63
+ }
47
64
  }
48
65
 
49
66
  /// Which item is lit. Setting it animates, which is the point.
@@ -264,7 +281,11 @@ public final class LQBottomBarView: UIView, UITabBarDelegate {
264
281
  guard let items = tabBar.items, index >= 0, index < items.count else {
265
282
  return
266
283
  }
267
- tabBar.selectedItem = items[index]
284
+ let target = items[index]
285
+ // Already there. Re-assigning restarts the capsule animation, and this
286
+ // runs on every prop transaction, so the bar would shimmer continuously.
287
+ if tabBar.selectedItem === target { return }
288
+ tabBar.selectedItem = target
268
289
  }
269
290
 
270
291
  // MARK: - UITabBarDelegate
@@ -280,8 +301,28 @@ public final class LQBottomBarView: UIView, UITabBarDelegate {
280
301
  */
281
302
  onTabSelected?(["index": item.tag])
282
303
 
283
- // Put the bar back where JS still thinks it is, so a vetoed press does
284
- // not leave the highlight on the wrong item for a frame.
285
- applySelection()
304
+ /*
305
+ THE SNAP-BACK HAS TO WAIT A TURN, and this is the whole reason the bar
306
+ can end up lit on a tab the app is not showing.
307
+
308
+ UIKit calls this delegate method while it is still settling its OWN
309
+ selection — on iOS 26 the capsule is mid-animation — so assigning
310
+ `selectedItem` from in here is assigning into a change that has not
311
+ finished. UIKit wins, the highlight stays on the tapped item, and because
312
+ `selectedIndex` never changed VALUE, the prop never fires `didSet` again
313
+ and nothing ever corrects it. The bar is then permanently out of step with
314
+ the app: Events lit while the Profile screen is on screen.
315
+
316
+ Hopping to the next runloop turn puts the assignment after UIKit has
317
+ finished, where it sticks.
318
+
319
+ Reading `selectedIndex` late is a second benefit rather than a hazard: if
320
+ JS accepted the press and sent a new index in the meantime, this applies
321
+ THAT, so an accepted press does not visibly bounce back before moving
322
+ forward again.
323
+ */
324
+ DispatchQueue.main.async { [weak self] in
325
+ self?.applySelection()
326
+ }
286
327
  }
287
328
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@honeypathkar/react-native-lq-bottom-bar",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "The real iOS UITabBar (liquid glass on iOS 26) and a matching blurred Android bar, taking React icons rather than only images",
5
5
  "publishConfig": {
6
6
  "access": "public"